8-puzzle game

Creating Games? Developing something for fun?

Moderators: FourthWorld, heatherlaine, Klaus, kevinmiller, robinmiller

Brittgriscom
Posts: 95
Joined: Wed Mar 30, 2011 10:15 am

8-puzzle game

Post by Brittgriscom » Fri Apr 01, 2011 3:34 pm

I couldn't get the 8-puzzle game featured in the lessons to work. Basically, when I click on the card, it should place the puzzle pieces on the card, right? Didn't work. Has anyone seen a working stack of this puzzle?
-

Klaus
Posts: 13820
Joined: Sat Apr 08, 2006 8:41 am
Location: Germany
Contact:

Re: 8-puzzle game

Post by Klaus » Fri Apr 01, 2011 4:51 pm

Hi Britt,

do you have an URL of that lesson handy, so I can take a look?


Best

Klaus

Brittgriscom
Posts: 95
Joined: Wed Mar 30, 2011 10:15 am

Re: 8-puzzle game

Post by Brittgriscom » Fri Apr 01, 2011 8:34 pm

The forum wouldn't let me post a link.

It is the first lesson in the folder "how to - step-by-step guides to tasks in livecode"

Thanks.

Brittgriscom
Posts: 95
Joined: Wed Mar 30, 2011 10:15 am

Re: 8-puzzle game

Post by Brittgriscom » Mon Apr 04, 2011 3:34 am

Creating the Board
Since I can't post links, here is the description. I notice it uses "new button" to create a button, rather than "create button". That could be why the board isn't being created for me.

The board is created as soon as openStack is called. You should only have to run the following code once, but there is no harm in calling the code several times.

First set up the variables that store information about the game:

# these are local stack variables that store the offset between tiles, the number of tiles
# used horizontally and vertically, the order of the tiles in the winning state and the
# number of the space button
local sOffset, sTilesX, sTilesY, sWinningState, sSpaceButton

Now create openStack that calls a command to initialize the local stack variables and set up the tiles that are to appear on the 3X3 grid.
Note: We are creating nine tiles on the board and then hide the last one. This makes it easier to keep track of the board state and swap tiles around. The Shuffle button is also created in openStack.

on openStack
# set up the constants for the game type
setGameConstants 3, 3
# remove any previous buttons that may already be on the card
# this prevents us from duplicating existing buttons
repeat while the number of buttons of this card > 0
delete button 1 of this card
end repeat
# create the board buttons
createBoard 50
# write the values onto the buttons
writeButtonLabels sWinningState
# create the space on the board
hide button ("b" & sSpaceButton)
# create the shuffle button
createShuffleButton
# make sure the board cannot be resized
set resizable of me to false
end openStack

Command setGameConstants sets up the local stack variables that store relevant board information. This command takes pPuzzleSizeX and pPuzzleSizeY that specify how many tiles are to appear horizontally and vertically on the game board.

command setGameConstants pPuzzleSizeX pPuzzleSizeY
local tTile
# set the offset between the squares on the board
put 2 into sOffset
# set up the number of tiles to use
put pPuzzleSizeX into sTilesX
put pPuzzleSizeY into sTilesY
# set up the start up and winning board state
put empty into sWinningState
repeat with tTile = 0 to (pPuzzleSizeX * pPuzzleSizeY - 1)
if sWinningState is not empty then
put " " after sWinningState
end if
put tTile after sWinningState
end repeat
# define what button is used as space
put pPuzzleSizeX * pPuzzleSizeY - 1 into sSpaceButton
end setGameConstants

Command createBoard places the eight tiles on the stack card. pPadding specifies the amount of space that is to be placed between the top and the left edge of the stack card and the grid of tiles. Command createBoard also populates the button scripts that are executed when you select a tile that is to be moved around the game board.

