TutorialsDijkstra’s Algorithm

4  Finish a Route

Goal

By the end of this chapter, the search finishes what it started. The tower settles. Cost shows what the cheapest route costs, and a yellow line runs home from the tower to the camp, one cell at a time. Last, the compass walks it.

The game as it is at the end of this chapter. Click the game to play.

Find route comes in Chapter 5, so play this one with Step, or Space.

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.

Weighted Maze, Chapter 4 finishedMyth project · 1.9 MB

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

Starting with this chapter? It begins where Chapter 3 ends, and Chapter 3’s Goal has that chapter’s finished project.

4.1 The Idea

In Chapter 3 the search stopped at the tower. Press Step in the game above, again and again, and after 78 Steps the tower settles at cost 24. Back then, that was the end. Step did nothing more, and the compass waited at the camp.

Now the tower is a beginning. Cost, which has shown two dashes since Chapter 1, says 24, and the words “Building route…” appear on the Status line. Press Step again. The tower’s ring turns yellow. Again, and the road beside it turns yellow too, with a yellow line between the two. Each Step adds one more cell, back toward the camp.

Here’s the question this chapter answers. The search knows the cheapest route costs 24, but which cells does that route pass through?

Nothing in Chapter 3 wrote a route down. Each cell kept one thing: its parent, the cell its cheapest way in came through. That turns out to be enough.

Think of each parent as a signpost, pointing back the way the cheapest route came. Stand on the tower and follow the signposts, and you walk the route home to the camp, backward. That’s tracing the route.

As each cell is traced, it gets a number, its routeOrder. The tower’s is 0. The cell before it gets 1, the one before that 2, and so on back to the camp. Every other cell keeps -1. So the route is written down backward, and the compass walks it forward, counting down.

Once the camp is traced, “Follow the route.” appears on the Status line. From then on, each Step slides the compass one cell along the yellow line, in a fifth of a second. When it stands on the tower, “Cheapest route.” ends the trial.

On the supplied map, that route is 24 moves. Each move costs 1, because the route keeps to the road the whole way. Straight across the top and down the right side is only 20 moves. It costs 53. The cheapest route isn’t the one with the fewest moves.

It takes 78 Steps to reach the tower, 25 to trace the route home, and 24 more to walk it. For now, your thumb is the clock. In Chapter 5, a button does the pressing for you, a Step every 0.045 seconds.

4.2 The Plan

Here’s how the pieces work together. Read each line from left to right. Most of the names are new, so skim them now, and come back when the compass walks.

  • The tower settles -> Settle Cheapest Cell sends cellSettled -> Show Settled Cell writes “Building route…” on the Status line, and Step Trial switches to Tracing.
  • A Step of the search or the trace ends -> searchChanged -> Show Route Cost shows the tower’s distance, and the rings and the line show the route so far.
  • Each Step of Tracing -> Step Trial gives one cell its routeOrder, checks its link with Check Route Link, and moves on to its parent.
  • The camp is marked -> Step Trial writes “Follow the route.” and switches to Replaying.
  • Each Step of Replaying -> Step Trial moves $markerCell on and sends markerMoved -> Slide Marker slides the compass -> markerLanded -> Step Trial is ready for the next Step.
  • The compass reaches the tower -> Step Trial writes “Cheapest route.” and switches to Finished.
  • The Scene starts -> Lay Out Route Lines makes a piece of line between every two neighbors -> placedOnMap -> Show Route Line stands each piece in its place.

4.3 Set Up

This chapter’s Set up card has two blocks.

GLOBAL VARIABLES adds $markerCell, the cell the compass stands on, or is sliding to. Make it with New Global… on Reset Trial’s Globals card, as before. It needs only its name. A new Global is a Number at 0 already.

REMOVED lists Show Marker, and this time it means it. Show Marker stood the compass on the camp, once, as the game began. In this chapter the compass learns to walk, and in the last part Slide Marker takes over its job.

Take Show Marker off the Marker first. Open the Marker, switch to Logic, and press the red trash button on its row in Attached Logic. After that, delete Show Marker from the Resources. The Set up card on the Making page says why the order matters. Taking Show Marker away doesn’t move the compass, because its placement already stands it on the camp’s corner.

Set upWhat this chapter adds
GLOBAL VARIABLES
$markerCellNumberstarts at 0
REMOVED
Logic Component: Show Marker

4.4 The Tower Settles

First, the moment the search ends gets words of its own, on the Status line and beside Cost.

When the search settles the tower, its work is done. The tower’s distance is final, so it’s the cost of the cheapest route, and nothing found later could beat it. The search stops right there. Every cell settled before the tower offered its neighbors a route through itself, as you saw in Chapter 3. The tower makes no offers.

Messages gets five new rows, the words for the rest of the trial: buildingRoute, followRoute, invalidRoute, noCost and cheapestRoute. The noCost words are the two dashes Cost shows until there’s a route. Add each row with Add Row. Double-click a cell to type in it, as before.

Data TableMessagesData Structure: Messages EntryAddDelete
Row 0ready
wordsPaint terrain.
Row 1cellSettled
words: cost
Row 2noRoute
wordsNo route.
Row 3wrongMapSize
wordsFix columns and rows.
Row 4wrongSettings
wordsCheck search settings.
Row 5wrongEndpoints
wordsCheck start and goal.
Row 6wrongCost
wordsUse costs 0 to maxCost.
Row 7blockedEndpoint
wordsUnblock S and G.
Row 8buildingRoute
wordsBuilding route...
Row 9followRoute
wordsFollow the route.
Row 10invalidRoute
wordsInvalid route.
Row 11noCost
words--
Row 12cheapestRoute
wordsCheapest route.

4.4.1 Show Settled Cell

In Chapter 3, Show Settled Cell named every cell it heard about. Now it makes one exception. When the cell is the tower, the words “Building route…” go on the Status line instead, because the search is over and the route comes next.

On the card, that’s an If around everything. Its True line writes the new words. Its Else holds your three old steps: the cell’s name, the cellSettled words, and last the distance.

Your three old steps now belong inside the If’s Else, and the If comes first. So the quickest way is to start this line again. Take the three old steps out with Delete, and add the card’s six in order. First the If, then its one Action with Add inside Then. Next comes Add Else. Its three Actions go in with Add inside Else, one at a time. If, Else and Repeat on the Making page shows each move.

