Classic 15 Puzzle Game

I’m trying to create a 15-puzzle (also called Gem Puzzle, Boss Puzzle, Game of Fifteen, Mystic Square and many others) game. It is a sliding puzzle that consists of a frame of numbered square tiles in random order with one tile missing. I’m going to have pictures in place of number tiles to make it more complicated. The object of the puzzle is to place the tiles in order by making sliding moves that use the empty space. Unfortunately I just don’t seem to be able to figure out how to do this. I can make a board of tiles with no problem and can figure out what tile has been clicked on but I don’t know how I can figure out if that tile can move or not based on whether there is an open space next to it. Any help would be greatly appreciated. [import]uid: 168506 topic_id: 34678 reply_id: 334678[/import]

I would suggest an underlying data structure to hold the information and for example let zero be the free position. Like this:

local board = { {1,2,3,4}, {5,6,7,8}, {9,10,11,12}, {13,14,15,0} }

From here you can build functions that operate on the data structure. Maybe:

row, col = findPosition(board, 0) -\> row = 4 and col = 4 I hope this gives you some ideas how it could be implemented.

(By the way… remember not to randomize the positions of the board. It will render the game unsolvable in half of the cases.) [import]uid: 25729 topic_id: 34678 reply_id: 137878[/import]

Hi - funny you posted this. I was recently working on implementing exactly what you describe. I’m challenging myself to shrink the code to under 1k for the CoronaPaper challenge, but I’ve only managed to get it down to around 1500 bytes so far. But here is a main.lua that will achieve exactly what you describe - plus I built in the ability to take a photo with the camera and slice up that photo to make the tiles of the game. Forgive the difficult-to-read nature of this code - I truncated as much of it as I could to try and get under 1k. But if you load this up in the simulator (best viewed on the iPhone 3GS skin) you’ll see it works…

p=require "physics" P=p.addBody w=require "widget".newButton a=40 b=120 c=200 d=280 e=39.5 f=320 h=80 D=display D.setStatusBar(D.HiddenStatusBar) r=D.newRect g=D.newGroup q="static" t=transition.to G=g() s="strokeWidth" S={{a,a},{b,a},{c,a},{d,a},{a,b},{b,b},{c,b},{d,b},{a,c},{b,c},{c,c},{d,c},{a,d},{b,d},{c,d}} T={} p.start() p.setGravity(0,0) function A() for i=1,15 do if T[i].p then T[i].p:removeSelf();T[i].p=nil else media.show(media.Camera,Z) return true end end end function B(E) W=E.target X=E.phase Y=D.getCurrentStage() if "began"==X then Y:setFocus(W,E.id) W.bodyType="dynamic" W.j=p.newJoint("touch",W,E.x,E.y) elseif "moved"==X then W.j:setTarget(E.x,E.y) elseif X==("ended" or "cancelled") then Y:setFocus(W,nil) W.j:removeSelf() W.bodyType=q for i=a,d,h do if i-W.xt(W,{x=i,time=a}) end if i-W.yt(W,{y=i,time=a}) end end end end function Z(E) U=E.target G:insert(U) w=480/U.contentHeight U:scale(w,w) U.x=160 U.y=240 A() for i=1,15 do u=D.captureBounds({xMin=S[i][1]-a,yMin=S[i][2]-a,xMax=S[i][1]+a,yMax=S[i][2]+a}) u.x=0 u.y=0 u[s]=2 T[i]:insert(u) T[i].p=u end U:removeSelf() end z=r(0,f,f,9);P(z,q) z=r(-9,0,9,f);P(z,q) z=r(f,0,9,f);P(z,q) z=r(0,-9,f,9);P(z,q) for i=1,15 do j=g() P(j,q,{shape={-e,-e,e,-e,e,e,-e,e}}) j.isFixedRotation=true j:addEventListener("touch",B) l=r(0,0,h,h,14) l.x=0 l.y=0 l:setStrokeColor(0,0,0) l[s]=2 j:insert(l) k=D.newText(j,i,0,0,native.systemFont,24) k.x,k.y=0,0 k:setTextColor(0,0,0) j:insert(k) j.x=S[i][1] j.y=S[i][2] T[i]=j G:insert(j) end z=w{label="set/remove photo",onRelease=A} z.x=160 z.y=400 [import]uid: 27636 topic_id: 34678 reply_id: 137897[/import]