command createBoard pPadding
local tHorizontal, tVertical, tButtonNumber
put 0 into tButtonNumber
# cycle through the vertical board buttons
repeat with tVertical = 0 to sTilesY - 1
# cycle through the horizontal board buttons
repeat with tHorizontal = 0 to sTilesX - 1
# create a button that is used as a game tile
new button
set the width of button "New Button" to 60
set the height of button "New Button" to 60
set the location of button "New Button" to \
pPadding + tHorizontal * (60 + sOffset), pPadding + tVertical * (60 + sOffset)
# populate the button script for each tile
set the script of button "New Button" to \
"on mouseUp" & cr & \
"local tCurrentState" & cr & \
"swapButtons " & sSpaceButton & ", " & tButtonNumber & cr & \
"put boardToBoardState (" & pPadding & \
") into tCurrentState" & cr & \
"if manhattanDistance (tCurrentState) is 0 then" & cr & \
"answer " & quote & "You Made It!" & quote & " with "& quote & \
"OK" & quote & cr & \
"end if" & cr & \
"end mouseUp"
# give each button a unique name
set the name of button "New Button" to "b" & tButtonNumber
add 1 to tButtonNumber
end repeat
end repeat
end createBoard

Command writeButtonLabels assigns numbers to the labels of the tiles. The tiles are numbered 0 to 7. 8 is the hidden tile that represents the empty space. Argument pButtonLabelNames is the string of labels that is to be assigned to the tiles.

command writeButtonLabels pButtonLabelNames
local tButtonLabelName, tButtonNumber
put 0 into tButtonNumber
# populate each label of a button with a number
repeat for each word tButtonLabelName in pButtonLabelNames
set the label of button ("b" & tButtonNumber) to tButtonLabelName
add 1 to tButtonNumber
end repeat
end writeButtonLabels

Command createShuffleButton creates the Shuffle button and populates the script that is called when pressing the button. This command also resizes the card on which the button and tiles are placed.

command createShuffleButton
# create the shuffle button
new button
set label of button "New Button" to "Shuffle"
set width of button "New Button" to 60 * sTilesX + sOffset * (sTilesX - 1)
set location of button "New Button" to (60 * sTilesX + sOffset * (sTilesX - 1)) / 2 + 20, \
(60 * sTilesY + sOffset * (sTilesY - 1)) + 50
# populate the button script for the shuffle button
set the script of button "New Button" to \
"on mouseUp" & cr & \
"shuffleBoard 10" & cr & \
"end mouseUp"
set the name of button "New Button" to "Shuffle"
# resize the board for the buttons
set the width of me to 60 * sTilesX + sOffset * (sTilesX - 1) + 40
set the height of me to 60 * sTilesY + sOffset * (sTilesY - 1) + 80
end createShuffleButton

The board is now ready, we are just missing the underlying functionality that drives the game.

Shuffling the Tiles
In order to make the game more challenging, there is a Shuffle button that moves the tiles around. The Shuffle button randomly selects tiles and moves them a set number of times. pTimes specifies how many times a tile is to be moved.

command shuffleBoard pTimes
local tSwaps, tButtonToSwap, tLastTile
repeat while tSwaps < pTimes
# randomly chose a tile
put random (sSpaceButton) - 1 into tButtonToSwap
# if the tile is not the last one that was moved and
# the tile can be moved into the empty space then move
# it and increment the number of successful moves
if (tLastTile is not tButtonToSwap) and \
(isSwapable (sSpaceButton, tButtonToSwap) is true) then
swapButtons sSpaceButton, tButtonToSwap
put tButtonToSwap into tLastTile
add 1 to tSwaps
end if
end repeat
end shuffleBoard

Swapping Tiles
One of the most important actions to take is to move tiles around the board. This is easily implemented on a physical board due to the natural constraints that real tiles have. Simulating these constraints in software is somewhat more challenging. The following code implements the physical constraints, but it is easy to change the code to allow for more exotic moves. An example would be to allow tiles to move diagonally.

Function isSwapable provides a test that determines if two tiles are either horizontally or vertically adjacent.
Note: We are using the physical location of the buttons to calculate whether or not a move is possible. pButton1 and pButton2 are the two tiles to be tested.

function isSwapable pButton1, pButton2
local tRelativeX, tRelativeY
# calculate the horizontal offset between two button locations
put item 1 of location of button ("b" & pButton1) - \
item 1 of location of button ("b" & pButton2) into tRelativeX
# calculate the vertical offset between two button locations
put item -1 of location of button ("b" & pButton1) - \
item -1 of location of button ("b" & pButton2) into tRelativeY
# if there is only a horizontal or a vertical offset and the offset is of a specific size
# then the tiles can be swapped
if (tRelativeX is 0 and abs (tRelativeY) is 60 + sOffset) or \
(tRelativeY is 0 and abs (tRelativeX) is 60 + sOffset) then
return true
else
return false
end if
end isSwapable

