Match game

Here’s something I thru together just to pass the time. To play, touch an image and slide it up, down, left, or right one square to a matching image. The image you slide will disappear and any image above it will take it’s place. The object is to eliminate as many images as you can. The text will tell you how many images are left, your low score and how many valid moves are left. You’ll get a message when there are no more valid move and the game is over. It should work in landscape or portrait mode. This was written on an iPad Air, so I’m not sure what it looks like on a smaller or larger screen.

viewer.mode=FULLSCREEN

function setup()
    --clearProjectData()
    score=13*19
    xVal,yVal=19,13
    if HEIGHT>WIDTH then
        xVal,yVal=13,19
    end
    lowScore=readProjectData("lowScore",score)
    spr={asset.builtin.Planet_Cute.Character_Pink_Girl,
        asset.builtin.Planet_Cute.Character_Horn_Girl,
        asset.builtin.Planet_Cute.Character_Cat_Girl,
        asset.builtin.Planet_Cute.Character_Princess_Girl,
        asset.builtin.Planet_Cute.Character_Boy}    
    selected=0
    moves={-1,0,1,0,0,-1,0,1}
    tab={}
    for x=1,xVal do       
        tab[x]={}
        for y=1,yVal do
            tab[x][y]=math.random(5)
        end
    end
    checkPieces()
end

function draw()
    background(0)
    stroke(255)
    strokeWidth(2)
    fill(128)
    for x=1,xVal do
        for y=1,yVal do
            rect(x*50,y*50,52)
            if tab[x][y]>0 then
                sprite(spr[tab[x][y]],x*50+25,y*50+35,50)
            end
        end
    end
    fill(255)
    fontSize(20)
    text("Score  "..score.."    Low Score  "..string.format("%d",lowScore).."    Valid moves left  "..count,WIDTH/2,HEIGHT-10)
    if gameOver then
        fontSize(70)
        fill(255)
        text("NO MORE MOVES",WIDTH/2,HEIGHT/2)
    end
end

function touched(t)
    x=math.floor(t.x/50)
    y=math.floor(t.y/50)
    if x<1 or x>xVal or y<1 or y>yVal then
        fillGap()
        return
    end
    if t.state==BEGAN and tab[x][y]>0 then
        selected=tab[x][y]
        selX,selY=x,y
    end
    if t.state==ENDED and selected>0 then
        if validMove() and selected==tab[x][y] then
            tab[selX][selY]=0
            selected=0
            score=score-1
            checkScore()
        end
        fillGap()
    end
end

function validMove()
    if selX==x and (selY==y+1 or selY==y-1) then
        return true
    elseif selY==y and (selX==x+1 or selX==x-1) then
        return true
    end
    return false
end

function checkScore()
    if score<lowScore then
        lowScore=score
        saveProjectData("lowScore",lowScore)
    end    
end

function fillGap()
    for x=1,xVal do
        for y=1,yVal do
            if tab[x][y]==0 then
                for z=1,yVal-y do
                    if tab[x][y+z]~=0 then
                        tab[x][y]=tab[x][y+z]
                        tab[x][y+z]=0
                        break
                    end                   
                end
            end
        end
    end
    checkPieces()
end

function checkPieces()
    count=0
    gameOver=true
    for x=1,xVal do
        for y=1,yVal do
            if tab[x][y]>0 then
                for h=1,8,2 do
                    x1=x+moves[h]
                    y1=y+moves[h+1]
                    if x1>=1 and x1<=xVal and y1>=1 and y1<=yVal then
                        if tab[x][y]==tab[x1][y1] then
                            gameOver=false
                            count=count+1
                        end
                    end
                end                
            end
        end
    end
end

@dave1707 - neat but a touch frustrating. Perhaps an option to change the size of the images so you can move from easy to progressively harder. Also, some of the images are very similar making this hard on the eyes at time (my old eyes that is). Quite subtle in a way as you need to asses the effect of making a move on the surrounding images.

@Bri_G Here’s an updated version. I changed the images and added a size variable to alter the number of squares. Change the size value to something thats comfortable.

PS. Removed clearProjectData and added a reset button in the upper right corner to reset the low score if you change the number of squares.

PS. You have to look a little ahead to see how each move affects how some other moves can be made.

viewer.mode=FULLSCREEN