Jason,
This is exactly what i was looking for!!! Thanks so much for the help. You are the greatest … I would give you the prize in the CoronaPaper challenge. [import]uid: 168506 topic_id: 34678 reply_id: 137973[/import]

You’re welcome, Betsy. Glad I could help! :slight_smile: [import]uid: 27636 topic_id: 34678 reply_id: 137993[/import]

I would suggest an underlying data structure to hold the information and for example let zero be the free position. Like this:

local board = { {1,2,3,4}, {5,6,7,8}, {9,10,11,12}, {13,14,15,0} }

From here you can build functions that operate on the data structure. Maybe:

row, col = findPosition(board, 0) -\> row = 4 and col = 4 I hope this gives you some ideas how it could be implemented.

(By the way… remember not to randomize the positions of the board. It will render the game unsolvable in half of the cases.) [import]uid: 25729 topic_id: 34678 reply_id: 137878[/import]

Hi - funny you posted this. I was recently working on implementing exactly what you describe. I’m challenging myself to shrink the code to under 1k for the CoronaPaper challenge, but I’ve only managed to get it down to around 1500 bytes so far. But here is a main.lua that will achieve exactly what you describe - plus I built in the ability to take a photo with the camera and slice up that photo to make the tiles of the game. Forgive the difficult-to-read nature of this code - I truncated as much of it as I could to try and get under 1k. But if you load this up in the simulator (best viewed on the iPhone 3GS skin) you’ll see it works…

p=require "physics" P=p.addBody w=require "widget".newButton a=40 b=120 c=200 d=280 e=39.5 f=320 h=80 D=display D.setStatusBar(D.HiddenStatusBar) r=D.newRect g=D.newGroup q="static" t=transition.to G=g() s="strokeWidth" S={{a,a},{b,a},{c,a},{d,a},{a,b},{b,b},{c,b},{d,b},{a,c},{b,c},{c,c},{d,c},{a,d},{b,d},{c,d}} T={} p.start() p.setGravity(0,0) function A() for i=1,15 do if T[i].p then T[i].p:removeSelf();T[i].p=nil else media.show(media.Camera,Z) return true end end end function B(E) W=E.target X=E.phase Y=D.getCurrentStage() if "began"==X then Y:setFocus(W,E.id) W.bodyType="dynamic" W.j=p.newJoint("touch",W,E.x,E.y) elseif "moved"==X then W.j:setTarget(E.x,E.y) elseif X==("ended" or "cancelled") then Y:setFocus(W,nil) W.j:removeSelf() W.bodyType=q for i=a,d,h do if i-W.xt(W,{x=i,time=a}) end if i-W.yt(W,{y=i,time=a}) end end end end function Z(E) U=E.target G:insert(U) w=480/U.contentHeight U:scale(w,w) U.x=160 U.y=240 A() for i=1,15 do u=D.captureBounds({xMin=S[i][1]-a,yMin=S[i][2]-a,xMax=S[i][1]+a,yMax=S[i][2]+a}) u.x=0 u.y=0 u[s]=2 T[i]:insert(u) T[i].p=u end U:removeSelf() end z=r(0,f,f,9);P(z,q) z=r(-9,0,9,f);P(z,q) z=r(f,0,9,f);P(z,q) z=r(0,-9,f,9);P(z,q) for i=1,15 do j=g() P(j,q,{shape={-e,-e,e,-e,e,e,-e,e}}) j.isFixedRotation=true j:addEventListener("touch",B) l=r(0,0,h,h,14) l.x=0 l.y=0 l:setStrokeColor(0,0,0) l[s]=2 j:insert(l) k=D.newText(j,i,0,0,native.systemFont,24) k.x,k.y=0,0 k:setTextColor(0,0,0) j:insert(k) j.x=S[i][1] j.y=S[i][2] T[i]=j G:insert(j) end z=w{label="set/remove photo",onRelease=A} z.x=160 z.y=400 [import]uid: 27636 topic_id: 34678 reply_id: 137897[/import]