Logic ComponentShow Settled CellAttach to: StatusAddDelete

The Status line while the search settles cells: each time Settle Cheapest Cell settles a cell (cellSettled), Status names the cell by its Maze Cells row and gives its distance, as in "Cell 52: cost 8". When the cell settled is the Tower, Status says the Route is being built instead (the Messages buildingRoute words).

Attach toAny Object
On Cell Settled
WhencellSettledCustom Event
1Change Variable
Variable
$status
Value
dataRow('Maze Cells', $currentCell)
2Change Variable
Variable
$status
Operator
+=
Value
data('Messages', 'cellSettled', 'words')
3Change Variable
Variable
$status
Operator
+=
Value
data('Maze Cells', $currentCell, 'distance')
1If$currentCell == data('Maze Settings', 'rules', 'goalCell')
Then
2Change Variable
Variable
$status
Value
data('Messages', 'buildingRoute', 'words')
3Otherwise
4Change Variable
Variable
$status
Value
dataRow('Maze Cells', $currentCell)
5Change Variable
Variable
$status
Operator
+=
Value
data('Messages', 'cellSettled', 'words')
6Change Variable
Variable
$status
Operator
+=
Value
data('Maze Cells', $currentCell, 'distance')

4.4.2 Show Route Cost

Cost has shown two dashes since Chapter 1. Show Route Cost, on Route Cost, finally gives it something to say.

It listens for searchChanged, the message Step Trial sends after each Step of the search and the trace. Clear Trial sends it too, whenever it clears a trial. Each time, Show Route Cost asks one question. Is the tower settled? If it is, Cost shows the tower’s distance, the cost of the cheapest route. If not, it shows the noCost words.

It never keeps a copy of the cost. Every time, it reads the tower’s row, so a Reset brings the dashes back by itself. A Declare Variable, tower, holds the tower’s cell number from Maze Settings.

Its two Change Component Values write self.text.text, as Show Terrain Choice’s did in Chapter 1. So its Components card needs the Text / UI, self.text. It turns up there by itself once the first one is in.

Logic ComponentShow Route CostAttach to: Route Cost

The Cost readout: whenever the search changes, it shows the cost of the cheapest Route, which is the Tower's distance once the Tower is settled, or the Messages noCost words ("--") while it is not.

Attach toAny Object
COMPONENTS
self.textText / UI
On Search Changed
WhensearchChangedCustom Event
1Declare Variable
Name
tower
Value
0
2Change Variable
Variable
tower
Value
data('Maze Settings', 'rules', 'goalCell')
3Ifdata('Maze Cells', tower, 'settled') == 1
Then
4Change Component Value
Component Value
self.text.text
Value
data('Maze Cells', tower, 'distance')
5Otherwise
6Change Component Value
Component Value
self.text.text
Value
data('Messages', 'noCost', 'words')

Attach it to Route Cost.

ObjectRoute CostAddDelete
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Text / UI
Text--
Font Size21
EmphasisSemibold
Color#203F43
AlignmentCenter
RenderingVector
Logic
Show Route Cost

Run the game, and press Step until the tower settles. It takes 78 Steps on the supplied map, and Space does the same, one Step for each press. Holding it down still makes only one. Cost says 24, and the words “Building route…” are on the Status line. Press Step again, and nothing happens. For now, Step Trial still ends the trial at the tower.

4.5 Home Along the Signposts

Now Step Trial learns what comes after the tower.

First, the route needs somewhere to be written down, so Maze Cells gets one more column, routeOrder. The tower’s will be 0, and every cell before it one more, back to the camp. A cell that isn’t on the route has -1.

Add it to Maze Cells Entry with Add Field, Value -1, as in Chapter 3. Your 120 rows get 0 again, not -1. This time a 0 means something: on the route, in the tower’s place. So give Clear Trial its two new steps now, before anything reads the column.

The card’s IMPORT CSV block is the one from Chapter 1, and there’s no need to import the file again. It holds only the costs. Your table has those already.

Clear Trial’s step 5 writes -1 on every row whenever a trial is cleared, and the game clears one as it starts. It goes at the end of the Repeat’s panel. Right-click the Repeat, choose Add inside Repeat, and join the block with Connect to sequence.

Its step 9 puts $markerCell back on the camp, for the compass. It goes between two steps you have, so join it with the round handles. Actions, one at a time shows how.

Data StructureMaze Cells EntryAddDelete

One row of the "Maze Cells" Data Table.

costNumberdefault 1
distanceNumberdefault 1000000
parentNumberdefault -1
settledNumberdefault 0
routeOrderNumberdefault -1
Data TableMaze CellsData Structure: Maze Cells EntryAddDelete
IMPORT CSV120 rows, from a file
FIRST ROWS3 of 120, to compare
Row 0Cell 00
cost1
distance1000000
parent-1
settled0
routeOrder-1
Row 1Cell 01
cost1
distance1000000
parent-1
settled0
routeOrder-1
Row 2Cell 02
cost1
distance1000000
parent-1
settled0
routeOrder-1
FunctionClear TrialAddDelete

Puts the trial back to Ready, keeping the terrain: every cell of Maze Cells gets distance unreached (Maze Settings), no parent (-1) and settled 0 and no place on the Route (routeOrder -1), Visited goes back to 0, no cell is current (currentCell -1), the Marker's cell is the Camp (markerCell), Status asks the learner to paint terrain (the Messages ready words), and searchChanged clears every Search Outline. Last, trialCleared tells Step Trial on rules (Maze Rules) that the trial was cleared, so the next Step starts from Ready.

PARAMETERS
NameTypeStarts at
rulesObjectMaze Rules
ACTIONS
1RepeatdataCount('Maze Cells')times
Each time
2Set Data Cell
Table
"Maze Cells"
Row
dataRow('Maze Cells', local.index)
Column
"distance"
Value
data('Maze Settings', 'rules', 'unreached')
3Set Data Cell
Table
"Maze Cells"
Row
dataRow('Maze Cells', local.index)
Column
"parent"
Value
-1
4Set Data Cell
Table
"Maze Cells"
Row
dataRow('Maze Cells', local.index)
Column
"settled"
Value
0
5Set Data Cell
Table
"Maze Cells"
Row
dataRow('Maze Cells', local.index)
Column
"routeOrder"
Value
-1
6Change Variable
Variable
$visited
Value
0
7Change Variable
Variable
$currentCell
Value
-1
8Change Variable
Variable
$status
Value
data('Messages', 'ready', 'words')
9Change Variable
Variable
$markerCell
Value
data('Maze Settings', 'rules', 'startCell')
10Trigger Custom Event
Custom Event
searchChanged
11Trigger Custom Event on Object
Applies To
rules
Custom Event
trialCleared

