TutorialsA* Search

4  Solve and Pause

Goal

By the end of this chapter, a gold Solve button sits beside Step. Press it, and the search runs on its own, and the Tiles slide home one after another. Press it again, and everything holds still until you’re ready.

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. Open it and compare, or start Chapter 5 from it.

8-Puzzle Solver, Chapter 4 finishedMyth project · 3.7 MB

To open it, start Myth Engine, choose File › Open Project... and pick the downloaded file.

4.1 The Idea

Try the game above. This time, press the gold button, Solve, beside Step, or press A.

The count doesn’t climb. It leaps. Before your eye can follow it, the count is at 20 and the card says Solving... And the Tiles begin to move on their own. Tile 1 slides up, then Tile 8 slides right, then the next, each at the same even speed as a click. The gold button reads Pause now.

Press it. The Tile that’s moving finishes its slide, and then nothing moves. The card says Paused.

Press Solve again. The Tiles go on from where they stopped, until the last one slides home and the card says Solved. The button reads Solve again, all by itself.

In Chapter 3, all of that took 46 presses of Step, one press at a time. Solve makes no new kind of search. It presses Step for you.

That’s the whole idea of this chapter, and here’s a way to picture it. Solve is a finger on the Step button. Every 0.05 seconds it presses, up to 8 times in a row, and each press is the very Step you took by hand. While a Tile slides, the finger lifts, so the Route plays one slide at a time. Pausing lifts it too, and nothing is lost. Every line in the notebook and every record of the Route stays where it was.

So who decides when a Step happens? Until now, Run Search listened for the Step button itself. Now a Step can come from the button, from Space or from Solve’s timer, so one new Logic Component, Solve Timer, takes every request. Run Search hears only one message, takeStep, and does what it always did.

One thing will look different. Press Step once, and the card says Paused, not Searching... Step takes one Step and then pauses Solve, in case it was running. A search that waits for your next press is a paused search, and the card says so.

For that, the Status Line has to remember what’s happening, not only its words. Each situation is one row of Messages, such as searching or solving. A situation has two sets of words now: one while Solve runs, and one while it’s paused.

Partway through this chapter, Step goes quiet for a while, and that’s expected. Solve Timer gives it back.

4.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.

  • You press Solve, or A -> Solve Timer starts its timer, solveTick, and the button reads Pause.
  • Each ring of solveTick -> Solve Timer asks for up to 8 Steps -> unless a Tile slides, each one becomes takeStep -> Run Search takes one Step, as before.
  • You press Step, or Space -> stepPressed reaches Solve Timer -> it asks for one Step, then sends pauseSolve.
  • A Move is made -> boardChanged -> Solve Timer takes no Step until slideFinished.
  • You press Pause or Escape, the Route ends, or your hand moves a Tile -> pauseSolve -> the timer stops, the button reads Solve, and the Status Line shows its paused words.
  • Run Search, Check Goal or Start Board names a situation -> Show Status Row keeps it in $statusRow and shows its words.

4.3 Set Up

This chapter’s Set up card has two Commands, two Globals and one placement.

COMMANDS adds Solve, a Button joined to A, and Pause, a Button joined to Escape. Make them in the Input Manager, the way you made Step.

GLOBAL VARIABLES adds two Text Globals. The first, $solveButtonWords, is what the gold button says, and it starts empty. The second, $statusRow, is the situation the Status Line is in. It starts at ready. On Start Board’s canvas, press + on the Globals card and choose New Global… for each, as before. Clear the Default box of $solveButtonWords. Type ready into the Default box of $statusRow, with no quote marks.

PLACE THESE has the gold button. It waits for the last part, once everything it rings is ready. The Set up card on the Making page says more about each block.

Set upWhat this chapter adds
COMMANDS (Game Settings > Input Manager)
SolveButton
KeybindingsA
PauseButton
KeybindingsEscape
GLOBAL VARIABLES
$solveButtonWordsTextstarts at
$statusRowTextstarts at ready
SCENE Puzzle Table: PLACE THESE
ObjectXY
Puzzle Button500-737
with Text / UI > Text = {$solveButtonWords} (Edit Object Locally)
with Sprite > Tint = #BC9152 (Edit Object Locally)
with Per Instance Variables > Message = solvePressed

4.4 Words That Wait

