TutorialsBinary Search

3  Scan Record

Goal

By the end of this chapter, you can watch the search happen. A strip of a hundred marks runs under the safe, teal for every code still possible. Each scan turns some dark. The code you tried glows gold. And along the bottom, a small card appears for every scan you make, with its guess and its clue.

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

Crack the Safe, Chapter 3 finishedMyth project · 1.5 MB

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

3.1 The Idea

Scan 50 in Chapter 2, and the safe tells you something true. But what it told you lives in two small numbers, POSSIBLE CODE: 51 - 100, and you have to hold the picture in your head. By the fourth scan, most people have lost track of what the first one said.

This chapter puts that picture on the screen. Under the safe, a hundred thin marks stand in a row, one for each code. Every mark starts teal, since every code is possible. Scan 50 and hear HIGHER. The marks below 50 go dark, and the 50 itself turns gold, the guess you tried last. Now the teal band runs from 51 to 100. You can see it.

Under the strip, a row of seven dark cards waits, hidden until it’s needed. The first scan turns up card 1. It says 50, and under it, HIGHER. The second scan turns up card 2, beside it, with its own guess and its own clue. By the end of a trial, the cards are the whole story of how you got there.

The strip is Chapter 2’s paper with a hundred squares, made real. A clue snips a piece off one end, and the snipped squares go dark.

The chapter’s new words are these. The Scan Log is a table the game writes while it plays, one row for each scan. A Code Mark is one mark on the strip. A Scan Card is one card along the bottom, with its guess and its clue on it.

One thing here is new to Myth, too. You won’t place a hundred marks by hand, or seven cards. One piece of Logic makes them all when the room opens, and it works out where each one goes.

This is where binary search becomes something you can watch. Scan in the middle of the teal band, and about half of it goes dark. Scan near one end of the band, and if the clue points away from that end, only a few marks go out.

3.2 The Plan

  • The room opens -> Lay Out Scan Record makes the hundred marks and the seven cards, and tells each one its code or its scan.
  • You scan -> Scan Guess counts it -> Record Scan adds a row to the Scan Log: Scan 1, then Scan 2.
  • The count $scans changes -> Show Code Mark colors each mark gold, teal or dark.
  • The count $scans changes -> Show Scan Card shows each card whose scan has happened, with its row of the Scan Log.
  • You press R -> Start New Safe empties the Scan Log, and the cards hide again.

3.3 Set Up

This Set up card is short. ART names the two pictures this chapter uses, Safe Flat for the marks and Safe History for the cards. The Starter Kit brought both in Chapter 1.

PLACE THESE has only two Objects, the labels under the two ends of the strip, and they come last. Everything else in this chapter is made while the game runs.

Set upWhat this chapter adds
ART (sprites, from the starter kit)
Safe Flat
Safe History
SCENE Safe Room: PLACE THESE
ObjectXY
Lowest Code Label59-649
Highest Code Label1019-649

3.4 The Record

First, the game learns to write down each scan. Nothing on the screen changes in this part. It’s quiet work, the record the cards will read later.

The record is a Data Table, the Scan Log, and it starts with no rows at all. While you play, the game adds one row for each scan: its guess, its clue, and where the range stood afterward. Make it as you made Safe Copy. Its Data Structure, Scan Log Entry, comes with it, and its four fields are all Numbers. Leave the table with no rows, because the game fills it.

Data StructureScan Log Entry

One row of the Scan Log Data Table, made by the engine for the table's rows: guess, clue, low, high (the table's description says what each column means).

guessNumberdefault 0
clueNumberdefault 0
lowNumberdefault 0
highNumberdefault 0
Data TableScan LogData Structure: Scan Log Entry

3.4.1 Record Scan

Record Scan writes one row. It has one Action, Control Data Row, which adds a row to a table, or changes a row that’s already there.

The row’s name is worked out from the count: text('Scan ', $scans). On the first scan, that’s Scan 1. On the third, Scan 3. The name is how a card will find its row later.

Then the four values, one for each column. The clue is the one that Check Guess left waiting for it in Safe State. And low and high are the range after Follow the Clue has moved it, not before.