4.5.2 Step Trial

Step Trial’s On Step Requested grows from three Modes to six. Ready and Searching keep the work they did in Chapter 3. When the tower settles, each now switches to Tracing instead of Finished, and Ready gets two new steps to set up the trace. Tracing, Replaying and Waiting For Marker are new, and a third Event arrives, for the message markerLanded.

Its card is the longest in the chapter. So it comes in three parts, each right after the words it needs. Start with what goes in before any Action.

First add the three new Modes with New Mode…, so Switch Mode can pick them by name. The card lists the six in the order a trial meets them. Finished comes last. If yours don’t stand that way, Move Left and Move Right put them there. Ready stays the Starting Mode either way.

The trace takes many Steps, and it has to remember two things from one Step to the next: the cell it marks next, and how many cells it has marked. A Declare Variable is gone as soon as its Event ends. So Step Trial keeps those two in a Data Structure of its own, Step Trial Data. Press + on its Data Structures card and choose New Data Structure. Myth makes Step Trial Data and opens it, ready for the two fields on its card: traceCell and routeLength. Step Trial reads them as self.stepTrialData.traceCell and self.stepTrialData.routeLength, and changes them with Change Component Value. Maze Rules gets it From Logic, as its card shows.

Data StructureStep Trial Data

What Step Trial keeps between Steps while it traces the Route: traceCell, the cell the next Tracing Step marks, and routeLength, how many cells of the Route are marked so far.

traceCellNumberdefault -1
routeLengthNumberdefault 0
ObjectMaze RulesAddDelete
Data Structure
From LogicStep Trial
Data StructureStep Trial Data
Logic
Change Terrain
Restore Defaults
Step Trial
Reset Trial

The card’s first part holds Ready and Searching. Ready’s steps 4 and 5 are new. They get the trace ready before the search begins, with traceCell on the tower and routeLength at 0. They go inside step 2’s True line, between two steps you have. So add each with step 2’s Add inside Then, which puts it on that line, and join it there with the round handles. Actions, one at a time shows how. In Ready and in Searching, one Switch Mode changes from Finished to Tracing. Pick Tracing in that block’s Mode list.

Logic ComponentStep TrialAttach to: Maze RulesAddDelete

Makes one Step of the trial each time stepRequested arrives (the Step button or the Step Command). What a Step does depends on the trial's phase, the Mode of stepRequested: Ready begins the search and settles the Camp, Searching settles the next cheapest cell, and Finished does nothing. Before the first settle, Ready checks the map and Maze Settings (Check Maze Input); maxCostLimit and unreachedLimit are the largest maxCost and unreached that check allows. Once the Tower is settled, Tracing marks one cell of the Route per Step, from the Tower back to the Camp, checking each link (Check Route Link); traceCell is the cell the next Step marks and routeLength how many cells are marked. Then Replaying moves the Marker one cell of the Route per Step, toward the Tower: it commits the next cell (markerCell) and waits (Waiting For Marker) until Slide Marker says it has landed (markerLanded). On the Tower the Route is complete. markerOrder is the routeOrder of the Marker's cell. A cleared trial (trialCleared, from Clear Trial) starts again from Ready.

Attach toAny Object
DATA STRUCTURES
Step Trial Dataself.stepTrialData
SHARED VARIABLES
NameTypeKindStarting Value
maxCostLimitNumberConstant99
unreachedLimitNumberConstant1000000000
On Step Requested
WhenstepRequestedCustom Event
ModeReady (Starting Mode)
1Check Maze InputFunction
Action Name
inputCheck
maxCostLimit
@self.maxCostLimit
unreachedLimit
@self.unreachedLimit
2IfinputCheck.value == ""
Then
3Set Data Cell
Table
"Maze Cells"
Row
dataRow('Maze Cells', data('Maze Settings', 'rules', 'startCell'))
Column
distance
Value
0
4Change Component Value
Component Value
self.stepTrialData.traceCell
Value
data('Maze Settings', 'rules', 'goalCell')
5Change Component Value
Component Value
self.stepTrialData.routeLength
Value
0
6Settle Cheapest CellFunction
Action Name
firstSettle
7IffirstSettle.value == data('Maze Settings', 'rules', 'goalCell')
Then
8Switch Mode
Event
On Step Requested
Mode
Tracing
Mode
Finished
9Otherwise
10Relax NeighborsFunction
cell
firstSettle.value
11Switch Mode
Event
On Step Requested
Mode
Searching
12Trigger Custom Event
Custom Event
searchChanged
13Otherwise
14Change Variable
Variable
$status
Value
data('Messages', inputCheck.value, 'words')
15Switch Mode
Event
On Step Requested
Mode
Finished
On Step Requested
WhenstepRequestedCustom Event
ModeSearching
1Settle Cheapest CellFunction
Action Name
settle
2Ifsettle.value >= 0
Then
3Ifsettle.value == data('Maze Settings', 'rules', 'goalCell')
Then
4Switch Mode
Event
On Step Requested
Mode
Tracing
Mode
Finished
5Otherwise
6Relax NeighborsFunction
cell
settle.value
7Otherwise
8Change Variable
Variable
$status
Value
data('Messages', 'noRoute', 'words')
9Switch Mode
Event
On Step Requested
Mode
Finished
10Trigger Custom Event
Custom Event
searchChanged

Here is the chapter’s one hard idea, so take it slowly. It’s Tracing, the card’s second part.

Tracing makes one mark per Step. Step Trial Data’s traceCell is the cell this Step marks, and Ready put it on the tower. Its other field, routeLength, counts the cells marked so far. It starts at 0.

Count the first few. On the first Tracing Step, traceCell is the tower. Set Data Cell writes routeLength, still 0, into the tower’s routeOrder, and routeLength becomes 1. The tower isn’t the camp, so Check Route Link looks at the tower’s link to its parent.