First, the Status Line learns to remember.

4.4.1 Messages, Changed

Messages gets a second column, pausedWords, a Text like words. A column is a field of Messages Entry, so open Messages Entry and press Add Field. Clear its Value box, as you did for words, because it starts empty.

Data StructureMessages EntryAddDelete

One row of the "Messages" Data Table.

wordsTextdefault (empty)
pausedWordsTextdefault (empty)

The new column shows up in Messages at once. Fill in each row’s pausedWords from the card below.

Look down that column. A search and the Route’s slides say Paused while they wait. Every other row keeps its own words, so a pause never hides Solved or Try a new puzzle.

Three rows go at the end. The solveButton and pauseButton rows are the gold button’s two words. The ready row has no words at all, because that’s the Status Line when nothing is under way.

Data TableMessagesData Structure: Messages EntryAddDelete
Row 0solved
wordsSolved
pausedWordsSolved
Row 1searching
wordsSearching...
pausedWordsPaused
Row 2tryNew
wordsTry a new puzzle.
pausedWordsTry a new puzzle.
Row 3building
wordsBuilding route...
pausedWordsBuilding route...
Row 4solving
wordsSolving...
pausedWordsPaused
Row 5solveButton
wordsSolve
pausedWords
Row 6pauseButton
wordsPause
pausedWords
Row 7ready
words
pausedWords

4.4.2 Show Status Row

Until now, every piece that changed the Status Line wrote its words straight onto it. Show Status Row writes down the situation first.

It’s handed a row’s name, in row. It keeps that name in $statusRow, and then it shows that row’s words. So the words are the same as before, and the game also knows which row they came from. Solve Timer will read that row again when Solve pauses, and show its pausedWords instead.

It’s two Change Variables. The second reads the row back by $statusRow, which the first has set a moment before.

FunctionShow Status Row

Shows a situation on the Status Line: $statusRow becomes row, the Messages row of that situation, and the Status Line shows that row's words. While Solve is paused, Solve Timer shows the same row's pausedWords instead.

PARAMETERS
rowText
ACTIONS
1Change Variable
Variable
$statusRow
Value
row
2Change Variable
Variable
$statusLine
Value
data('Messages', $statusRow, 'words')

Every place that wrote words onto the Status Line calls it now. On each card below, the old Change Variable is struck. Show Status Row takes its step, with its row typed bare, as the card shows it.

Check Goal names the solved row.

Logic ComponentCheck GoalAttach to: Puzzle RulesAddDelete

Says "Solved" on the Status Line when a Move, the player's or the Route's, makes the Goal, as its Tile begins to slide.

Attach toAny Object
On Board Changed
WhenboardChangedCustom Event
1If$boardState == data('Puzzle Settings', 'puzzle', 'goalBoard')
Then
2Change Variable
Variable
$statusLine
Value
data('Messages', 'solved', 'words')
2Show Status RowFunction
row
solved

Start Board names tryNew, when it refuses a puzzle.

Logic ComponentStart BoardAttach to: Puzzle RulesAddDelete

Sets up the Board State when the puzzle opens: the Start Board from Puzzle Settings, when it can reach the Goal (Check Start Board) and is not already the Goal. A Board that cannot, or that leaves nothing to solve, is refused: the Board opens as the Goal, and the Status Line asks for a new puzzle.

Attach toAny Object
When the Scene Starts
WhenScene Start
1Check Start BoardFunction
Action Name
startCheck
board
data('Puzzle Settings', 'puzzle', 'startBoard')
2IfstartCheck.value == true && data('Puzzle Settings', 'puzzle', 'startBoard') != data('Puzzle Settings', 'puzzle', 'goalBoard')
Then
3Change Variable
Variable
$boardState
Value
data('Puzzle Settings', 'puzzle', 'startBoard')
4Otherwise
5Change Variable
Variable
$boardState
Value
data('Puzzle Settings', 'puzzle', 'goalBoard')
6Change Variable
Variable
$statusLine
Value
data('Messages', 'tryNew', 'words')
6Show Status RowFunction
row
tryNew

Run the game. Nothing looks different, and that’s right. The words are the same, and now the game also remembers which row each one came from.

4.5 One Message for Every Step

Now Run Search stops listening for the Step button. From here on, every Step reaches it as one message, takeStep, whoever asked for it.