Jason,
This is exactly what i was looking for!!! Thanks so much for the help. You are the greatest … I would give you the prize in the CoronaPaper challenge. [import]uid: 168506 topic_id: 34678 reply_id: 137973[/import]

You’re welcome, Betsy. Glad I could help! :slight_smile: [import]uid: 27636 topic_id: 34678 reply_id: 137993[/import]

Hi Guys,

I am making a game like 8/15 puzzle like like the one you all have been discussing.
I have just started.I was only able to initialise the camera and gallery in Corona.But I have two problems on which I am stuck.

1.making a board and breaking the picture and moving the pieces.
2.Matching the pieces to check for proper pieces position.

Any code you guys can share will be very helpful.I will be very thankfull.

Thanks.
[import]uid: 139309 topic_id: 34678 reply_id: 139330[/import]

Hey wooyij,
Thanks to Jason I was able to get the sliding parts to work. For my game, I had a pre-made image sliced into 15 pieces that I loaded into the board. I loaded the pieces already scrambled, making sure it was a solvable puzzle, and went from there. To determine if the piece was in the right place, I actually used sensors to detect which piece was where. If you want, give me your e-mail address and I will send you what I have worked out. It’s not exactly what you are looking for since it sounds like you want to on-the-fly create the sliced images, but it might help with puzzle detection. If you can find a way to slice the image, you could use animation to move the pieces to the scrambled positions. You have to make sure that your puzzle is solvable though so you might have to store an array of solvable scrambles and randomly pick one to be used.
Betsy [import]uid: 168506 topic_id: 34678 reply_id: 139344[/import]

Hey Betsy,

Thanks for your help.I understood what u had said.MY email id is nasir.mayo@yahoo.com.
If you can send me what you have done.It will be really helpful.

Thanks.

[import]uid: 139309 topic_id: 34678 reply_id: 139441[/import]

Hi Guys,

I am making a game like 8/15 puzzle like like the one you all have been discussing.
I have just started.I was only able to initialise the camera and gallery in Corona.But I have two problems on which I am stuck.

1.making a board and breaking the picture and moving the pieces.
2.Matching the pieces to check for proper pieces position.

Any code you guys can share will be very helpful.I will be very thankfull.

Thanks.
[import]uid: 139309 topic_id: 34678 reply_id: 139330[/import]

Hey wooyij,
Thanks to Jason I was able to get the sliding parts to work. For my game, I had a pre-made image sliced into 15 pieces that I loaded into the board. I loaded the pieces already scrambled, making sure it was a solvable puzzle, and went from there. To determine if the piece was in the right place, I actually used sensors to detect which piece was where. If you want, give me your e-mail address and I will send you what I have worked out. It’s not exactly what you are looking for since it sounds like you want to on-the-fly create the sliced images, but it might help with puzzle detection. If you can find a way to slice the image, you could use animation to move the pieces to the scrambled positions. You have to make sure that your puzzle is solvable though so you might have to store an array of solvable scrambles and randomly pick one to be used.
Betsy [import]uid: 168506 topic_id: 34678 reply_id: 139344[/import]

Hey Betsy,

Thanks for your help.I understood what u had said.MY email id is nasir.mayo@yahoo.com.
If you can send me what you have done.It will be really helpful.

Thanks.

[import]uid: 139309 topic_id: 34678 reply_id: 139441[/import]

Hi, Jasonschroeder

Your post has been most helpful to me! Thank you very much)

And I have one more question. Could you, please, advise me on how to write the function that checks the 15 puzzle game for victory?