The link passes. So traceCell becomes the parent. On the next Step, that cell gets routeOrder 1, its own link is checked, and traceCell moves on again. One cell per Step, the trace walks home along the signposts.

At the camp, the trace is done. Step Trial writes the followRoute words on the Status line and switches to Replaying, ready to walk. A link that fails its check ends the trial instead.

One more guard sits in Tracing’s second If, step 8. The test after its && asks that routeLength stay below the number of rows in Maze Cells. A route can’t pass through more cells than the map has. So the trace always ends.

Step 2 adds 1 to routeLength with the Operator +=. On a Change Component Value, the Operator list shows += as Plus. Pick Plus.

Tracing’s Else holds the call to Check Route Link and that second If, and the If has an Else of its own. With one If inside another, right-click the If or the Else whose line the Action belongs on. Step 13 is on Tracing’s own line, after the first If, so right-click the Tracing block and choose Add unattached Action here.

Logic ComponentStep TrialcontinuedAddDelete
On Step Requested
WhenstepRequestedCustom Event
ModeTracing
1Set Data Cell
Table
"Maze Cells"
Row
dataRow('Maze Cells', self.stepTrialData.traceCell)
Column
routeOrder
Value
self.stepTrialData.routeLength
2Change Component Value
Component Value
self.stepTrialData.routeLength
Operator
+=
Value
1
3Ifself.stepTrialData.traceCell == data('Maze Settings', 'rules', 'startCell')
Then
4Change Variable
Variable
$status
Value
data('Messages', 'followRoute', 'words')
5Switch Mode
Event
On Step Requested
Mode
Replaying
6Otherwise
7Check Route LinkFunction
Action Name
link
cell
self.stepTrialData.traceCell
8Iflink.value == 1 && self.stepTrialData.routeLength < dataCount('Maze Cells')
Then
9Change Component Value
Component Value
self.stepTrialData.traceCell
Value
data('Maze Cells', self.stepTrialData.traceCell, 'parent')
10Otherwise
11Change Variable
Variable
$status
Value
data('Messages', 'invalidRoute', 'words')
12Switch Mode
Event
On Step Requested
Mode
Finished
13Trigger Custom Event
Custom Event
searchChanged

The last part is the walk. Replaying moves the compass one cell per Step, toward the tower. The compass stands on $markerCell. Clear Trial put that on the camp. The route was numbered backward, so walking it forward is counting down: the next cell along has a routeOrder one less than the compass’s own. That number is markerOrder, a Declare Variable, since Replaying needs it only while it runs.

To find it, Replaying uses dataFind. It looks down the routeOrder column for that number and hands back the cell. That’s how Cell Picture found a row of Terrains, back in Chapter 1.

The move is decided first, then shown. Replaying writes the next cell into $markerCell, switches to Waiting For Marker, and sends markerMoved. While the compass slides, a Step lands on Waiting For Marker, which has no Actions. So that Step does nothing.

When the compass lands, it sends markerLanded, and Step Trial’s new Event hears it. On the tower, the walk is over. That Event writes “Cheapest route.” on the Status line and switches On Step Requested to Finished, so a Step does nothing more. Anywhere else, it switches back to Replaying, ready for the next Step.

Replaying’s own Else does the same ending. It matters only on a map whose camp and tower are one cell, and only a change to Maze Settings makes one. Build it as the card shows.

Trigger Custom Event takes any name you type, so markerMoved needs nothing more. But markerLanded needs Add Custom Event…, since nothing sends it yet. Its one Mode comes named Step Trial. That can stay.

Logic ComponentStep TrialcontinuedAddDelete
On Step Requested
WhenstepRequestedCustom Event
ModeReplaying
1Declare Variable
Name
markerOrder
Value
-1
2Change Variable
Variable
markerOrder
Value
data('Maze Cells', $markerCell, 'routeOrder')
3IfmarkerOrder > 0
Then
4Change Variable
Variable
$markerCell
Value
dataFind('Maze Cells', 'routeOrder', markerOrder - 1)
5Switch Mode
Event
On Step Requested
Mode
Waiting For Marker
6Trigger Custom Event
Custom Event
markerMoved
7Otherwise
8Change Variable
Variable
$status
Value
data('Messages', 'cheapestRoute', 'words')
9Switch Mode
Event
On Step Requested
Mode
Finished
On Step Requested
WhenstepRequestedCustom Event
ModeWaiting For Marker
(no Actions)
On Step Requested
WhenstepRequestedCustom Event
ModeFinished
(no Actions)
On Marker Landed
WhenmarkerLandedCustom Event
1If$markerCell == data('Maze Settings', 'rules', 'goalCell')
Then
2Change Variable
Variable
$status
Value
data('Messages', 'cheapestRoute', 'words')
3Switch Mode
Event
On Step Requested
Mode
Finished
4Otherwise
5Switch Mode
Event
On Step Requested
Mode
Replaying
On Trial Cleared
WhentrialClearedCustom Event
1Reset Modes
Event
On Step Requested

Run the game now, before anything shows the route. Press Step until the tower settles, and then 25 more times, watching the map. It doesn’t change. The tower’s ring stays white. But after the 25th, “Follow the route.” is on the Status line, because the trace has walked home. The next part makes it show.

4.5.3 Color Search Outline

Now the route shows on the map. Color Search Outline gets a fourth question, asked last, so it wins. Is the cell on the route, with a routeOrder of 0 or more? If it is, the ring takes routeTint, yellow. Every route cell is settled, so the ring is already thick.

Add routeTint to its PARAMETERS, a Color, with the card’s code as its Default. The new If goes at the end of the line, after the If for the current cell. It’s the same shape.

FunctionColor Search OutlineAddDelete

Shows one cell's place in the search on its Search Outline: hidden while the search has not reached the cell, a thin ring in frontierTint once it is reached, a thick ring in settledTint once it is settled, and currentTint while it is the current cell (the one the last Step settled), and routeTint once it is on the Route (its routeOrder is 0 or more). A later rule wins: Route over current over settled over frontier. The thin and the thick ring are frames 0 and 1 of the Cell Outline picture, so a settled cell shows by its shape as well as by its color.