4.5.1 Run Search, Changed

On Run Search’s canvas, find On Step Pressed, the Event block with five Modes beside it. Right-click it and choose Change Event…. In the window, choose Custom Events > Project Events > Add Custom Event…, then type takeStep. A small window asks “Change Event?” first. Press Change Event.

The Event now listens for takeStep, and its name follows: On Take Step. All five Modes stay, with their Actions. Every Switch Mode that named On Step Pressed now names On Take Step by itself, so the lines the card marks there are right already.

Leave Run Search’s other Event, When Step Is Pressed, as it is. Space still rings stepPressed there, the way the Step button does, so Run Search sends that message. It no longer listens for it.

The rest of Run Search’s changes are small. Where it set the Status Line’s words, Show Status Row names the situation instead.

Not Started gains an Else, step 6. If the Board is the Goal already, there’s nothing to search. So it sends pauseSolve to Puzzle Rules, and a running Solve stops. To add it, right-click step 1, the If, and choose Add Else, then put its Action in with Add inside Else.

Logic ComponentRun SearchAttach to: Puzzle RulesAddDelete

Runs the A* solver one Step at a time, from the Board shown. Solve Timer sends takeStep for each Step. The first Step stores that Board as the first open Search State; each Step then Expands the best open Search State (Expand Best State) until the Goal is accepted (goalAccepted). Each Step then copies one record of the Route, back along the parents (Building Route), and then plays one of its Moves (Playing Route). When the search stops (searchStopped) or the Route has been played, Step has nothing more to do (Finished). A hand Move cancels the search. The Status Line says what the solver is doing ($statusRow).

Attach toAny Object
DATA STRUCTURES
Run Search Dataself.runSearchData
SHARED VARIABLES
NameTypeKindStarting Value
puzzleRulesObjectConstantNone
When Step Is Pressed
WhenCommand PressedCommand: Step
1Trigger Custom Event on Object
Applies To
@self.puzzleRules
Custom Event
stepPressed
On Take Step
WhentakeStepCustom Event
On Step Pressed
WhenstepPressedCustom Event
ModeNot Started (Starting Mode)
1If$boardState != data('Puzzle Settings', 'puzzle', 'goalBoard')
Then
2Add Search StateFunction
board
$boardState
movesSoFar
0
parentState
-1
3Change Variable
Variable
$statusLine
Value
data('Messages', 'searching', 'words')
3Show Status RowFunction
row
searching
4Switch Mode
Event
On Take Step
Event
On Step Pressed
Mode
Searching
5Expand Best StateFunction
puzzleRules
@self.puzzleRules
6Otherwise
7Trigger Custom Event on Object
Applies To
@self.puzzleRules
Custom Event
pauseSolve
On Take Step
WhentakeStepCustom Event
On Step Pressed
WhenstepPressedCustom Event
ModeSearching
1Expand Best StateFunction
puzzleRules
@self.puzzleRules

Building Route names solving where it once wrote the words. Playing Route doesn’t change. Finished gets its first Action: it sends pauseSolve, because the Route has been played and there’s nothing left to do.

Logic ComponentRun SearchcontinuedAddDelete
On Take Step
WhentakeStepCustom Event
On Step Pressed
WhenstepPressedCustom Event
ModeBuilding Route
1Copy Route RecordFunction
Action Name
copied
board
self.runSearchData.routeBoard
2Ifcopied.value == (-1)
Then
3Change Component Value
Component Value
self.runSearchData.movesPlayed
Value
0
4Change Variable
Variable
$statusLine
Value
data('Messages', 'solving', 'words')
4Show Status RowFunction
row
solving
5Switch Mode
Event
On Take Step
Event
On Step Pressed
Mode
Playing Route
6Otherwise
7Change Component Value
Component Value
self.runSearchData.routeBoard
Value
copied.value
On Take Step
WhentakeStepCustom Event
On Step Pressed
WhenstepPressedCustom Event
ModePlaying Route
1Change Component Value
Component Value
self.runSearchData.routeNextBoard
Value
data('Solution Route', text(self.runSearchData.movesPlayed + 1), 'state')
2Tile For MoveFunction
Action Name
routeTile
board
$boardState
nextBoard
self.runSearchData.routeNextBoard
3Choose TileFunction
puzzleRules
@self.puzzleRules
tile
routeTile.value
On Take Step
WhentakeStepCustom Event
On Step Pressed
WhenstepPressedCustom Event
ModeFinished
(no Actions)
1Trigger Custom Event on Object
Applies To
@self.puzzleRules
Custom Event
pauseSolve