On the block, Values is one small box. Click it, and a panel opens with a row for each of the table’s columns, its name and its type beside a box. Type each card line into its own row. The Operation list stays at Add or Update, as it starts, and the box after the word as takes addedRow.

FunctionRecord Scan

Adds the scan just made to the Scan Log as row "Scan N" (N is the scan's number): its guess, its clue, and the range after the clue narrowed it.

PARAMETERS
NameTypeStarts at
safeRulesObjectStructural
Required Components
Slot NamesafeState
Component TypeData Structure
Required SchemaSafe State
ACTIONS
1Control Data Row
Action Name
addedRow
Table
Scan Log
Row
text('Scan ', $scans)
Values > guess
$lastGuess
Values > clue
safeRules.safeState.clue
Values > low
$low
Values > high
$high

3.4.2 Scan Guess and Start New Safe

Scan Guess gets one more step. After Follow the Clue, it calls Record Scan, so a scan is written down only once its clue is known. Step 10 goes at the end of step 3’s True line, so right-click step 3’s If and use Add Inside Then. Hand it safeRules, as the call before it does.

FunctionScan GuessAddDelete

Tries the entry as a guess. A guess counts only while the safe is Locked and only inside the range of codes still possible; it then counts one scan, asks Check Guess for the clue, and follows it.

PARAMETERS
NameTypeStarts at
safeRulesObjectStructural
Required Components
Slot NamesafeState
Component TypeData Structure
Required SchemaSafe State
ACTIONS
1Check Game State
Action Name
safeIsLocked
Applies to
safeRules
Game State
Locked
2IfsafeIsLocked.value == true
Then
3If$entry >= $low && $entry <= $high
Then
4Change Variable
Variable
$lastGuess
Value
$entry
5Change Variable
Variable
$scans
Operator
+=
Value
1
6Change Component Value
Component Value
safeRules.safeState.startsNewEntry
Value
true
7Check GuessFunction
Action Name
checkedGuess
guess
$lastGuess
code
safeRules.safeState.code
8Change Component Value
Component Value
safeRules.safeState.clue
Value
checkedGuess.value
9Follow the ClueFunction
safeRules
safeRules
10Record ScanFunction
safeRules
safeRules
11Otherwise
12Change Variable
Variable
$message
Value
data('Safe Copy','Outside the Range','text')
13Otherwise
14Change Variable
Variable
$message
Value
data('Safe Copy','Start a New Safe','text')

Start New Safe gets one too. A new trial starts with an empty record, so Reset Data Table clears the Scan Log, right after the new code is drawn. It goes between two old blocks, so use the round handles, as Actions, one at a time shows.

FunctionStart New SafeAddDelete

Starts a fresh trial on a new safe: draws a new secret code with the engine's random block, opens the whole range again, sets the scans to 0, and shows the starting entry.

PARAMETERS
NameTypeStarts at
safeRulesObjectStructural
Required Components
Slot NamesafeState
Component TypeData Structure
Required SchemaSafe State
ACTIONS
1Generate Random Integer
Action Name
codeRoll
Minimum
data('Safe Settings','Standard Safe','lowestCode')
Maximum
data('Safe Settings','Standard Safe','highestCode')
2Change Component Value
Component Value
safeRules.safeState.code
Value
codeRoll.value
3Reset Data Table
Table
Scan Log
4Change Variable
Variable
$low
Value
data('Safe Settings','Standard Safe','lowestCode')
5Change Variable
Variable
$high
Value
data('Safe Settings','Standard Safe','highestCode')
6Change Variable
Variable
$scans
Value
0
7Change Variable
Variable
$lastGuess
Value
0
8Change Component Value
Component Value
safeRules.safeState.clue
Value
0
9Change Variable
Variable
$entry
Value
data('Safe Settings','Standard Safe','startEntry')
10Change Component Value
Component Value
safeRules.safeState.startsNewEntry
Value
true
11Set Game State
Applies to
safeRules
Game State
Locked
12Change Variable
Variable
$message
Value
data('Safe Copy','Scan a Guess','text')

Run the game and scan a few times. Nothing looks different, and that’s expected. The game is keeping notes, and you can’t see them yet. Two parts from now, they show up on the cards.

3.5 The Marks

Now the strip.

A Code Mark is small, 7 pixels wide and 14 tall, a sliver of the Safe Flat picture. Each mark carries a tiny Data Structure, Mark Face, with one field: code, the code it stands for. The mark for 37 holds 37, and that’s all it knows about itself.

Data StructureMark Face

What one Code Mark stands for, given by Lay Out Scan Record when it makes the mark. code: the code it marks.

codeNumberdefault 0

3.5.1 Show Code Mark

Show Code Mark colors one mark. It runs on every mark at once, each looking only at its own code.

It listens for $scans to change. That happens on each counted scan, and when a new safe sets the count back to 0. It’s exactly when the range or the last guess can move.

It asks three questions, in order, and the first yes wins. Is my code the last guess? Then gold. Is my code inside the range, from $low to $high? Then teal. Otherwise, dark: a clue has ruled it out.

The three colors aren’t typed into the Logic. They’re its three Shared Variables, lastGuessColor, inRangeColor and ruledOutColor, each a Color that’s Constant. Want a different gold? Change one Starting Value, and every mark follows it.

Each color goes in through Change Sprite Tint Property, which tints this Object’s picture with the color in its Value. The If, Else If and Else are the same shape as Check Guess. And its Data Structures card lists Mark Face, so the Logic reads the code as self.markFace.code.

Logic ComponentShow Code MarkAttach to: Code Mark

Colors one Code Mark by what the player knows about its code: gold for the last guess, teal while the code is still possible, dark once it is ruled out.

Attach toAny Object
DATA STRUCTURES
Mark Faceself.markFace
SHARED VARIABLES
NameTypeKindStarting Value
lastGuessColorColorConstant#f3c569
inRangeColorColorConstant#48bab5
ruledOutColorColorConstant#263b47
On Variable Changed · Scans
WhenVariable ChangedGlobal Variable: scans
1Ifself.markFace.code == $lastGuess
Then
2Change Sprite Tint Property
Value
@self.lastGuessColor
3Otherwise
4Ifself.markFace.code >= $low && self.markFace.code <= $high
Then
5Change Sprite Tint Property
Value
@self.inRangeColor
6Otherwise
7Change Sprite Tint Property
Value
@self.ruledOutColor

The Code Mark itself is a picture with the Mark Face and Show Code Mark on it. It isn’t placed anywhere. The marks are made while the game runs, in the last part.

ObjectCode Mark
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 7, Y 14
Sprite
Z Index Within Object-1
SpriteSafe Flat
Tint#48BAB5
Transform > SizeX 7, Y 14
Data Structure
Data StructureMark Face
Logic
Show Code Mark

Each mark starts teal. Set the Sprite’s Tint before its Transform, in the order its card shows. Show Code Mark takes over when you scan.

3.6 The Cards

Next, the cards along the bottom. A Scan Card is three Objects stacked in one spot: the card itself, its guess in gold on top, and its clue in small letters under that.

3.6.1 Clue Word

A clue is a number, 1, -1 or 0. A card should say a word. Clue Word is the small Function that turns one into the other.

It gets the clue, and it hands back a word: HIGHER for 1, LOWER for -1, UNLOCKED for 0. The words live in Safe Copy, in three new rows, so all the game’s words stay in one table.

Data TableSafe CopyData Structure: Safe Copy EntryAddDelete
Row 0Scan a Guess
textScan a guess to get a clue.
Row 1Higher Than
textHIGHER THAN
Row 2Lower Than
textLOWER THAN
Row 3Unlocked
textUNLOCKED!
Row 4Out of Scans
textOUT OF SCANS
Row 5Outside the Range
textUse the remaining range.
Row 6Start a New Safe
textStart a new safe.
Row 7Code Sealed
textCode sealed.
Row 8Possibilities Remain
textpossibilities remain
Row 9One Possibility Remains
textpossibility remains
Row 10Card Higher
textHIGHER
Row 11Card Lower
textLOWER
Row 12Card Unlocked
textUNLOCKED

It’s built like Check Guess, the other way round. Declare Variable makes word, a Text this time, so set its Type list to Text. An If, an Else If and an Else pick the row, and Return hands the word back. Its RETURN TYPE is Text.

FunctionClue Word

Answers with the words a Scan Card shows for a clue: UNLOCKED for 0, HIGHER for 1, LOWER for -1.

PARAMETERS
NameTypeStarts at
clueNumber0
RETURN TYPEText
ACTIONS
1Declare Variable
Type
Text
Name
word
2Ifclue == 0
Then
3Change Variable
Variable
word
Value
data('Safe Copy','Card Unlocked','text')
4Otherwise
5Ifclue == 1
Then
6Change Variable
Variable
word
Value
data('Safe Copy','Card Higher','text')
7Otherwise
8Change Variable
Variable
word
Value
data('Safe Copy','Card Lower','text')
9Return
Value
word

3.6.2 Show Scan Card

Every piece of every card carries the same Data Structure, Card Face: scan, the card’s number, then guess and clue, the two things it shows. The Card Guess’s Text is {guess}, and the Card Clue’s is {clue}, so each one shows a field of its own Card Face.

Data StructureCard Face

What one piece of a Scan Card shows, written by its Show Scan Card Logic. scan: the number of the scan the card shows (1 for the first), given by Lay Out Scan Record. guess: the code scanned. clue: the clue in words (HIGHER, LOWER or UNLOCKED).

scanNumberdefault 0
guessNumberdefault 0
clueTextdefault (empty)

Show Scan Card runs on all three pieces of every card. When a piece is first made, at the start, there’s nothing for it to show yet. It hides. From then on, each time $scans changes, it asks one question. Has my scan happened yet?

Card 3 waits through the first two scans, hidden and patient. On the third, its answer turns yes. It reads the row Scan 3 from the Scan Log, takes the guess, and hands the clue to Clue Word for its word. Then it shows itself. When a new safe sets $scans to 0, every card’s answer turns no, and they all hide again.

From each card’s point of view, there’s nothing to keep track of. It knows its own number. The Scan Log knows everything else.

The answer from Clue Word comes back by its Action Name, cardWords, as cardWords.value. And its formulas find the row by the very name Record Scan gave it: text('Scan ', self.cardFace.scan).

Logic ComponentShow Scan CardAttach to: Card Guess, Card Clue, Scan Card

Shows one piece of a Scan Card (the card, its guess or its clue) once the card's scan has happened, with that scan's guess and clue from the Scan Log, and hides it again when a new safe starts.

Attach toAny Object
DATA STRUCTURES
Card Faceself.cardFace
When Created
WhenCreate
1Set Visible
Visible
No
On Variable Changed · Scans
WhenVariable ChangedGlobal Variable: scans
1Ifself.cardFace.scan <= $scans
Then
2Change Component Value
Component Value
self.cardFace.guess
Value
data('Scan Log', text('Scan ', self.cardFace.scan), 'guess')
3Clue WordFunction
Action Name
cardWords
clue
data('Scan Log', text('Scan ', self.cardFace.scan), 'clue')
4Change Component Value
Component Value
self.cardFace.clue
Value
cardWords.value
5Set Visible
Visible
Yes
6Otherwise
7Set Visible
Visible
No

The three Objects come next. The Scan Card is the dark panel, the Safe History picture. The Card Guess and the Card Clue are words, so they have no picture. All three carry Card Face and Show Scan Card. Like the Code Mark, none of them is placed.

ObjectScan Card
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 127, Y 63
Sprite
Z Index Within Object-1
SpriteSafe History
Transform > SizeX 127, Y 63
Data Structure
Data StructureCard Face
Logic
Show Scan Card
ObjectCard Guess
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Text / UI
Text{guess}
Font Size23
EmphasisBold
Color#F5CD73
RenderingVector
Data Structure
Data StructureCard Face
Logic
Show Scan Card
ObjectCard Clue
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Text / UI
Text{clue}
Font Size11
EmphasisBold
Color#C9D6DC
RenderingVector
Data Structure
Data StructureCard Face
Logic
Show Scan Card

3.7 Laying Out the Record

Two threads meet here. The marks and the cards exist, and the record is being written, but nothing puts them on the screen. One piece of Logic sets them all out.

3.7.1 Lay Out Scan Record

This is the chapter’s one hard idea. Take it slowly.

Lay Out Scan Record sits on Safe Rules and runs once, when the Scene starts. It has two jobs, and each one is a Region on its canvas: Code Marks, then Scan Cards.

Picture the strip as a shelf 970 pixels wide. The range holds 100 codes, so the shelf is shared out into 100 equal slots, side by side. Each mark stands in the middle of its own slot. That’s the whole layout.

Take the first mark slowly. Its slot starts at the shelf’s left edge, which is stripLeft. Its middle is half a slot further in. The second mark’s middle is one whole slot after that, and the third one after that. So the mark with index n stands at stripLeft, plus n and a half slots.

A Repeat does that for every code. Its Count is codeCount, the number of codes in the range. Inside, local.index says which time round it is, from 0. Each time round, three things happen. Create Instance makes a new Code Mark and names it mark. Set Position puts mark in its slot. And Change Variable gives mark its code, the lowest code plus local.index. So the first mark is 1, and the hundredth is 100.

Inside the Repeat, the blocks aim at mark, through their Applies to box or the start of their Variable box. So Change Variable writes mark.code, the short way, as the card spells it. That’s the code in the new mark’s Mark Face. The values in those blocks still read Safe Rules, though, because Safe Rules is the Object running the Logic.

The cards work the same way, in the second Region, with seven slots instead of a hundred. One thing is added. When the slots are narrower than a card, cardScale shrinks every card to fit, so no card covers the next. At seven scans, there’s room, and min keeps the scale at 1.

Why not place the marks by hand? You could. A hundred placements is a long afternoon, though, and if you ever change highestCode, every one is in the wrong place. This way the strip follows the table. That’s the Challenge, at the end.

Now build it. Its Data Structures card gets something new. Press its +, and choose New Data Structure this time. Myth makes Lay Out Scan Record Data, names it after the Logic, and opens it. Give it the four fields on its card. Note cardScale’s default, 1.

Data StructureLay Out Scan Record Data

What Lay Out Scan Record keeps on Safe Rules while it lays out the record strip. codeCount: how many codes the range holds. markSlot: the width of one code's slot on the strip, in pixels. cardSlot: the width of one scan's slot in the row of cards. cardScale: how large each card is drawn (1 is full size).

codeCountNumberdefault 0
markSlotNumberdefault 0
cardSlotNumberdefault 0
cardScaleNumberdefault 1

Next come the Shared Variables, thirteen of them, all Constant. Four are Objects, the pieces it makes, starting at None. The other nine are Numbers: positions and widths, each with the card’s Starting Value. They’re the screen’s own pixels.

The Event is Scene Start. Build the first Region’s blocks down the card: two Change Component Values, then the Repeat with its three blocks inside, through Add Inside Repeat. Then select those blocks and choose Region from Selection, with the card’s Label and Color. The second Region is the same pattern. Its Repeat holds the same four blocks three times, one set each for card, guess and clue. They differ only in their names and their tops, and your hands will know the rhythm by the second set.

Logic ComponentLay Out Scan RecordAttach to: Safe Rules

Makes the record strip when the Safe Room begins: one Code Mark for each code a safe can have, and one Scan Card, with its guess and its clue, for each scan of the budget. The strip and the row of cards keep their width and share it out in equal slots, so the record fits the window whatever the range of codes and the scan budget.

Attach toAny Object
DATA STRUCTURES
Lay Out Scan Record Dataself.layOutScanRecordData
SHARED VARIABLES
NameTypeKindStarting Value
codeMarkObjectConstantNone
stripLeftNumberConstant54.15
stripWidthNumberConstant970
markTopNumberConstant-629
scanCardObjectConstantNone
cardGuessObjectConstantNone
cardClueObjectConstantNone
rowLeftNumberConstant51.5
rowWidthNumberConstant973
cardRoomNumberConstant139
cardTopNumberConstant-695
guessTopNumberConstant-685
clueTopNumberConstant-707
When the Scene Starts
WhenScene Start
1Region
Label
"Code Marks"
Color
#48BAB5
2Change Component Value
Component Value
self.layOutScanRecordData.codeCount
Value
data('Safe Settings','Standard Safe','highestCode') - data('Safe Settings','Standard Safe','lowestCode') + 1
3Change Component Value
Component Value
self.layOutScanRecordData.markSlot
Value
@self.stripWidth / self.layOutScanRecordData.codeCount
4Repeatself.layOutScanRecordData.codeCounttimes
Each time
5Create Instance
Object to Create
@self.codeMark
X
0
Y
0
Created As
mark
6Set Position
Applies to
mark
Position Relative To
Screen
X
@self.stripLeft + (local.index + 0.5) * self.layOutScanRecordData.markSlot
Y
@self.markTop
7Change Variable
Variable
mark.code
Value
data('Safe Settings','Standard Safe','lowestCode') + local.index
8Region
Label
"Scan Cards"
Color
#F3C569
9Change Component Value
Component Value
self.layOutScanRecordData.cardSlot
Value
@self.rowWidth / data('Safe Settings','Standard Safe','scanBudget')
10Change Component Value
Component Value
self.layOutScanRecordData.cardScale
Value
min(1, self.layOutScanRecordData.cardSlot / @self.cardRoom)
11Repeatdata('Safe Settings','Standard Safe','scanBudget')times
Each time
12Create Instance
Object to Create
@self.scanCard
X
0
Y
0
Created As
card
13Set Position
Applies to
card
Position Relative To
Screen
X
@self.rowLeft + (local.index + 0.5) * self.layOutScanRecordData.cardSlot
Y
@self.cardTop
14Set Scale
Applies to
card
Horizontal Scale
self.layOutScanRecordData.cardScale
Vertical Scale
self.layOutScanRecordData.cardScale
15Change Variable
Variable
card.scan
Value
local.index + 1
16Create Instance
Object to Create
@self.cardGuess
X
0
Y
0
Created As
guess
17Set Position
Applies to
guess
Position Relative To
Screen
X
@self.rowLeft + (local.index + 0.5) * self.layOutScanRecordData.cardSlot
Y
@self.guessTop
18Set Scale
Applies to
guess
Horizontal Scale
self.layOutScanRecordData.cardScale
Vertical Scale
self.layOutScanRecordData.cardScale
19Change Variable
Variable
guess.scan
Value
local.index + 1
20Create Instance
Object to Create
@self.cardClue
X
0
Y
0
Created As
clue
21Set Position
Applies to
clue
Position Relative To
Screen
X
@self.rowLeft + (local.index + 0.5) * self.layOutScanRecordData.cardSlot
Y
@self.clueTop
22Set Scale
Applies to
clue
Horizontal Scale
self.layOutScanRecordData.cardScale
Vertical Scale
self.layOutScanRecordData.cardScale
23Change Variable
Variable
clue.scan
Value
local.index + 1

Attach Lay Out Scan Record to Safe Rules. Its Lay Out Scan Record Data comes along with it, marked From Logic on the Object, so there’s nothing to attach by hand. Then, under Variables, pick the four Objects it makes: Code Mark, Scan Card, Card Guess and Card Clue.

ObjectSafe RulesAddDelete
Transform
Bounding Box > SizeX 0.8, Y 0.8
Data Structure
Data StructureSafe State
Data Structure
From LogicLay Out Scan Record
Data StructureLay Out Scan Record Data
Logic
New Safe
Keypad Keys
Button Clicks
Lay Out Scan Record
Shared Variables
codeMarkCode Mark
scanCardScan Card
cardGuessCard Guess
cardClueCard Clue

Last, the two labels under the ends of the strip. Each Text reads its end of the range straight from Safe Settings, so a bigger range labels itself.

ObjectLowest Code Label
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Text / UI
Text{data('Safe Settings','Standard Safe','lowestCode')}
Font Size10
EmphasisMedium
Color#7D96A5
RenderingVector
ObjectHighest Code Label
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Text / UI
Text{data('Safe Settings','Standard Safe','highestCode')}
Font Size10
EmphasisMedium
Color#7D96A5
RenderingVector

Place them both from PLACE THESE.

Run the game. Under the range line, a strip of a hundred teal marks runs across the room, with a small 1 under its left end and 100 under its right. Scan 50, and the whole strip lights up at once: teal on one side, dark on the other, and one gold mark at 50. Card 1 turns up along the bottom, with 50 on it, and HIGHER or LOWER underneath.

The search is on the screen now.

Play it!

Run it, and watch for each of these in turn.

  • Under POSSIBLE CODE, a strip of a hundred thin marks runs nearly the width of the room. A small 1 sits under its left end and 100 under its right.
  • No cards show along the bottom yet.
  • Scan 50. If the clue is HIGHER, every mark below 50 goes dark, the 50 is gold, and 51 to 100 are teal. LOWER is the mirror of it.
  • Card 1 appears at the left of the row: 50 in gold, and HIGHER or LOWER under it.
  • Scan the middle of the teal band. About half of what’s left goes dark, and card 2 appears beside card 1.
  • Now scan near one end of the band instead. If the clue points away from that end, only a few marks go dark.
  • Try a code that’s already dark. “Use the remaining range.” No card appears, and no mark changes.
  • Open the safe. Its mark is gold, the only one lit, and its card says UNLOCKED.
  • Press R. Every card hides, and the whole strip turns teal again.
How it works

Record Scan writes every scan into the Scan Log. Show Code Mark colors each mark by the range and the last guess. Show Scan Card shows each card once its scan has happened, reading its row. Lay Out Scan Record makes all of them when the room opens, each in the middle of its own slot.

Underneath is one idea: the screen keeps nothing of its own. Every mark and every card reads the same few values, $low, $high, $lastGuess, $scans and the Scan Log, and draws what they say. Change those, and the whole record follows.

3.8 If It Didn’t Work

There are a lot of new pieces in this chapter, and a lot of them only show up once the others work. If something’s off, that’s normal. Scan once and look closely at what changes, and what doesn’t.

No strip at all? Perhaps Lay Out Scan Record never ran. Look for it on Safe Rules, with Scene Start as its Event. Look too at the four Objects under Safe Rules’ Variables, because a codeMark left at None makes nothing.

All the marks pile up in one spot? Then Set Position isn’t aimed at mark. Look at its box after the word on, where mark belongs. Look at its Position Relative To list too, which should say Screen.

The marks never change color? Look at Show Code Mark’s Event, which listens for scans, written without the $. And look at the Code Mark’s Mark Face. Each mark needs its code, set by the Change Variable mark.code in the Repeat.

Every mark turns dark, even the teal ones? Perhaps the second condition has one of its two tests turned round. It reads self.markFace.code >= $low && self.markFace.code <= $high.

The cards never show? Perhaps Record Scan isn’t called. Look at the end of Scan Guess’s inner True line. And look at Record Scan’s Row, text('Scan ', $scans), with a space after Scan inside the quotes. Show Scan Card looks for a row of the very same name.

A card shows the guess, but no word under it? Look at Clue Word’s Return, and at its RETURN TYPE, which should be Text.

Old cards stay after R? Look for Reset Data Table in Start New Safe. Without it, the cards of the last trial keep their rows.

Each of these is one small fix. Make it, scan again, and watch the strip. The catch-up download in the Goal is this chapter’s game, finished, if you’d like to compare.

Challenge

In Chapter 2, a bigger safe only showed in two numbers. Now you can see it. In Safe Settings, change highestCode to 200 and scanBudget to 12, and run the game. The strip still fits the room, now with two hundred marks, and twelve smaller cards share the row. Nothing else changed. Then try a range you choose, and see how the record looks. Before you start Chapter 4, set them back to 100 and 7.

The search is on the screen now, one gold mark at a time. In Chapter 4, you hand the scanner to the computer, and watch it halve the teal band every turn.