PARAMETERS
NameTypeStarts at
outlineObjectStructural
Required Components
Slot Namesprite
Component TypeSprite
cellNumber0
frontierTintColor#32e9ed
settledTintColor#64afff
currentTintColor#ffffff
routeTintColor#ffe043
ACTIONS
1Set Visible
Applies To
outline
Visible
false
2Ifdata('Maze Cells', cell, 'distance') < data('Maze Settings', 'rules', 'unreached')
Then
3Set Visible
Applies To
outline
Visible
true
4Set Frame
Applies To
outline
Frame
0
5Change Sprite Tint Property
Applies To
outline
Value
frontierTint
6Ifdata('Maze Cells', cell, 'settled') == 1
Then
7Set Visible
Applies To
outline
Visible
true
8Set Frame
Applies To
outline
Frame
1
9Change Sprite Tint Property
Applies To
outline
Value
settledTint
10Ifcell == $currentCell
Then
11Set Visible
Applies To
outline
Visible
true
12Change Sprite Tint Property
Applies To
outline
Value
currentTint
13Ifdata('Maze Cells', cell, 'routeOrder') >= 0
Then
14Set Visible
Applies To
outline
Visible
true
15Change Sprite Tint Property
Applies To
outline
Value
routeTint

Show Search Outline passes the new color along. Give it a fourth Shared Variable, routeTint, a Color that stays Constant, with the card’s code as its Starting Value. Each of its two calls has a routeTint box now, because the Function has one. Type @self.routeTint into both.

Logic ComponentShow Search OutlineAttach to: Search OutlineAddDelete

One cell's ring that shows where the search is: whenever the search changes, it is hidden, a thin cyan ring (frontier), a thick blue ring (settled) or a thick white ring (the current cell), with Color Search Outline. Its cell is its Map Place cell, which Lay Out Map gives it. frontierTint, settledTint, currentTint and routeTint are the four colors: a cell on the Route is yellow.

Attach toAny Object
DATA STRUCTURES
Map Placeself.mapPlace
COMPONENTS
self.spriteSprite
SHARED VARIABLES
NameTypeKindStarting Value
frontierTintColorConstant#32e9ed
settledTintColorConstant#64afff
currentTintColorConstant#ffffff
routeTintColorConstant#ffe043
On Placed on Map
WhenplacedOnMapCustom Event
1Cell Screen PositionFunction
Action Name
place
cell
self.mapPlace.cell
2Set Position
Position Relative To
Screen
X
place.x
Y
place.y
3Color Search OutlineFunction
outline
self
cell
self.mapPlace.cell
frontierTint
@self.frontierTint
settledTint
@self.settledTint
currentTint
@self.currentTint
routeTint
@self.routeTint
On Search Changed
WhensearchChangedCustom Event
1Color Search OutlineFunction
outline
self
cell
self.mapPlace.cell
frontierTint
@self.frontierTint
settledTint
@self.settledTint
currentTint
@self.currentTint
routeTint
@self.routeTint

Run the game. Press Step until the tower settles, and keep going. The tower’s ring turns yellow, then the road beside it, one cell per Step, back toward the camp. After 25 of those Steps, the camp turns yellow, and “Follow the route.” appears on the Status line.

Press Step once more, and nothing seems to happen. Step Trial has moved $markerCell on. It’s waiting for the compass to say it has landed, and the compass doesn’t know how to walk yet. That comes last.

4.6 The Yellow Line

Next comes the line that joins the yellow rings.

It isn’t one long Object. It’s made of short pieces, one between every two neighboring cells of the map. A piece shows only while its two cells follow each other on the route. Each is a yellow bar over a slightly bigger dark one, its backing, so the line stands out on any terrain.

Count them. Each of the ten rows has eleven gaps between its cells, and each of the twelve columns has nine. That’s 110 and 108, so 218 pieces, and 218 backings. The Scene places none of them.

Each piece keeps its two cells in a Data Structure, Route Link. Its fields are cell, and neighbor, the cell on its right or the cell below it. Both start at -1.

Data StructureRoute Link

The two neighboring cells a Route Line (or its Route Line Backing) joins: cell, and neighbor, the cell on its right or the cell below it. Lay Out Route Lines gives each line its two cells right after creating it.

cellNumberdefault -1
neighborNumberdefault -1

4.6.1 Draw Route Line

Draw Route Line decides whether one piece shows. It reads both cells’ routeOrder. The piece shows when both are on the route and one apart. Otherwise it hides.

One apart matters. Two cells can both be on a route, side by side on the map, without following each other on it. No line joins those. The card writes the rule with abs(), which gives the size of a difference, whichever way round. So abs(cellOrder - neighborOrder) is 1 for two cells in a row.

Its line is made the way you made Color Search Outline’s outline: an Object Parameter that stays Structural, with the Sprite as a Required Component.

FunctionDraw Route Line

Shows one Route Line (or its backing) between two neighboring cells, cell and neighbor, only while both are on the Route (routeOrder 0 or more) and one comes right after the other (their routeOrder values differ by 1); otherwise hides it. So a line never joins two Route cells that do not follow each other.

PARAMETERS
NameTypeStarts at
lineObjectStructural
Required Components
Slot Namesprite
Component TypeSprite
cellNumber0
neighborNumber0
ACTIONS
1Declare Variable
Name
cellOrder
Value
-1
2Declare Variable
Name
neighborOrder
Value
-1
3Change Variable
Variable
cellOrder
Value
data('Maze Cells', cell, 'routeOrder')
4Change Variable
Variable
neighborOrder
Value
data('Maze Cells', neighbor, 'routeOrder')
5IfcellOrder >= 0 && neighborOrder >= 0 && abs(cellOrder - neighborOrder) == 1
Then
6Set Visible
Applies To
line
Visible
true
7Otherwise
8Set Visible
Applies To
line
Visible
false

4.6.2 Show Route Line

Show Route Line is each piece’s own Logic. The line and its backing both use it, and only their color differs.

When placedOnMap arrives, the piece asks Cell Screen Position where its two cells stand. It goes halfway between them, and its Set Position’s Position Relative To says Screen, as Show Search Outline’s does. A piece is drawn lying across. When one cell stands above the other, the two share an x, and Set Rotation turns the piece a quarter turn, 90, to stand it upright.

After that, it takes its color, lineTint, and asks Draw Route Line whether to show. Each time searchChanged arrives, it asks again.