On Goal Accepted names building, and On Search Stopped names tryNew.

Logic ComponentRun SearchcontinuedAddDelete
On Goal Accepted
WhengoalAcceptedCustom Event
1Change Component Value
Component Value
self.runSearchData.routeBoard
Value
data('Puzzle Settings', 'puzzle', 'goalBoard')
2Change Variable
Variable
$statusLine
Value
data('Messages', 'building', 'words')
2Show Status RowFunction
row
building
3Switch Mode
Event
On Take Step
Event
On Step Pressed
Mode
Building Route
On Search Stopped
WhensearchStoppedCustom Event
1Change Variable
Variable
$statusLine
Value
data('Messages', 'tryNew', 'words')
1Show Status RowFunction
row
tryNew
2Switch Mode
Event
On Take Step
Event
On Step Pressed
Mode
Finished

On Board Changed names ready where it emptied the Status Line, so a hand Move clears the words. And a hand Move stops Solve too. Step 13 sends pauseSolve, at the end of the Else, inside it.

Logic ComponentRun SearchcontinuedAddDelete
On Board Changed
WhenboardChangedCustom Event
1If$boardState == self.runSearchData.routeNextBoard
Then
2Change Component Value
Component Value
self.runSearchData.movesPlayed
Operator
+=
Value
1
3If$boardState == data('Puzzle Settings', 'puzzle', 'goalBoard')
Then
4Switch Mode
Event
On Take Step
Event
On Step Pressed
Mode
Finished
5Otherwise
6Reset Data Table
Table
"Search States"
7Reset Data Table
Table
"Solution Route"
8Change Component Value
Component Value
self.runSearchData.routeNextBoard
Value
-1
9Change Variable
Variable
$statesChecked
Value
0
10If$boardState != data('Puzzle Settings', 'puzzle', 'goalBoard')
Then
11Change Variable
Variable
$statusLine
Value
""
11Show Status RowFunction
row
ready
12Switch Mode
Event
On Take Step
Event
On Step Pressed
Mode
Not Started
13Trigger Custom Event on Object
Applies To
@self.puzzleRules
Custom Event
pauseSolve

Run the game and press Step. Nothing happens. Run Search waits for takeStep now, and nothing sends it yet.

4.6 A Finger on the Step Button

Last comes the piece that decides when.

4.6.1 Solve Timer

Solve Timer lives on Puzzle Rules, beside Run Search, and it has two Shared Variables you can feel. Its solveSeconds is how often its timer rings, 0.05 seconds, and its stepsPerTick is how many Steps each ring asks for, up to 8.

Make them first, on its Shared Variables card, as for slideSeconds in Slide To Slot. Both are Constant Numbers. The timer’s name, solveTick, goes into Start Recurring Timer bare, as the card shows it.

It has ten Events, so its card comes in five pieces. Make every Event first, with its Modes. Then add the Actions, leaving each Switch Mode for last.

The first piece is the keys. When the Scene starts, the gold button’s words become Solve. The A key rings solvePressed, the same message as the gold button. Escape rings pauseSolve. Each sends to Puzzle Rules, the Object Solve Timer is on, so there’s no Applies To line. Run Search’s sends name @self.puzzleRules, its Shared Variable for that same Object. Both reach Puzzle Rules.

Logic ComponentSolve TimerAttach to: Puzzle Rules

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.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
solveSecondsNumberConstant0.05
stepsPerTickNumberConstant8
When the Scene Starts
WhenScene Start
1Change Variable
Variable
$solveButtonWords
Value
data('Messages', 'solveButton', 'words')
When Solve Is Pressed
WhenCommand PressedCommand: Solve
1Trigger Custom Event on Object
Custom Event
solvePressed
When Pause Is Pressed
WhenCommand PressedCommand: Pause
1Trigger Custom Event on Object
Custom Event
pauseSolve

On Solve Pressed has two Modes, Paused and Running. Paused is the Starting Mode.