I’ve been trying to write it this and that way, but it wouldn’t work =(

I call my function at the end of your “B” function, so that I could check the state of all buttons on the board, but I don’t seem to understand how to correctly extract the coordinates of every button on the board at an exact moment so that I could check if they all are in the right places.

I’m looking forward to your reply) Thank you in advance!!)

Here’s my function:

local function Check4Victory(j) if (math.round(j[1].x) == a) and (math.round(j[1].y) == a) then checkSquaresPos = checkSquaresPos + 1 end if (math.round(j[2].x) == b) and (math.round(j[2].y) == a) then checkSquaresPos = checkSquaresPos + 1 end if (math.round(j[3].x) == c) and (math.round(j[3].y) == a) then checkSquaresPos = checkSquaresPos + 1 end if (math.round(j[4].x) == d) and (math.round(j[4].y) == a) then checkSquaresPos = checkSquaresPos + 1 end if (math.round(j[5].x) == a) and (math.round(j[5].y) == b) then checkSquaresPos = checkSquaresPos + 1 end if (math.round(j[6].x) == b) and (math.round(j[6].y) == b) then checkSquaresPos = checkSquaresPos + 1 end if (math.round(j[7].x) == c) and (math.round(j[7].y) == b) then checkSquaresPos = checkSquaresPos + 1 end if (math.round(j[8].x) == d) and (math.round(j[8].y) == b) then checkSquaresPos = checkSquaresPos + 1 end if (math.round(j[9].x) == a) and (math.round(j[9].y) == c) then checkSquaresPos = checkSquaresPos + 1 end if (math.round(j[10].x) == b) and (math.round(j[10].y) == c) then checkSquaresPos = checkSquaresPos + 1 end if (math.round(j[1].x) == c) and (math.round(j[11].y) == c) then checkSquaresPos = checkSquaresPos + 1 end if (math.round(j[12].x) == d) and (math.round(j[12].y) == c) then checkSquaresPos = checkSquaresPos + 1 end if (math.round(j[13].x) == a) and (math.round(j[13].y) == d) then checkSquaresPos = checkSquaresPos + 1 end if (math.round(j[14].x) == b) and (math.round(j[14].y) == d) then checkSquaresPos = checkSquaresPos + 1 end if (math.round(j[15].x) == c) and (math.round(j[15].y) == d) then checkSquaresPos = checkSquaresPos + 1 end if checkSquaresPos == 15 then local wSplash = display.newImage( "winSplash.png", true ) wSplash.x = Wd; wSplash.y = Hg+180; goWinSoundID = audio.loadSound("tada.mp3") goWinSound = audio.play( goWinSoundID ) physics.pause() end return true end

Hi mrjeenb3,

I think I might approach this in a slightly different way than you did above, actually. To start with, assign an “answer” sub-table to each tile, with X and Y values that correspond to the position the tile would be in, were the puzzle finished correctly. Something like:

[lua]
tile.answer= {x=40, y=40}
[/lua]

Then, every time the user lifts their finger off of a tile (triggered when event.phase==“ended” or event.phase==“cancelled”), you can run a compact Check4Victory function that sees if the tiles are in the right spot (or within a few pixels of the right spot, to allow for slight deviation from the exact winning X and Y position. (Keep in mind that the function below will not actually work - you’d need to change it to accurately reflect the table that contains all your tiles - plus I haven’t actually tested it in the simulator. There might be some little errors - I’m just “winging it” here):

[lua]
function Check4Victory()
for i=1, #tileTable do
local tile = tileTable[i]
local xOffset = math.abs(tile.x - tile.answer.x)
local yOffset = math.abs(tile.y - tile.answer.y)
if xOffset>5 or yOffset>5 then return true end --stops the loop if a tile is out of position

– show winning notification if all 15 tiles are in the right place
if i==#tileTable then
local wSplash = display.newImage( “winSplash.png”, true )
wSplash.x = Wd;
wSplash.y = Hg+180;
goWinSoundID = audio.loadSound(“tada.mp3”)
goWinSound = audio.play( goWinSoundID )
physics.pause()
end
end
end
[/lua]

I hope this helps!

Best,
Jason

Thank you ever so much 4 your help, Jason )