Command swapButtons swaps two tiles around. A test is also performed to determine whether or not tiles can be swapped. The moves are animated to provide more of a physical game experience. As with isSwapable, buttons pButton1 and pButton2 are the buttons to be swapped.

command swapButtons pButton1, pButton2
local tRelativeX, tRelativeY, tButtonLocation
# calculate the horizontal offset between two button locations
put item 1 of location of button ("b" & pButton1) - \
item 1 of location of button ("b" & pButton2) into tRelativeX
# calculate the vertical offset between two button locations
put item -1 of location of button ("b" & pButton1) - \
item -1 of location of button ("b" & pButton2) into tRelativeY
# if there is no horizontal offset but the vertical offset has a specific size
# then swap the two tiles
if (tRelativeX is 0 and abs (tRelativeY) is 60 + sOffset) or \
(tRelativeY is 0 and abs (tRelativeX) is 60 + sOffset) then
put location of button ("b" & pButton1) into tButtonLocation
set the moveSpeed to 65535
move button ("b" & pButton1) from location of button ("b" & pButton1) to \
location of button ("b" & pButton2) without messages
set the moveSpeed to 200
move button ("b" & pButton2) from location of button ("b" & pButton2) to \
tButtonLocation without messages
end if
end swapButtons

Did You Win?
Function boardToBoardState produces a string of numbers that specifies the order in which the labels on the tiles appear. pPadding specifies the amount of space that exists between the top and the left edge of the stack card and the grid of tiles.
Note: We have to access the physical button locations in order to determine the order in which the tiles are placed.

function boardToBoardState pPadding
local tHorizontal, tVertical, tButtonNumber, tPositionArray, tLine, tResult
# cycle through the vertical board buttons
repeat with tVertical = 0 to sTilesY - 1
# cycle through the horizontal board buttons
repeat with tHorizontal = 0 to sTilesX - 1
# cycle through the button numbers
repeat with tButtonNumber = 0 to sTilesX * sTilesY - 1
# put the number of the button label into an array
if location of button ("b" & tButtonNumber) is \
pPadding + tHorizontal * (60 + sOffset), pPadding + tVertical * (60 + sOffset) then
put tHorizontal + tVertical * sTilesX into tPositionArray [tButtonNumber]
end if
end repeat
end repeat
end repeat
# order the array by its keys
get the keys of tPositionArray
sort lines of it numeric by tPositionArray[each]
# convert the list of keys into a string
repeat for each line tLine in it
if tResult is not empty then
put " " after tResult
end if
put tLine after tResult
end repeat
return tResult
end boardToBoardState

Function manhattanDistance determines by how many squares a tile is displaced from the position that would be considered a winning position. There are simpler ways to determine if a board stated is a winning position, for example comparing pBoardState with the string of the winning board state. We are introducing the manhattan distance her as it will be used in the follow up lessons to help solve the puzzle automatically. pBoardState is a string with a sequence of tiles that is to be tested.

function manhattanDistance pBoardState
local tButton, tButton1H, tButton1V, tButton2H, tButton2V, tResult
# test how far each tile is away from its winning location
# we are not testing the location of the space
repeat with tButton = 0 to sSpaceButton - 1
# get the x and y positions of the two buttons to be swapped,
# with respect to the horizontal and vertical game grid
put (wordOffset (tButton, sWinningState) - 1) mod sTilesX into tButton1H
put (wordOffset (tButton, sWinningState) - 1) div sTilesY into tButton1V
put (wordOffset (tButton, pBoardState) - 1) mod sTilesX into tButton2H
put (wordOffset (tButton, pBoardState) - 1) div sTilesY into tButton2V
add abs (tButton1H - tButton2H) + abs (tButton1V - tButton2V) to tResult
end repeat
return tResult
end manhattanDistance

The 8-Puzzle Board

This figure shows you the board you should get when running the code in this lesson. The Shuffle button moves tiles around for you and changes the order in which the tiles are displayed.
To move a single tile, simply click on the tile you would like to move. In this example, you can only move tiles 5 and 7.

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3998
Joined: Sun Jan 07, 2007 9:12 pm
Location: Bochum, Germany

Re: 8-puzzle game

Post by bn » Mon Apr 04, 2011 1:33 pm

Hi Britt,