In Paused, a press starts the timer and switches to Running. The button reads Pause, and the Status Line shows the row’s words again, so a paused search goes back to Searching... In Running, a press sends pauseSolve. One button, two jobs, and the Mode says which.

Logic ComponentSolve Timercontinued
On Solve Pressed
WhensolvePressedCustom Event
ModePaused (Starting Mode)
1Start Recurring Timer
Timer Name
solveTick
Repeat Every (s)
@self.solveSeconds
2Switch Mode
Event
On Solve Pressed
Mode
Running
3Change Variable
Variable
$solveButtonWords
Value
data('Messages', 'pauseButton', 'words')
4Change Variable
Variable
$statusLine
Value
data('Messages', $statusRow, 'words')
On Solve Pressed
WhensolvePressedCustom Event
ModeRunning
1Trigger Custom Event on Object
Custom Event
pauseSolve

This is the promise kept. On Step Pressed hears the Step button and Space. It asks for one Step, as stepDue, and then sends pauseSolve. So Step gives you one Step, and a running Solve stops at once.

Logic ComponentSolve Timercontinued
On Step Pressed
WhenstepPressedCustom Event
1Trigger Custom Event on Object
Custom Event
stepDue
2Trigger Custom Event on Object
Custom Event
pauseSolve

Now comes the one hard idea of the chapter. Take it slowly.

Every 0.05 seconds, the timer rings. Each ring, a Repeat asks for a Step 8 times, each one a stepDue. A Step that’s due isn’t taken yet. On Step Due decides.

While no Tile slides, its Mode is No Tile Sliding, and each due Step becomes takeStep. Run Search takes it.

Then a Move is made, and Hand Moves, on Puzzle Rules, sends boardChanged. Solve Timer hears it too, and switches On Step Due to Tile Sliding, which has no Actions. Every Step due from then on is let go. Nothing is saved up for later.

Meanwhile, the Tile glides across into the Blank at its even speed. When it lands, it sends slideFinished, the message every Tile has sent since Chapter 1. On Step Due switches back. The next ring’s Steps are taken again.

So during the search, all 8 Steps of a ring go through, and the count leaps. During the Route, a Step makes a Move, and On Step Due switches to Tile Sliding. The Steps due while that Tile slides are let go. One Tile slides at a time.

Logic ComponentSolve Timercontinued
On the Solve Tick Timer
WhenRecurring TimerTimer name: solveTick
1Repeat@self.stepsPerTicktimes
Each time
2Trigger Custom Event on Object
Custom Event
stepDue
On Step Due
WhenstepDueCustom Event
ModeNo Tile Sliding (Starting Mode)
1Trigger Custom Event on Object
Custom Event
takeStep
On Step Due
WhenstepDueCustom Event
ModeTile Sliding
(no Actions)
On Board Changed
WhenboardChangedCustom Event
1Switch Mode
Event
On Step Due
Mode
Tile Sliding
On Slide Finished
WhenslideFinishedCustom Event
1Switch Mode
Event
On Step Due
Mode
No Tile Sliding

Everything that pauses Solve sends pauseSolve, and On Pause Solve is where it lands. It undoes what Solve started, and last it shows the row’s pausedWords. That’s why a paused search says Paused.

Nothing else changes. The notebook, the Route and the count stay as they were, so the next Step goes on from there.

Logic ComponentSolve Timercontinued
On Pause Solve
WhenpauseSolveCustom Event
1Stop Recurring Timer
Timer Name
solveTick
2Switch Mode
Event
On Solve Pressed
Mode
Paused
3Change Variable
Variable
$solveButtonWords
Value
data('Messages', 'solveButton', 'words')
4Change Variable
Variable
$statusLine
Value
data('Messages', $statusRow, 'pausedWords')

Now attach Solve Timer to Puzzle Rules. Its card shows it below Run Search, and the two numbers come with it, already at 0.05 and 8.

ObjectPuzzle RulesAddDelete
Data Structure
From LogicRun Search
Data StructureRun Search Data
Logic
Start Board
Hand Moves
Check Goal
Tile Keys
Run Search
Solve Timer
Shared Variables
puzzleRulesPuzzle Rules

Run the game and press Step. The count reads 1, and the card says Paused. Step works again, and this time it goes through Solve Timer.

4.7 The Gold Button