Wow, that was a quick response!

I’m very grateful to you))

Running away to code @once :) 

Hi, Jason.

It’s me again. Sorry for being such a nuisance.

I did as you advised me to. As far as I understood the table ‘tile’ should be used as an auxiliary table for my ‘Check4Victory’ function.

So I initialized a local variable ‘tile’ at first. Then I initialized all the 15 tiles ‘answer’-coordinates:

local tile = {answer = {}} tile.answer[1] = {x = 40, y = 40} tile.answer[2] = {x = 120, y = 40} tile.answer[3] = {x = 200, y = 40} tile.answer[4] = {x = 280, y = 40} tile.answer[5] = {x = 40, y = 120} tile.answer[6] = {x = 120, y = 120} tile.answer[7] = {x = 200, y = 120} tile.answer[8] = {x = 280, y = 120} tile.answer[9] = {x = 40, y = 200} tile.answer[10] = {x = 120, y = 200} tile.answer[11] = {x = 200, y = 200} tile.answer[12] = {x = 280, y = 200} tile.answer[13] = {x = 40, y = 280} tile.answer[14] = {x = 120, y = 280} tile.answer[15] = {x = 200, y = 280}

So the body of my ‘Check4Victory’ function looks like this:

local function Check4Victory(TileSet) for i=1, #TileSet do tile = TileSet[i] local xOffset = math.abs(tile.x - tile.answer.x) local yOffset = math.abs(tile.y - tile.answer.y) if xOffset\>5 or yOffset\>5 then return true end --stops the loop if a tile is out of position -- show winning notification if all 15 tiles are in the right place if i==#TileSet then local wSplash = display.newImage( "winSplash.png", true ) wSplash.x = Wd; wSplash.y = Hg+180; goWinSoundID = audio.loadSound("tada.mp3") goWinSound = audio.play( goWinSoundID ) physics.pause() end end end

*Note: the table ‘TileSet’ is the table which stores the set of actual tiles on the playboard.

However, every time I move a tile during the game, an error pops up in the console window of the simulator. It reads: “Runtime error: in
function ‘Check4Victory’ attempt to index field ‘answer’ (nil value)”.

I can’t figure out what exactly is wrong. Could you please help identify the mistake?

Thank you.

Hi again!

I think you misunderstood a bit - there’s no need to create a new table called “tile” as you showed above. If you have a table called “tileSet” that contains the actual tiles on the playboard, then you should assign each of the tiles in THAT table a “.answer” parameter. That way, your Check4Victory function will check the X and Y positions of each tile against the “answer” X and Y positions to see if they’re where they are supposed to be. Something like what you see below, though it might be easier to add these values at the moment you actually create the tiles:

[lua]
tileSet[1].answer = {x = 40, y = 40}
tileSet[2].answer = {x = 120, y = 40}
tileSet[3].answer = {x = 200, y = 40}
tileSet[4].answer = {x = 280, y = 40}
tileSet[5].answer = {x = 40, y = 120}
tileSet[6].answer = {x = 120, y = 120}
tileSet[7].answer = {x = 200, y = 120}
tileSet[8].answer = {x = 280, y = 120}
tileSet[9].answer = {x = 40, y = 200}
tileSet[10].answer = {x = 120, y = 200}
tileSet[11].answer = {x = 200, y = 200}
tileSet[12].answer = {x = 280, y = 200}
tileSet[13].answer = {x = 40, y = 280}
tileSet[14].answer = {x = 120, y = 280}
tileSet[15].answer = {x = 200, y = 280}
[/lua]

The runtime error you’re getting is because the Check4Victory function is looking for the “.answer” parameter on each tile, but you haven’t set those values as a parameter of the actual tiles. Once you assign those values to the actual tiles, it should work as expected.

I hope that makes sense.

Best,
Jason