I am afraid that you just left out the script local variables at the top of the stack script:

Code: Select all

local sOffset, sTilesX, sTilesY, sWinningState, sSpaceButton
out side of any handler. It is in the lesson but a little hard to see because it follows a comment at the very beginning.

I append a working version of the stack which I have built with the code you posted and completed with the script local variables from the lesson.

Mind you many people here use a prepended s in a variable name to denote a script local variable (a variable that lives within the script and can be accessed by any handler in the script), a t prepending a local variable (one that only lives within a handler) And a g prepending a global variable. For custom properties some use c as a marker, k for a constant.

The concept of the different scope of the variable types is very important, if you don't know it yet please look it up in the User Guide.

Kind regards

Bernd

PS if you want to post a URL just paste the URL like this link to the lesson
http://lessons.runrev.com/spaces/lesson ... zzle-Game-

You can upload a png or jpeg image using the upload tab
Or a file if you zip it first, also via the upload tab

Dont forget to click "Add the file"
Attachments
8TileStack.livecode.zip
(2.99 KiB) Downloaded 503 times

Brittgriscom
Posts: 95
Joined: Wed Mar 30, 2011 10:15 am

Re: 8-puzzle game

Post by Brittgriscom » Mon Apr 04, 2011 8:10 pm

You're right. It works.
Thank you so much!

Brittgriscom
Posts: 95
Joined: Wed Mar 30, 2011 10:15 am

Re: 8-puzzle game

Post by Brittgriscom » Mon Apr 11, 2011 7:00 pm

Bernd,

I want to integrate a version of this puzzle into my storybook app. Right now I have the puzzle loading up on preOpenCard. It takes a couple of seconds to load up, though. Is there a way for the card and the puzzle on it to be built on openStack? The original version of this puzzle, of course, builds the puzzle on openStack, but I will have multiple cards in my stack, so I'm not sure how to put the puzzle onto a particular card.

Cheers,

Britt
Last edited by Brittgriscom on Tue Apr 12, 2011 2:19 pm, edited 2 times in total.

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3998
Joined: Sun Jan 07, 2007 9:12 pm
Location: Bochum, Germany

Re: 8-puzzle game

Post by bn » Mon Apr 11, 2011 7:17 pm

Britt,

there is no inherent reason I can see to build the puzzle from scratch each time you want to use it. For instructional purposes it is nice to see code how you could do it on the fly. You could build it once on a card and then just do a shuffle when opening the card. The code is nicely structured and you should be able to separate the building parts from the playing parts. Mind the script local variables.

Kind regards

Bernd

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3998
Joined: Sun Jan 07, 2007 9:12 pm
Location: Bochum, Germany

Re: 8-puzzle game

Post by bn » Mon Apr 11, 2011 7:36 pm

Hi Britt,

as far as I found in a quick test the only thing you need from the openstack handler is
setGameConstants 3, 3
you could put all in a card handler and just issue

Code: Select all

 setGameConstants 3, 3
in an opencard handler.

should work, not extensively tested

Kind regards

Bernd

Brittgriscom
Posts: 95
Joined: Wed Mar 30, 2011 10:15 am

Re: 8-puzzle game AI

Post by Brittgriscom » Thu May 05, 2011 8:46 pm

I'm checking out the puzzle solving AI, but it uses 'repeat' that stops everything. Any ideas how I could convert the 'repeat' messages to 'send' messages?

http://lessons.runrev.com/spaces/lesson ... -8-Puzzle-

or how to stop a repeat loop with a 'stop' button?

I'd post my version of the puzzle, but I can't find the 'attach file' tab. How do I do that?

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3998
Joined: Sun Jan 07, 2007 9:12 pm
Location: Bochum, Germany

Re: 8-puzzle game

Post by bn » Fri May 06, 2011 12:00 am

Hi Britt,

if a repeat loop is tight and long lasting it blocks Livecode as you noticed. A trick is to add a

Code: Select all

wait 0 milliseconds with messages
in the loop. The "with messages" gives Livecode time to do its clean-up and respond to messages.
And it lets you stop the execution by hitting the escape key or command-period.

If you have nested repeat loops choose a loop that runs often but not too often. The wait makes your stack responsive but also slows things down a bit. This is mostly a problem in very large datasets with data crunching/parsing. Usually it is not a big problem.