function setup()
    size=60     -- the smaller the number, the more squares
    rectMode(CENTER)
    spriteMode(CENTER)
    xs=(WIDTH//size)-1
    ys=(HEIGHT//size)-1
    score=xs*ys
    lowScore=readProjectData("lowScore",score)
    spr={asset.builtin.Planet_Cute.Key,
        asset.builtin.Planet_Cute.Tree_Ugly,
        asset.builtin.Planet_Cute.Heart,
        asset.builtin.Planet_Cute.Character_Princess_Girl,
        asset.builtin.Planet_Cute.Star}
    selected=0
    moves={-1,0,1,0,0,-1,0,1}
    tab={}
    for x=1,xs do       
        tab[x]={}
        for y=1,ys do
            tab[x][y]=math.random(5)
        end
    end
    checkPieces()
end

function draw()
    background(0)
    stroke(255)
    strokeWidth(2)
    fill(128)
    for x=1,xs do
        for y=1,ys do
            rect(x*size,y*size,size)
            if tab[x][y]>0 then
                sprite(spr[tab[x][y]],x*size,y*size+10,size)
            end
        end
    end
    fontSize(20)
    fill(255)
    rect(WIDTH-30,HEIGHT-30,60,60)
    fill(0)
    text("reset",WIDTH-30,HEIGHT-10)
    text("low",WIDTH-30,HEIGHT-30)
    text("score",WIDTH-30,HEIGHT-50)
    fill(255)
    text("Score  "..score.."    Low Score  "..string.format("%d",lowScore).."    Moves left  "..count,WIDTH/2,HEIGHT-10)
    if gameOver then
        fontSize(40)
        fill(255)
        text("NO MORE MOVES",WIDTH/2,HEIGHT/2)
    end
end

function touched(t)
    if t.state==BEGAN and t.x>WIDTH-60 and t.y>HEIGHT-60 then
        lowScore=xs*ys
        saveProjectData("lowScore",lowScore)
        return
    end
    x=math.floor((t.x-size/2)/size)+1
    y=math.floor((t.y-size/2)/size)+1
    if x<1 or x>xs or y<1 or y>ys then
        fillGap()
        return
    end
    if t.state==BEGAN and tab[x][y]>0 then
        selected=tab[x][y]
        selX=x
        selY=y
    end
    if t.state==ENDED and selected>0 then
        if validMove() and selected==tab[x][y] then
            tab[selX][selY]=0
            selected=0
            score=score-1
            checkScore()
        end
        fillGap()
    end
end

function validMove()
    if selX==x and (selY==y+1 or selY==y-1) then
        return true
    elseif selY==y and (selX==x+1 or selX==x-1) then
        return true
    end
    return false
end

function checkScore()
    if score<lowScore then
        lowScore=score
        saveProjectData("lowScore",lowScore)
    end    
end

function fillGap()
    for x=1,xs do
        for y=1,ys do
            if tab[x][y]==0 then
                for z=1,ys-y do
                    if tab[x][y+z]~=0 then
                        tab[x][y]=tab[x][y+z]
                        tab[x][y+z]=0
                        break
                    end                   
                end
            end
        end
    end
    checkPieces()
end

function checkPieces()
    count=0
    gameOver=true
    for x=1,xs do
        for y=1,ys do
            if tab[x][y]>0 then
                for h=1,8,2 do
                    x1=x+moves[h]
                    y1=y+moves[h+1]
                    if x1>=1 and x1<=xs and y1>=1 and y1<=ys then
                        if tab[x][y]==tab[x1][y1] then
                            gameOver=false
                            count=count+1
                        end
                    end
                end                
            end
        end
    end
end

@dave1707 - much better, image difference more pronounced and larger size better on the eye. I have a slight problem with the touch on the periphery response seems poor - noticed more on right side and bottom. May be down to my huge finger tips. Going to play with this - I think this could amuse the grandkids. Thanks.

@Bri_G One thing you can try is to touch a square, wait a fraction of a second, then slide it. You might be hitting the first touch on a slide and it’s not being recognized. I’m only looking for BEGAN or ENDED and not CHANGED in the touched function.

@dave1707 - that seemed to improve the response a little thanks.
Ran into a couple of issues with it though. Firstly trying to modify code to use a path and array for loading sprites and the format I’m using wouldn’t allow underscores in the Sprite name. Any way round that?
Secondly, whilst playing the game I had a dialogue pop up about screen recording. May have hit the button bar in bottom corner, but didn’t recognize the dialogue. On selecting the no option Codea crashed -wasn’t expecting that. Do you think this may be due to old code in the latest version? I think most users use the iOS recording system now. An issue for @Simeon ?