Its Data Structures card takes Route Link, under Existing Data Structures, and its Components card needs the Sprite. The color is a Shared Variable, a Color that stays Constant, with the yellow as its Starting Value. Each call’s line box starts at self, the piece itself, so leave it there.

Logic ComponentShow Route LineAttach to: Route Line, Route Line Backing

One piece of the Route Line, the yellow line that joins the Route's cells: whenever the search changes it is shown only while its two cells follow each other on the Route (Draw Route Line). The Route Line and the Route Line Backing both use it. Its two cells are its Route Link cell and neighbor, which Lay Out Route Lines gives it; it stands halfway between them, upright when one is above the other. lineTint is its color: yellow on the Route Line, and dark on the Route Line Backing, each Object's own Shared Variable.

Attach toAny Object
DATA STRUCTURES
Route Linkself.routeLink
COMPONENTS
self.spriteSprite
SHARED VARIABLES
NameTypeKindStarting Value
lineTintColorConstant#ffeb55
On Placed on Map
WhenplacedOnMapCustom Event
1Cell Screen PositionFunction
Action Name
from
cell
self.routeLink.cell
2Cell Screen PositionFunction
Action Name
to
cell
self.routeLink.neighbor
3Set Position
Position Relative To
Screen
X
(from.x + to.x) / 2
Y
(from.y + to.y) / 2
4Iffrom.x == to.x
Then
5Set Rotation
Degrees
90
6Change Sprite Tint Property
Value
@self.lineTint
7Draw Route LineFunction
line
self
cell
self.routeLink.cell
neighbor
self.routeLink.neighbor
On Search Changed
WhensearchChangedCustom Event
1Draw Route LineFunction
line
self
cell
self.routeLink.cell
neighbor
self.routeLink.neighbor

The Route Line is a Screen UI Object showing the Panel picture at 76 by 12. The Route Line Backing shows it at 82 by 18, and its Scene Depth Draw Order of 2 puts it under the line, which has 3. Remove each one’s UI Interaction, and attach Show Route Line to both. Each gets its Route Link From Logic.

The Panel picture is white, and each piece’s color comes from its Logic. The line keeps the yellow it starts with, but the backing needs a color of its own. That’s the last line of its card. Open the backing, and in its Properties, under Variables, find lineTint, marked From Logic: Show Route Line. Type #101d29 into the box beside its swatch.

ObjectRoute Line
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 76, Y 12
Sprite
Scene Depth Draw Order3
Z Index Within Object3
SpritePanel
Transform > SizeX 76, Y 12
Data Structure
From LogicShow Route Line
Data StructureRoute Link
Logic
Show Route Line
ObjectRoute Line Backing
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 82, Y 18
Sprite
Scene Depth Draw Order2
Z Index Within Object2
SpritePanel
Transform > SizeX 82, Y 18
Data Structure
From LogicShow Route Line
Data StructureRoute Link
Logic
Show Route Line
Shared Variables
lineTint#101d29

4.6.3 Lay Out Route Lines

Lay Out Route Lines makes every piece when the Scene starts. It works like Lay Out Map, but with one Repeat inside another.

The outer Repeat runs once for each cell. The inner one runs once for each row of Directions, and asks Find Grid Neighbor which cell lies that way. Two neighbors are joined once, not twice. So a piece is made only when the neighbor comes after the cell, on its right or below it. Past the edge of the map, Find Grid Neighbor answers -1, which never comes after anything.

Each time, it makes a backing and a line. Each gets its two cells, and the message placedOnMap goes to it alone.

Inside the inner Repeat, local.index counts directions, not cells, so the outer Repeat’s number is kept first, in a Declare Variable, thisCell.

On the card, steps 2 and 3 go in step 1’s panel, and step 3 is the inner Repeat. Steps 4 and 5 go in step 3’s own panel, and steps 6 to 13 on step 5’s True line. Each block’s right-click menu adds only to its own panel. So right-click the block whose panel the Action belongs in: Add inside Repeat on step 1, then on step 3, and Add inside Then on step 5.

Steps 7 and 11 write into the new piece, newBacking.cell and newLine.cell, as newCell.cell did in Lay Out Map. Their Value is thisCell. Steps 8 and 12 hand on next.value, the answer of the Find Grid Neighbor call named next.

It makes its pieces from two Shared Variables, routeLineBacking and routeLine. Both are Objects. Each stays Constant and starts at None, like mapCell and searchOutline.

Logic ComponentLay Out Route LinesAttach to: Map

Lays out the Route Line when the Scene starts: it joins every two neighboring cells with a Route Line over a Route Line Backing, once, from the cell that comes first in reading order to its neighbor on the right or below. Each line gets its two cells (its Route Link) and is told, with placedOnMap, that it is placed; it then stands between its cells and shows itself. routeLineBacking and routeLine are the Objects it makes.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
routeLineBackingObjectConstantNone
routeLineObjectConstantNone
When the Scene Starts
WhenScene Start
1RepeatdataCount('Maze Cells')times
Each time
2Declare Variable
Name
thisCell
Value
local.index
3RepeatdataCount('Directions')times
Each time
4Find Grid NeighborFunction
Action Name
next
cell
thisCell
direction
dataRow('Directions', local.index)
5Ifnext.value > thisCell
Then
6Create Instance
Object to Create
@self.routeLineBacking
X
0
Y
0
Created As
newBacking
7Change Variable
Variable
newBacking.cell
Value
thisCell
8Change Variable
Variable
newBacking.neighbor
Value
next.value
9Trigger Custom Event on Object
Applies To
newBacking
Custom Event
placedOnMap
10Create Instance
Object to Create
@self.routeLine
X
0
Y
0
Created As
newLine
11Change Variable
Variable
newLine.cell
Value
thisCell
12Change Variable
Variable
newLine.neighbor
Value
next.value
13Trigger Custom Event on Object
Applies To
newLine
Custom Event
placedOnMap

On the Map, attach Lay Out Route Lines beside Lay Out Map. Then set its two Shared Variables the way you set mapCell: under Variables, pick Route Line Backing for routeLineBacking, and Route Line for routeLine.

ObjectMapAddDelete
Logic
Lay Out Map
Lay Out Route Lines
Shared Variables
mapCellMap Cell
searchOutlineSearch Outline
routeLineBackingRoute Line Backing
routeLineRoute Line