If you want to stop the execution of a repeat loop with a button you could query a global variable in the repeat loop right after the wait 0 milliseconds with messages that is changed by a buttton.

A wait 0 milliseconds without the "with milliseconds" part won't do the trick.

Kind regards

Bernd

To upload a stack you first have to zip it. Then in the editor of the forum below the text field there are two tabs.
Options and Upload attachement.
Click Upload attachment -> choose file and then don't forget to press the Add the file button.

You can upload unzipped png images I think up to 250 KB, same size applies to zipped files.

Brittgriscom
Posts: 95
Joined: Wed Mar 30, 2011 10:15 am

Re: 8-puzzle game

Post by Brittgriscom » Sun May 15, 2011 3:24 am

Thanks Bernd.

I've created a version of the game in which the user competes head-to-head with a bot employing the A Star path-finding algorithm. There are two 8-puzzles on the screen. After a shuffle, each board is identical. The object, of course, is to finish before the bot. Of course the bot is usually much faster than any human, so I've slowed him way down with this code after each swap he makes:

Code: Select all

wait 1600 milliseconds with messages
This way, the bot will finish in 30-40 seconds, and it will be competitive for humans. The problem is, when the bot is either moving or waiting (but not when he is thinking), the app responds really slowly to human actions.

My question is, how can I slow down the bot without slowing down the app's responsiveness to the user?

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3998
Joined: Sun Jan 07, 2007 9:12 pm
Location: Bochum, Germany

Re: 8-puzzle game

Post by bn » Sun May 15, 2011 4:32 pm

Hi Britt,

I changed the code of the handler "swapButtons" a bit:

Code: Select all

     set the moveSpeed to 65535
      move button ("b" & pButton1) from location of button ("b" & pButton1) to \
            location of button ("b" & pButton2) --without messages
      set the moveSpeed to 200
      move button ("b" & pButton2) from location of button ("b" & pButton2) to \
            tButtonLocation --without messages
   end if
end swapButtons
I just blocked "without messages"

This way when reshuffling the stack can be moved and will be responsive, but the move halts during some operations, e.g. moving the stack around.
seems to work.

What do you mean by the bot is waiting?

Kind regards

Bernd

Brittgriscom
Posts: 95
Joined: Wed Mar 30, 2011 10:15 am

Re: 8-puzzle game

Post by Brittgriscom » Sun May 15, 2011 5:50 pm

That works great in the desktop version, but it is still slow in the sim or on a device.

What do I mean when the bot is waiting?
Per the code above, I tell it to wait 1600 milliseconds between swaps.

Here is the bot's full "solvePuzzle" handler:

Code: Select all

command solvePuzzle pWidthPadding
, pHeightPadding 
    
   local tStatesToExplore, tExploredStates, tMove
    
   # create the first state to explore and calculate its estimated cost
    
   put "," & boardToBoardState (pWidthPadding
, pHeightPadding) into tStatesToExplore
    
   if manhattanDistance (item 2 of tStatesToExplore) is 0 then
        
      --         answer "Puzzle is Already Solved!" with "OK"
    
   else
        
      put costGH (tStatesToExplore) & "," & tStatesToExplore into tStatesToExplore
        
      repeat until tStatesToExplore is empty
  
              
         if manhattanDistance (item 3 of line 1 of tStatesToExplore) is 0 then
            
            repeat for each word tMove in item 2 of line 1 tStatesToExplore
  
               # trying to be able to stop it
               --               wait 0 seconds with messages      
               
               swapButtons gSpaceButton, tMove
 
               wait 1600 milliseconds with messages        
            end repeat

           exit repeat
                                  
         else
                
            exploreStates tStatesToExplore, tExploredStates
            
         end if
        
      end repeat
    
   end if
end solvePuzzle
I'm not sure why the 'exit repeat' needs to be there, but without it, the buttons keep shuffling indefinitely.

Note: copying the code into here added a lot of spaces

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3998
Joined: Sun Jan 07, 2007 9:12 pm
Location: Bochum, Germany

Re: 8-puzzle game

Post by bn » Sun May 15, 2011 7:16 pm

Hi Britt,

I am trying your script but it throws an error at

Code: Select all

put costGH (tStatesToExplore) & "," & tStatesToExplore into tStatesToExplore
it seems there is a function costGH that I don't have.

Kind regards

Bernd

Post Reply

Return to “Games”