One more Puzzle Button goes in the navy bar, to the right of Step.

Place it at 500 and -737, from PLACE THESE. Its message is solvePressed. It goes in the Message box under Send When Pressed, in the placement’s Logic, with no quote marks.

Its words and its color differ from the Object’s own. So both go in through Edit Object Locally, near the top of the placement’s Properties. In its Text / UI, the Text is {$solveButtonWords}, braces and all, so the button shows that Global. In its Sprite, the Tint is #BC9152, a warm gold. Then press Back to Scene. A placement’s own values shows each step.

Run the game. A gold button reading Solve sits beside Step. Press it.

The count leaps, and the Tiles slide home.

Play it!

Here is what to watch for.

  • Press Step. The count reads 1, and the card says Paused. Press it again: 2, and still Paused. Not a Tile moves.
  • Press Solve. The button reads Pause, and in a blink the count reaches 20 and stops there. The words go by too fast to read, and then the card says Solving...
  • Watch the Tiles. They slide in Chapter 3’s order, 1, 8, 7, 5, 4, 1, 5, 4, 1, 2, 3 and 6, one at a time, each at the same even speed as a click.
  • Press Pause, or Escape, partway through. The Tile that’s moving lands in its place, and after that nothing moves. The card says Paused.
  • Wait as long as you like, and press Solve again. The Tiles go on from where they stopped, and the count stays at 20.
  • On the last slide, the card says Solved. When that Tile lands, the button reads Solve again, by itself.
  • Press Solve once more. Nothing moves, and the card still says Solved.
  • Pause partway through the Route again, and click a Tile beside the Blank. It slides, the count goes back to 0, and the words go. Press Solve, and a new search starts from the Board you made, all the way to Solved.
How it works

Solve Timer decides when. Run Search decides what. Each ring of the timer asks for up to 8 Steps, and On Step Due lets a Step through only while no Tile slides. Step and Space ask for one Step, then pause. Show Status Row keeps the situation in $statusRow, so a pause can show its paused words, and Solve can bring the running words back.

Underneath all of it is one idea. A* Search doesn’t need to run in one go. Each Step leaves the notebook whole: every line written, every parent kept. So the search can stop between any two Steps, for a blink or for an hour, and the next Step goes on as if it never stopped. Solve doesn’t search a different way. It only takes the Steps faster than a hand can.

4.8 If It Didn’t Work

This chapter moves where a Step comes from, so a slip can make Step do too little or too much. That’s normal. Press Step once and watch the count.

Step does nothing at all? Perhaps Solve Timer isn’t on Puzzle Rules yet, or On Step Due’s Starting Mode isn’t No Tile Sliding. Look at its one Action too, which sends takeStep, spelled the same as Run Search’s Event.

One press of Step adds 2 to the count? Perhaps Run Search still has an On Step Pressed Event beside On Take Step, so both answer. Change Event turns the old Event into the new one, and keeps its Actions. Delete the extra Event.

Solve does nothing, and the button never says Pause? Look at the gold button’s placement first, where message should be solvePressed, and then at On Solve Pressed, which should start in Paused.

Solve searches, but no Tile ever slides? On Step Due may have switched to Tile Sliding and never come back. Look at On Slide Finished, which switches On Step Due to No Tile Sliding.

The button shows no word, or the wrong one? Its Text should be {$solveButtonWords}, braces and all, and When the Scene Starts sets that Global to the solveButton row’s words.

The card says Paused while Solve runs? Look at On Solve Pressed’s step 4, which reads ‘words’, and On Pause Solve’s step 4, which reads ‘pausedWords’. The two look alike, so they swap without a sound.

The words are gone where they should be? Show Status Row’s row has to match a row of Messages, letter for letter, such as searching or tryNew.

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 its finished project, if you’d like to compare.

Challenge

Slow the finger down, and watch the search work one Board at a time. Select Puzzle Rules, and look under Variables in its Properties. Set stepsPerTick to 1 and solveSeconds to 0.5. Run the game and press Solve. Now the count climbs two Boards a second, slow enough to read the words as they change.

Try it the other way, too, and see how fast you can make it. Set them back to 0.05 and 8 when you’re done.

Your solver runs on its own now, and it waits when you ask it to. In Chapter 5, it gets new puzzles, and a second way to guess.