Run the game, and step until the tower settles. Now watch the trace. Each Step colors one more ring yellow, and a yellow line on its dark backing joins it to the one before. By the time “Follow the route.” appears, the line runs unbroken from the tower to the camp.

4.7 The Compass Walks

Last, the compass walks the route. It has waited at the camp since Chapter 1.

Maze Layout gets one column, slideSeconds, how long each slide takes: 0.2 seconds, a quick glide. Add it to Maze Layout Entry with Add Field, Value 0.2. Add Field puts it last, after towerFrame. The card lists it after spacing, but the order doesn’t matter, as with rows in Chapter 2. Type 0.2 into the board row too, because a new field gives that row 0.

Data StructureMaze Layout EntryAddDelete

One row of the "Maze Layout" Data Table.

originXNumberdefault 56
originYNumberdefault 154
spacingNumberdefault 64
slideSecondsNumberdefault 0.2
markerOffsetNumberdefault 16
campFrameNumberdefault 4
towerFrameNumberdefault 5
Data TableMaze LayoutData Structure: Maze Layout EntryAddDelete
Row 0board
originX56
originY154
spacing64
slideSeconds0.2
markerOffset16
campFrame4
towerFrame5

4.7.1 Marker Screen Position

Marker Screen Position answers where the compass stands on a cell, starting from the cell’s center, which Cell Screen Position gives. From there it goes right and down by the markerOffset in Maze Layout, 16 pixels each way. Down is a minus on the card. On the screen, y goes below zero as you go down. That’s the sum Show Marker made in Chapter 1, moved into a Function, because two pieces need it now.

It hands back a Marker Screen Position Result, a Data Structure with x and y, as Cell Screen Position hands back its own. So make that Data Structure first, from its card, and then pick it in the Return Type list. Its Return has a box for each.

Data StructureMarker Screen Position Result

Where the Marker stands on a cell, as Marker Screen Position returns it: the cell's center (Cell Screen Position's x and y), moved Maze Layout markerOffset to the right and down.

xNumberdefault 0
yNumberdefault 0
FunctionMarker Screen Position

Where the Marker stands on one cell, in screen pixels: on the cell's lower-right corner, Maze Layout markerOffset pixels right of and below the cell's center (Cell Screen Position).

PARAMETERS
NameTypeStarts at
cellNumber0
RETURN TYPEMarker Screen Position Result (a Data Structure)
ACTIONS
1Declare Variable
Name
x
Value
0
2Declare Variable
Name
y
Value
0
3Cell Screen PositionFunction
Action Name
place
cell
cell
4Change Variable
Variable
x
Value
place.x + data('Maze Layout', 'board', 'markerOffset')
5Change Variable
Variable
y
Value
place.y - data('Maze Layout', 'board', 'markerOffset')
6Return
x
x
y
y

4.7.2 Stand Marker

Stand Marker puts the compass exactly on its cell, $markerCell, at once. First it stops any slide with Control Tween, so nothing pulls the compass away again. After that, it asks Marker Screen Position where to stand.

Its marker is an Object Parameter that names one Object, as Clear Trial’s rules names Maze Rules. So open its Settings, choose Nominal, and pick Marker. Control Tween shows in the Action List while Advanced Mode is on, as Tween does.

FunctionStand Marker

Stops the Marker and puts it exactly on the cell markerCell names (Marker Screen Position).

PARAMETERS
NameTypeStarts at
markerObjectMarker
ACTIONS
1Control Tween
Applies To
marker
2Marker Screen PositionFunction
Action Name
spot
cell
$markerCell
3Set Position
Applies To
marker
Position Relative To
Screen
X
spot.x
Y
spot.y

4.7.3 Slide Marker

Slide Marker, on the Marker, takes Show Marker’s place. It listens for three things.

When markerMoved arrives, the compass slides. Two Tween Actions run together, one across and one up and down, each over slideSeconds. So the compass glides in a straight line, at an even pace. At the same moment, a timer starts, markerSlide, set to ring once, when the slide is over.

Count the beats. The message arrives. The compass glides for a fifth of a second. The timer rings.

When markerSlide rings, Stand Marker sets the compass exactly on its cell, and Slide Marker sends markerLanded. Step Trial is waiting for that.

When searchChanged arrives, the timer is stopped, and the compass stands on $markerCell at once. After a Reset, that’s the camp. So a Reset in the middle of a slide sends the compass straight home, and no landing comes later. No Step of the walk sends searchChanged, so a Step never cuts a slide short. Only a cleared trial does that.

Type self.transform.position.x into the first Tween’s Value box, and self.transform.position.y into the second’s. Their Ease starts at Linear, the even pace, so leave it. Start Recurring Timer’s Ring Once is a list here too, set to Yes, and the timer’s name goes in bare, as the card has it.

The timer’s own Event is a Recurring Timer named markerSlide. Nothing has used that name yet, so choose Type a timer name…, as Events: the When bars shows. The message markerMoved is listed already. Step Trial sends it.

Slide Marker works on the Marker and nothing else, because Stand Marker’s marker takes only a Marker. So open its Logic Component card, and set Attach to Marker, as you set Reset Trial to Maze Rules. Then both Stand Marker calls’ marker box reads self · Marker. There’s nothing to change.

Logic ComponentSlide MarkerAttach to: Marker

Shows the Marker on the cell markerCell names. When Step Trial commits the Marker's next cell (markerMoved), the Marker slides there in Maze Layout slideSeconds and sends markerLanded when it is exactly on it. When the search changes or is cleared (searchChanged), the Marker stands on markerCell at once, ending any slide.

Attach toMarker
On Search Changed
WhensearchChangedCustom Event
1Stop Recurring Timer
Timer Name
markerSlide
2Stand MarkerFunction
marker
self
On Marker Moved
WhenmarkerMovedCustom Event
1Marker Screen PositionFunction
Action Name
spot
cell
$markerCell
2Tween
Value
self.transform.position.x
Target
spot.x
Duration
data('Maze Layout', 'board', 'slideSeconds')
3Tween
Value
self.transform.position.y
Target
spot.y
Duration
data('Maze Layout', 'board', 'slideSeconds')
4Start Recurring Timer
Timer Name
markerSlide
Repeat Every (s)
data('Maze Layout', 'board', 'slideSeconds')
Ring Once
Yes
On the Marker Slide Timer
WhenRecurring TimerTimer name: markerSlide
1Stand MarkerFunction
marker
self
2Trigger Custom Event
Custom Event
markerLanded

