- Learn
- Tutorials
- Algorithms
- A* Search
- 5 New Puzzles and Two Estimates
5 New Puzzles and Two Estimates
By the end of this chapter, New puzzle and Reset join the navy bar, and an Estimate button sits on the card. A line under the buttons says what each Step does. Solve your puzzle with each Estimate, and the count tells you which guess was closer.
The game as it is at the end of this chapter. Click the game to play.
If your game and this chapter part ways, here is the chapter’s finished project. It’s the whole game, ready to open beside yours.
To open it, start Myth Engine, choose File › Open Project... and pick the downloaded file.
5.1 The Idea
Try the game above. Press Solve, and the count stops at 20. The Tiles slide home, and the card says Solved.
Now press Reset, or R. The Tiles don’t slide back. They jump, all at once, to the puzzle you started with, and the count reads 0 again.
On the card, under the count, a button reads Estimate: Manhattan. Press it, or E, and now it reads Estimate: Misplaced Tiles. Press Solve once more.
The count runs on past 20 this time. It stops at 84. Then the Tiles slide home, in 12 Moves, the same as before.
Last, press New puzzle, or N. The Tiles jump to a Board you’ve never seen, and to another when you press it again.
Under the navy bar, a line of pale letters has been talking the whole time. Whenever the work changes, it says what a Step does now. That’s the Message Line.
Until now, your solver had one puzzle and one way to guess. That was enough to watch A* Search work. It isn’t enough to ask the question underneath it. Does the guess matter?
Chapter 2’s Estimate is called Manhattan. It asks every Tile how far it is from home, in rows and columns, like counting city blocks. The new one is Misplaced Tiles. It only asks each Tile whether it’s home. A Tile that isn’t counts 1, however far away it sits.
Take Tile 1 on your puzzle. It sits at the bottom, three Slots from its home in the top-left corner. Manhattan counts 3 for it, and Misplaced Tiles counts 1. For your whole puzzle, Manhattan guesses 12 Moves, and Misplaced Tiles guesses 8. The real answer is 12.
Both guesses are honest. A Tile out of place needs at least one Move, and at least as many as its rows and columns. So neither guess ever says too many, and both find a shortest Route. But Misplaced Tiles says less. More Boards look as good as the best one, and the search has to check them before it can be sure. That’s the 84.
The new puzzles need one more new word. A Move slides one Tile. A jump puts a whole Board on the table at once, with no slides. New puzzle and Reset both jump.
5.2 The Plan
Here’s how the new pieces join the game. Read each line once for its shape, and come back to it after the last part.
- Run Search names a situation -> it copies that row’s explanation into
$messageLine-> the Message Line shows it. - You press New puzzle, or N -> New Puzzle asks Scramble Board for a Board -> Scramble Board makes 12 random Moves from the Goal with Random Move.
- New puzzle or Reset puts a Board in
$boardState-> New Puzzle sendsboardJumpedto every Object. - Every Tile, Hand Moves and Solve Timer hear
boardJumped-> each Tile jumps to its Slot, and the other two stop waiting for a slide. - You press the Estimate button, or E -> Choose Estimate moves
$estimateto the next row of Estimates -> Estimate Moves Left counts that way from now on. - A hand Move, a jump, or a new Estimate -> Run Search sends itself
cancelSearch-> the next Step searches again.
5.3 Set Up
This chapter’s Set up card has four blocks.
COMMANDS adds three Buttons: New puzzle, joined to N, Reset, joined to R, and Change Estimate, joined to E. Make them in the Input Manager, as before.
GLOBAL VARIABLES adds two Text Globals. The first, $estimate, is the Estimate the search uses, and it starts at Manhattan. The second, $messageLine, is the sentence under the buttons, and it starts empty. On Start Board’s canvas, press + on the Globals card and choose New Global… for each, as before. Type Manhattan into the Default box of $estimate, with no quote marks. Clear the Default box of $messageLine.
CHANGE THESE PLACEMENTS makes room. The navy bar needs space for four buttons now, so Step moves left, to 209, and Solve follows it, to 403. Those are the two Puzzle Button lines, Step first. On the card, the Status Line moves up to -560 and States Checked to -525, so the Estimate button fits under them. Select each one in the Scene, and type its new numbers. A placement’s own values on the Making page says more.
PLACE THESE waits. Each of its three buttons, and the Message Line, goes in with the part that makes what it shows or rings.
NRE5.5 New Puzzles
Next, the table gets new puzzles.
Chapter 1’s Challenge said it first. Any Board you reach by sliding Tiles away from the Goal can be solved, because every Move can be undone. A New puzzle is made exactly that way. Start from the Goal, make random Moves, and wherever you stop is the new puzzle.
5.5.1 Puzzle Settings, Changed
Puzzle Settings gets a third column, scrambleMoves, a Number that starts at 12, and its one row, puzzle, holds 12 too. That’s how many random Moves a New puzzle makes. Like explanation, it’s a new field of the table’s Data Structure, Puzzle Settings Entry, made with Add Field.
One row of the "Puzzle Settings" Data Table.
542703816123456780125.5.2 Random Move
Random Move makes one random Move on a Board. It’s handed a board, and notFromSlot, a Slot whose Tile may not move. It gives back the new board, and movedTo, the Slot the Tile slid into.
Two answers make a small Data Structure, Random Move Result, as they did for Pick Best Open State in Chapter 2. Make it first. Then it’s on the Return Type list.
What Random Move gives back: board, the Board after the random Move, and movedTo, the Slot the Tile slid into (where the Blank was).
Picking at random takes two walks over the Slots. Think of a teacher who has to choose one raised hand, fairly. First she counts the hands. She picks a number, and counts again in the same order, stopping at that number.
Random Move does the same. Three Declare Variables come first: choices, counted and fromSlot. The last starts at -1, before the first Slot. Its Repeat counts every Tile beside the Blank into choices, all but the one in notFromSlot. Then Generate Random Integer picks a number from 1 to choices, as pick.value.
The second count is a While. Each time round, fromSlot moves on one Slot, and counted counts the same Tiles again. When counted reaches pick.value, the loop ends. The Tile in fromSlot is the one that slides, and Board After Move, from Chapter 1, makes the Move. Last, the Return fills in both answers.
Every Tile beside the Blank has the same chance. And the While always ends, because pick is never more than the Tiles there are to count. If, Else, Repeat and While on the Making page shows a While, and Actions, one at a time shows Generate Random Integer.
Makes one random legal Move on a Board: one of the Tiles beside the Blank, each with the same chance, slides into it. The Tile in notFromSlot is never chosen, so the Move just made is not undone (-1 lets every Tile beside the Blank be chosen). movedTo is the Slot the Tile slid into, where the Blank was.
- Name
choices- Value
0
- Name
counted- Value
0
- Name
fromSlot- Value
-1
- Action Name
blank- board
board- tile
0
9times- Action Name
apart- fromSlot
local.index- toSlot
blank.value
apart.value == 1 && local.index != notFromSlot- Variable
choices- Operator
+=- Value
1
- Action Name
pick- Minimum
1- Maximum
choices
counted < pick.value- Variable
fromSlot- Operator
+=- Value
1
- Action Name
apartAgain- fromSlot
fromSlot- toSlot
blank.value
apartAgain.value == 1 && fromSlot != notFromSlot- Variable
counted- Operator
+=- Value
1
- Action Name
afterMove- board
board- fromSlot
fromSlot- toSlot
blank.value
- board
afterMove.value- movedTo
blank.value
5.5.3 Scramble Board
Scramble Board makes the whole puzzle. It declares two Variables, board and notFromSlot, and puts the goalBoard in board. Then it calls Random Move as many times as scrambleMoves says, each time on the Board the last Move made. It reads the answers as scramble.board and scramble.movedTo.
Each time, it hands over the last movedTo as notFromSlot. The Tile that slid last sits there now, where the Blank was a moment ago. Sliding it straight back would undo the Move, so that’s the one Tile that may not go.
Random Moves can wander back to the Goal. A New puzzle that’s already solved is no puzzle at all. So if that happens, the If at step 8 makes one more Move.
It has no PARAMETERS. Its Return hands back board, a Number.
Makes a New puzzle: starts from the Goal and makes Puzzle Settings / scrambleMoves random legal Moves (Random Move), never undoing the Move just made. If that lands on the Goal, one more Move is made. A Board made by legal Moves from the Goal can always be solved.
- Name
board- Value
0
- Name
notFromSlot- Value
-1
- Variable
board- Value
data('Puzzle Settings', 'puzzle', 'goalBoard')
data('Puzzle Settings', 'puzzle', 'scrambleMoves')times- Action Name
scramble- board
board- notFromSlot
notFromSlot
- Variable
board- Value
scramble.board
- Variable
notFromSlot- Value
scramble.movedTo
board == data('Puzzle Settings', 'puzzle', 'goalBoard')- Action Name
oneMore- board
board- notFromSlot
notFromSlot
- Variable
board- Value
oneMore.board
- Value
board
5.5.4 New Puzzle
New Puzzle is the Logic behind two buttons. New puzzle asks Scramble Board for a Board and puts it in $boardState. Reset puts back the Board the last New puzzle made.
So it remembers that Board, in resetBoard. That field lives in a Data Structure of New Puzzle’s own, New Puzzle Data. On its Data Structures card, press + and choose New Data Structure, as you did for Run Search Data in Chapter 3. New Puzzle reads the field as self.newPuzzleData.resetBoard, and Change Component Value changes it.
What New Puzzle remembers on Puzzle Rules: resetBoard, the Board Reset puts back (the Board of the last New puzzle, or the Board the game opened with).
When Created stores the first Board: the one the game opened with. Create runs after Scene Start, where Start Board has set up the Board, so it’s there to remember. After that, each New puzzle stores its own.
N and R ring newPressed and resetPressed, the messages the buttons send, the way A does for Solve.
Reset has one more job, for a puzzle that was refused. Remember Chapter 2, when you typed a Board into startBoard to see a refusal? A refused puzzle opens as the Goal. So when Reset puts back the Goal, it’s putting back a refused puzzle. Show Status Row names tryNew again, as it did when the game first opened that refused puzzle. New puzzle never makes the Goal, so nothing else brings it back.
Both buttons end the same way. They send boardJumped, with Trigger Custom Event, so every Object hears it. Nothing listens yet. Four pieces will: every Tile, Hand Moves, Solve Timer and Run Search.
Attach New Puzzle to Puzzle Rules once its Events are made. New Puzzle Data arrives on Puzzle Rules with it, From Logic, the way Run Search Data did.
Puts a whole Board on the table at once, without slides. New puzzle makes a Board by random legal Moves from the Goal (Scramble Board); Reset puts back the Board of the last New puzzle, or the Board the game opened with. Either way it broadcasts boardJumped, so the Tiles jump and the search starts over. When Reset puts back a Start Board that was refused (the Goal), the Status Line asks for a new puzzle again, as when the puzzle opened.
Any Objectself.newPuzzleData- Component Value
self.newPuzzleData.resetBoard- Value
$boardState
- Custom Event
newPressed
- Custom Event
resetPressed
- Action Name
scrambled
- Variable
$boardState- Value
scrambled.value
- Component Value
self.newPuzzleData.resetBoard- Value
$boardState
- Custom Event
boardJumped
- Variable
$boardState- Value
self.newPuzzleData.resetBoard
$boardState == data('Puzzle Settings', 'puzzle', 'goalBoard')- row
tryNew
- Custom Event
boardJumped
5.5.5 Slide To Slot, Changed
Now the Tiles learn to jump. Slide To Slot gets an Event for boardJumped, and it does what When Created used to do. It finds the Tile’s Slot and sets its position there at once.
First, though, it stops any slide under way, the slide timer and the glide both. So no Tile is left halfway.
When Created’s four steps all go. One new step takes their place, and it sends boardJumped to the Tile itself, with no Applies To. So a new Tile jumps to its Slot the same way the whole Board does. On the card, the four struck steps sit above the new step 1.
Keeps this Tile on the Slot the Board State gives it. It jumps there when it is created and when New puzzle or Reset puts a whole Board at once (boardJumped), cutting short any slide. When a Move moves it, it slides there in a straight line at an even speed, taking slideSeconds, lands exactly on the Slot, and tells Puzzle Rules that the slide is over.
Any Objectself.numberedTileself.slideToSlotData- Action Name
startSlot- board
$boardState- tile
self.numberedTile.number
- Action Name
startPlace- slot
startSlot.value- layout
board
- Position Relative To
Screen- X
startPlace.x- Y
startPlace.y
- Component Value
self.slideToSlotData.shownSlot- Value
startSlot.value
- Custom Event
boardJumped
- Timer Name
slide
- Action Name
jumpSlot- board
$boardState- tile
self.numberedTile.number
- Action Name
jumpPlace- slot
jumpSlot.value- layout
board
- Position Relative To
Screen- X
jumpPlace.x- Y
jumpPlace.y
- Component Value
self.slideToSlotData.shownSlot- Value
jumpSlot.value
- Action Name
mySlot- board
$boardState- tile
self.numberedTile.number
mySlot.value != self.slideToSlotData.shownSlot- Component Value
self.slideToSlotData.shownSlot- Value
mySlot.value
- Action Name
place- slot
mySlot.value- layout
board
- Value
self.transform.position.x- Target
place.x- Duration
@self.slideSeconds
- Value
self.transform.position.y- Target
place.y- Duration
@self.slideSeconds
- Timer Name
slide- Repeat Every (s)
@self.slideSeconds- Ring Once
Yes
- Action Name
restingPlace- slot
self.slideToSlotData.shownSlot- layout
board
- Position Relative To
Screen- X
restingPlace.x- Y
restingPlace.y
- Applies To
@self.puzzleRules- Custom Event
slideFinished
5.5.6 Hand Moves and Solve Timer, Changed
Two more listeners come next. A slide that’s cut short never finishes, so its slideFinished never comes. Hand Moves would wait for it forever in While Sliding, and Solve Timer would wait in Tile Sliding.
So each gets an On Board Jumped, and each switches back at once. Hand Moves goes back to Ready, where a click counts again.
Makes each Move on the Board State, one at a time. When a Tile beside the Blank is chosen (by the player, or by the Route), the Move is stored and every Tile is told (boardChanged); while that Tile slides, other choices are ignored. When New puzzle or Reset puts a whole Board at once (boardJumped), any slide is cut short, so choices count again.
Any Object- Action Name
chosenSlot- board
$boardState- tile
$chosenTile
- Action Name
blankSlot- board
$boardState- tile
0
- Action Name
apart- fromSlot
chosenSlot.value- toSlot
blankSlot.value
apart.value == 1- Action Name
afterMove- board
$boardState- fromSlot
chosenSlot.value- toSlot
blankSlot.value
- Variable
$boardState- Value
afterMove.value
- Event
On Tile Chosen- Mode
While Sliding
- Custom Event
boardChanged
- Event
On Tile Chosen- Mode
Ready
- Event
On Tile Chosen- Mode
Ready
Solve Timer goes to No Tile Sliding, so the Steps that Solve asks for go through again.
Decides when the solver takes a Step (takeStep): once each time the player presses Step, and, while Solve runs, up to stepsPerTick Steps every solveSeconds. No Step is taken while a Tile slides, so the Route plays one slide at a time and a Step pressed during a slide does nothing. The Solve button and the A key start Solve or pause it; Escape and Step pause it, and so does the search when it has nothing more to do. Pausing keeps all progress. The Status Line shows the words of $statusRow while Solve runs, and its pausedWords while it is paused.
Any Object- Variable
$solveButtonWords- Value
data('Messages', 'solveButton', 'words')
- Custom Event
solvePressed
- Custom Event
pauseSolve
- Timer Name
solveTick- Repeat Every (s)
@self.solveSeconds
- Event
On Solve Pressed- Mode
Running
- Variable
$solveButtonWords- Value
data('Messages', 'pauseButton', 'words')
- Variable
$statusLine- Value
data('Messages', $statusRow, 'words')
- Custom Event
pauseSolve
- Custom Event
stepDue
- Custom Event
pauseSolve
@self.stepsPerTicktimes- Custom Event
stepDue
- Custom Event
takeStep
- Event
On Step Due- Mode
Tile Sliding
- Event
On Step Due- Mode
No Tile Sliding
- Event
On Step Due- Mode
No Tile Sliding
- Timer Name
solveTick
- Event
On Solve Pressed- Mode
Paused
- Variable
$solveButtonWords- Value
data('Messages', 'solveButton', 'words')
- Variable
$statusLine- Value
data('Messages', $statusRow, 'pausedWords')
Nobody will ever see these two Events work. Without them, a New puzzle pressed mid-slide would leave every click on a Tile doing nothing at all.
Two Puzzle Buttons go in the navy bar now, from PLACE THESE: New puzzle at 597 and -737, and Reset at 791 and -737. Give each its Tint, #F1E8D3, as you did for Step. PLACE THESE doesn’t list it, because it’s on the Puzzle Button’s own card, as Tint (on its placement). Each one’s words go in through Edit Object Locally, in its Text / UI: New puzzle, and Reset. Their messages are newPressed and resetPressed. Each goes in the Message box under Send When Pressed, as Solve’s did.
Run the game and press New puzzle. The Tiles jump, all at once, to a new Board. Press it again, and again. Then press Reset, and the last one comes back.
The search doesn’t hear boardJumped yet. Until the last part, jump before you press Step, not after.
5.6 Two Ways to Guess
Now the second guess.
5.6.1 Estimates
Estimates is a small table, with one row for each Estimate. Each row is named by the words the button will show: Manhattan, and Misplaced Tiles.
Its countsSlotsAway column is a Boolean. Yes means a Tile out of place counts as many Slots as it is from home. No means it counts 1. The next column names the Estimate that comes after this one. Manhattan’s next is Misplaced Tiles, and Misplaced Tiles’ next is Manhattan, so the two go round in a ring.
YesMisplaced TilesNoManhattanBoth columns are fields of Estimates Entry, made with Add Field. The card gives countsSlotsAway’s default as true, which is Yes written the other way. Clear the Value box of next.
One row of the "Estimates" Data Table.
5.6.2 Estimate Moves Left, Changed
Estimate Moves Left, from Chapter 2, learns the second way. First it reads countsSlotsAway from the row $estimate names, and keeps it in a Variable of the same name.
That Variable is a Boolean, like isFound in Chapter 2. It’s made by one of three new steps at the top, between the first Declare Variable and the Repeat. Steps 2 and 3 declare tileMoves and countsSlotsAway. For countsSlotsAway, pick Boolean in the block’s Type list, and give it true. Step 4 reads the table. Join each one in its place with the round handles.
For each Tile, it still finds how far the Tile is from home. Now that number goes into tileMoves first. If this Estimate doesn’t count Slots, and the Tile isn’t home, tileMoves becomes 1. Then tileMoves goes onto the total.
On the card, the old last step becomes step 10. Its Variable is tileMoves now, and its Operator goes back to =. So it keeps away.value in tileMoves. Steps 11, 12 and 13 are new, and they follow it, inside step 7’s If. Right-click that If and choose Add inside Then for 11 and 13. Step 11 is an If whose box joins two tests with &&, and step 12 goes inside it. Step 13 adds tileMoves onto total.
The Estimate of the Moves still needed from a Board to the Goal, by the Estimate chosen in $estimate (a row of the Estimates table). Manhattan distance adds, for each Tile, the rows plus the columns between its Slot and its Slot in the Goal; misplaced Tiles adds 1 for each Tile out of place. The Blank is not counted. Neither guesses more Moves than are really needed, which is what lets A* find the shortest Route.
- Name
total- Value
0
- Name
tileMoves- Value
0
- Type
Boolean- Name
countsSlotsAway- Value
true
- Variable
countsSlotsAway- Value
data('Estimates', $estimate, 'countsSlotsAway')
9times- Action Name
here- board
board- slot
local.index
here.value > 0- Action Name
home- board
data('Puzzle Settings', 'puzzle', 'goalBoard')- tile
here.value
- Action Name
away- fromSlot
local.index- toSlot
home.value
- Variable
tileMoves- Value
away.value
countsSlotsAway == false && tileMoves > 0- Variable
tileMoves- Value
1
- Variable
total- Operator
+=- Value
tileMoves- Value
away.value
- Value
total
5.6.3 Choose Estimate
Choose Estimate is the button’s Logic, and it’s short: E rings estimatePressed, as the button does.
Each press moves $estimate one step round the ring. While it’s Manhattan, the next column of Manhattan’s row says Misplaced Tiles, so that’s what it becomes. The next press reads Misplaced Tiles’ row, and Manhattan comes back. One look-up on the card does it all.
Then Choose Estimate sends estimateChanged, to tell Puzzle Rules that the guess the search uses has changed. Nothing listens for that yet either.
The Estimate button and the E key change the Estimate the search uses ($estimate) to the next row of the Estimates table. The search made with the old Estimate is cancelled, so both Estimates can be tried on the same Board.
Any Object- Custom Event
estimatePressed
- Variable
$estimate- Value
data('Estimates', $estimate, 'next')
- Custom Event
estimateChanged
Puzzle Rules’ card shows New Puzzle and Choose Estimate attached, below Solve Timer.
Run SearchRun Search DataPuzzle RulesThe last button goes on the card itself. Place a Puzzle Button at 766 and -610, under the count, with the same Tint, #F1E8D3, and estimatePressed in its Message box. It’s wider than the others, so three of its values go in through Edit Object Locally. In its Text / UI, the Text is Estimate: {$estimate}, braces and all. Its Transform’s Bounding Box Size and its Sprite’s Transform Size are both 260 by 48.
Run the game. The button on the card reads Estimate: Manhattan. Press it, or E, and it reads Estimate: Misplaced Tiles. Press it again, and it’s back.
The search doesn’t hear the change yet, and that’s the last part.
5.7 Every Change Starts Over
Last, the search learns to begin again.
Three things now make the search’s notebook wrong. A hand Move changes the Board it was searching, so every line in it is about some other Board. So does a jump. And a new Estimate makes every guess in it the wrong kind of guess. Each time, the answer is the same. Empty the notebook, and start over.
5.7.1 Run Search, Changed Again
Chapter 4’s On Board Changed did that work itself, in its Else. Now the work moves into an Event of its own, On Cancel Search, and three Events ask for it with one message, cancelSearch. On Board Changed sends it for your own Move. On Board Jumped sends it for a jump, and On Estimate Changed for a new guess. Each sends it to Puzzle Rules, with no Applies To.
So On Board Changed keeps one step in its Else, the one that sends cancelSearch. Its inner If gets a new step 4 as well. When the Route’s last Move makes the Goal, the Message Line says the Route has been played.
Here is the finished Event, whole, from Run Search’s own card. First, delete every step in its old Else. The If there has a step inside it, and deleting the If deletes that step as well. You’ll build those steps again in a moment, in On Cancel Search. Its card shows each one new.
Then build step 7 in the empty Else, with Add inside Else. Step 4 goes in the inner If, step 3. Right-click it and choose Add inside Then, and the new block joins that If’s line after Switch Mode. The card has step 4 first, but neither step reads what the other writes. So on your Event, Switch Mode stays step 4 and the new step is 5. Either order plays the same.
$boardState == self.runSearchData.routeNextBoard- Component Value
self.runSearchData.movesPlayed- Operator
+=- Value
1
$boardState == data('Puzzle Settings', 'puzzle', 'goalBoard')- Variable
$messageLine- Value
data('Messages', 'solved', 'explanation')
- Event
On Take Step- Mode
Finished
- Custom Event
cancelSearch
On Cancel Search is Chapter 4’s old Else, with one step more: step 7 puts the ready row’s sentence on the Message Line. As before, the Status Line keeps its words on the Goal. So Solved stays Solved. A refused puzzle still says Try a new puzzle.
The last two Events are one step each, and each sends cancelSearch.
- Table
"Search States"
- Table
"Solution Route"
- Component Value
self.runSearchData.routeNextBoard- Value
-1
- Variable
$statesChecked- Value
0
$boardState != data('Puzzle Settings', 'puzzle', 'goalBoard')- row
ready
- Variable
$messageLine- Value
data('Messages', 'ready', 'explanation')
- Event
On Take Step- Mode
Not Started
- Applies To
@self.puzzleRules- Custom Event
pauseSolve
- Custom Event
cancelSearch
- Custom Event
cancelSearch
Run the game. Press Solve, and let it finish. Then press Reset, press E, and press Solve again.
The count runs on past 20, all the way to 84, and the Tiles slide home.
Here is what to watch for.
- When the game opens, the line under the buttons reads Slide a Tile beside the Blank, or press Step to search from this Board.
- Press Step once. The count reads 1, and the card says Paused. The line now says each Step Expands the open Search State with the lowest Moves so far + Estimate (g + h).
- Press Solve. The count stops at 20, the Tiles slide home, and the card says Solved. The line says the Route has been played.
- Press Reset, or R. Every Tile jumps back to your puzzle at once. The count reads 0, the Status Line is empty, and the line under the buttons tells you how to begin.
- Press E. The button reads Estimate: Misplaced Tiles. Press Solve. This time the count stops at 84, and the Tiles slide home in 12 Moves, as before.
- Press E again, and then New puzzle. The Tiles jump to a Board you haven’t seen, and to another when you press New puzzle again.
- Click a Tile beside the Blank, then press Reset. The new puzzle comes back as New puzzle made it, not the puzzle you started with.
- Press Solve. The search solves the new puzzle too.
New puzzle makes a Board by random Moves from the Goal, so it can always be solved. Reset puts back the last one. Both jump, and boardJumped reaches every Tile, Hand Moves, Solve Timer and Run Search. Estimate Moves Left counts each Tile out of place as its Slots from home, or as 1, by the row in $estimate. A hand Move, a jump or a new Estimate empties the notebook through On Cancel Search. The Message Line says what each Step does now.
Underneath all of it is one idea. A guess that never says too many keeps A* Search honest. Manhattan and Misplaced Tiles both pass, so both find a shortest Route, 12 Moves from your puzzle. But the closer guess tells the search more. Manhattan checks 20 Boards, and Misplaced Tiles checks 84.
One more thing is true of this search, and you built it. Pick Best Open State reads every line of the notebook, one by one, on every Step. A search thousands of lines long would want a quicker way to find the best line. This one stays small enough to follow.
5.8 If It Didn’t Work
This chapter adds new ways to start over, so a slip often shows as something that doesn’t start over. That’s normal. Press a button, and watch the count and the line under the buttons.
New puzzle does nothing? Look at the button’s message, newPressed, and at New Puzzle on Puzzle Rules. Last, look at On New Pressed’s step 4. It’s a Trigger Custom Event, which rings for every Object, not the kind with on Object.
Some Tiles jump and some don’t? Perhaps Slide To Slot’s On Board Jumped asks about the wrong Tile, so look at its step 3, Slot Of Tile, whose tile should read self.numberedTile.number.
After a jump, clicks on the Tiles do nothing? Hand Moves may be stuck in While Sliding. Look at its On Board Jumped, which switches On Tile Chosen to Ready.
The count doesn’t go back to 0 after New puzzle, Reset or E? Look at Run Search’s On Board Jumped and On Estimate Changed, and at each one’s Custom Event. It should be cancelSearch, spelled the same as the On Cancel Search Event.
The Estimate button reads Estimate: and nothing after it? Look at $estimate’s Default, which should be Manhattan with no quote marks, and at the button’s Text, Estimate: {$estimate}, braces and all.
The button’s words change, but the count is 20 both ways? Look at Estimate Moves Left’s step 4, whose Value reads ‘countsSlotsAway’. The row names in Estimates, and the words in its next column, have to match letter for letter.
Misplaced Tiles doesn’t stop at 84? Perhaps step 11 lost its second test, tileMoves > 0, and without it a Tile already home counts 1 too.
The line under the buttons stays empty? Its Text should be {$messageLine}. After that, look at Run Search’s When the Scene Starts, and the explanation column, spelled the same in Messages and on every Value that reads it.
Each of these is one small fix. Make it, run the game again, and you’re back with the chapter. The catch-up download at the top of this chapter is the finished game’s project, if you’d like to compare.
Make the puzzles harder, and watch the two guesses part ways. In Puzzle Settings, set scrambleMoves to 30. Press New puzzle, and solve it with Manhattan. Then press Reset, press E, and solve it again with Misplaced Tiles. Write down both counts. Try a few puzzles. Which guess checks fewer Boards, and by how much?
If the card ever says Try a new puzzle., read the line under the buttons. The search grew too large, and that’s a real answer too.
Estimate Moves Left knows two ways to count, and a third is yours to invent. When you’re done, put scrambleMoves back to 12.
In Chapter 1, the navy bar under the Board was empty, and only your hands could move a Tile. Now it holds four buttons, and the card keeps a count, a guess and a reason. Every Tile, word and number on that screen is one you put there.
The lantern still burns at the corner of the table. Press New puzzle. See what your solver makes of it.