On the Marker, Slide Marker joins the Attached Logic, where Show Marker was.

ObjectMarkerAddDelete
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 24, Y 24
Sprite
Scene Depth Draw Order5
Z Index Within Object5
SpriteCompass
Transform > SizeX 24, Y 24
Logic
Slide Marker
Show Marker

Run the game, and play it through. Step until the tower settles, then until the route is traced, then once more. The compass glides from the camp onto the road beside it. It stops square on the cell’s corner. Each Step takes it one cell further along the yellow line, and two quick presses still move it one cell. On the tower, “Cheapest route.” ends the trial.

Play it!

Watch for each of these.

  • Before the first Step, Cost shows two dashes, and the compass sits on the camp’s lower-right corner.
  • Press Step 77 times. The Status line reads “Cell 115: cost 24”, and Cost still shows two dashes. Cell 115 tied with the tower at 24, and the lower number went first.
  • The 78th Step settles the tower. Visited says 78, Cost says 24, and “Building route…” is on the Status line.
  • The next Step turns the tower’s ring thick and yellow. Each Step after it turns one more cell yellow, back toward the camp, and a yellow line on a dark backing joins it to the cell before.
  • The 25th Step after the tower settled turns the camp yellow too. The line runs unbroken along the road, from the camp to the tower, and “Follow the route.” appears on the Status line.
  • Press Step. The compass glides onto the road beside the camp, in a fifth of a second.
  • Press Step twice, fast. The compass still moves one cell.
  • Keep stepping. 24 moves after the camp, the compass stands on the tower. The words “Cheapest route.” are on the Status line, and Steps change nothing now.
  • Count the yellow cells after the camp. There are 24, all road, so the route costs 24, as Cost says.
  • Press R. Every ring and every piece of line hides, Cost shows two dashes, and the compass is back on the camp. Press R in the middle of a slide, and it’s back at once.
  • Step into the trace, then paint a cell with a new terrain. The search clears, as if you had pressed Reset.
How it works

The search settles the tower, and from then on, its distance is the route’s cost, the number Cost shows. Each cell kept its parent. So Step Trial follows the parents home from the tower, one cell per Step, checking each link and numbering each cell. The compass walks the numbers back down, one slide per Step.

Underneath it is one idea. The search never had to remember a whole route. Each cell remembered one thing, where its cheapest way in came from, and that was enough. Follow those signposts from any settled cell, and you walk the cheapest route back to the camp.

That’s Dijkstra’s algorithm, from the first Step to the last. The search settles each cell at its cheapest cost, and the parents it leaves behind turn the tower’s cost into a route. You’ve built all of it.

4.8 If It Didn’t Work

Something may be off the first time, and that’s all right. Three things happen in turn in this chapter: the tower settles, the route is traced, and the compass walks. So first, see how far your game gets, and look for the slip in the stage where it stops.

Cost still shows two dashes after the tower settles? Look for Show Route Cost in Route Cost’s Attached Logic, and at its two Change Component Values, which write self.text.text.

The Status line names Cell 119, not “Building route…”? Show Settled Cell’s If isn’t asking about the tower, because its row should compare $currentCell with goalCell.

The tower settles, and the next Step does nothing? Perhaps Step Trial still switches to Finished there, so look at Searching’s step 4. Its Mode should say Tracing.

Every ring is yellow, before the first Step? The routeOrder column still holds Add Field’s 0s, and a 0 means the tower’s place on the route. Look at Clear Trial’s step 5, inside its Repeat. Is it standing apart from the rest, with no arrow running into it?

Does the trace stop at once, with “Invalid route.” on the Status line? A link failed its check. Perhaps Check Route Link’s Return hands back something other than isValid, or step 13’s Operator is = where it should be +=.

Only the tower’s ring turns yellow, and more Steps add nothing? The trace never leaves the tower. Look at Tracing’s step 9, on the second If’s True line. It moves traceCell on.

The Status line reaches “Follow the route.”, but no ring turns yellow? Look at Color Search Outline’s step 13, the If that asks for a routeOrder of 0 or more. Perhaps it stands apart, with no arrow into it. Or look at the routeTint box in each of Show Search Outline’s two calls. It may be empty.

The rings turn yellow, but no line joins them? Look on the Map for Lay Out Route Lines, with both of its Variables set. After that, look at Show Route Line’s Set Position. It should say Screen.

Every piece of line stands upright, even across the top of the map? Set Rotation belongs inside the If, on its True line.

The line looks white, or wide and all yellow? Each piece’s color comes from Show Route Line: the line keeps the yellow lineTint starts with, and the backing’s lineTint, under its Variables, should be #101d29.

The compass never leaves the camp? Look for Slide Marker on the Marker. And look at the name markerMoved in two places, Step Trial’s Replaying and Slide Marker’s When bar.

The compass stands on each cell’s upper-right corner, not its lower-right? Look at Marker Screen Position’s step 5. It takes markerOffset away from y, because down is a minus.

The compass wanders off the yellow line? Look at Replaying’s step 4. It should look for markerOrder - 1, the next cell along.

The compass jumps one cell and stops, and Step does nothing more? Perhaps Maze Layout’s board row still holds the 0 that Add Field gave slideSeconds. Type 0.2.

The compass slides once, but never again? Step Trial’s On Marker Landed should switch back to Replaying on its Else line. And Slide Marker’s timer Event should send markerLanded. That Event and Start Recurring Timer must name the same timer, markerSlide.

Each of these is one small fix. Make it, run the game again, and you’re back with the chapter. The catch-up download in the Goal is this chapter’s game, finished, to compare with yours.

Challenge

Now the route is yours to bend. Put mud in its way: paint one of the yellow road cells as Mud. Before you press Step, guess where the new route will go, and what it will cost. Run the search and see. Try a wall instead, and the route has to go round it, however costly the way round is. How far can you push the route before the only answer is “No route.” on the Status line?

That’s a lot of pressing. The finished game on the Welcome page runs the same search with Find route, if your thumb needs a rest. In Chapter 5, your game gets that button too.

The route is found, and the compass stands on the tower. In Chapter 5, it walks there again, with Find route pressing Step for you, and stopping whenever you ask.