- Learn
- Tutorials
- Algorithms
- Binary Search
- 3 Scan Record
3 Scan Record
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.
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
$scanschanges -> Show Code Mark colors each mark gold, teal or dark. - The count
$scanschanges -> 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.
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.
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).
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.
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.
safeStateData StructureSafe State- 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.
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.
safeStateData StructureSafe State- Action Name
safeIsLocked- Applies to
safeRules- Game State
Locked
safeIsLocked.value == true$entry >= $low && $entry <= $high- Variable
$lastGuess- Value
$entry
- Variable
$scans- Operator
+=- Value
1
- Component Value
safeRules.safeState.startsNewEntry- Value
true
- Action Name
checkedGuess- guess
$lastGuess- code
safeRules.safeState.code
- Component Value
safeRules.safeState.clue- Value
checkedGuess.value
- safeRules
safeRules
- safeRules
safeRules
- Variable
$message- Value
data('Safe Copy','Outside the Range','text')
- 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.
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.
safeStateData StructureSafe State- Action Name
codeRoll- Minimum
data('Safe Settings','Standard Safe','lowestCode')- Maximum
data('Safe Settings','Standard Safe','highestCode')
- Component Value
safeRules.safeState.code- Value
codeRoll.value
- Table
Scan Log
- Variable
$low- Value
data('Safe Settings','Standard Safe','lowestCode')
- Variable
$high- Value
data('Safe Settings','Standard Safe','highestCode')
- Variable
$scans- Value
0
- Variable
$lastGuess- Value
0
- Component Value
safeRules.safeState.clue- Value
0
- Variable
$entry- Value
data('Safe Settings','Standard Safe','startEntry')
- Component Value
safeRules.safeState.startsNewEntry- Value
true
- Applies to
safeRules- Game State
Locked
- 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.
What one Code Mark stands for, given by Lay Out Scan Record when it makes the mark. code: the code it marks.
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.
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.
Any Objectself.markFaceself.markFace.code == $lastGuess- Value
@self.lastGuessColor
self.markFace.code >= $low && self.markFace.code <= $high- Value
@self.inRangeColor
- 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.
Window / UIUIX 7, Y 14-1Safe Flat#48BAB5X 7, Y 14Mark FaceEach 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.
Scan a guess to get a clue.HIGHER THANLOWER THANUNLOCKED!OUT OF SCANSUse the remaining range.Start a new safe.Code sealed.possibilities remainpossibility remainsHIGHERLOWERUNLOCKEDIt’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.
Answers with the words a Scan Card shows for a clue: UNLOCKED for 0, HIGHER for 1, LOWER for -1.
- Type
Text- Name
word
clue == 0- Variable
word- Value
data('Safe Copy','Card Unlocked','text')
clue == 1- Variable
word- Value
data('Safe Copy','Card Higher','text')
- Variable
word- Value
data('Safe Copy','Card Lower','text')
- 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.
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).
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).
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.
Any Objectself.cardFace- Visible
No
self.cardFace.scan <= $scans- Component Value
self.cardFace.guess- Value
data('Scan Log', text('Scan ', self.cardFace.scan), 'guess')
- Action Name
cardWords- clue
data('Scan Log', text('Scan ', self.cardFace.scan), 'clue')
- Component Value
self.cardFace.clue- Value
cardWords.value
- Visible
Yes
- 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.
Window / UIUIX 127, Y 63-1Safe HistoryX 127, Y 63Card FaceWindow / UIUI{guess}23Bold#F5CD73VectorCard FaceWindow / UIUI{clue}11Bold#C9D6DCVectorCard Face3.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.
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).
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.
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.
Any Objectself.layOutScanRecordData- Label
"Code Marks"- Color
#48BAB5
- Component Value
self.layOutScanRecordData.codeCount- Value
data('Safe Settings','Standard Safe','highestCode') - data('Safe Settings','Standard Safe','lowestCode') + 1
- Component Value
self.layOutScanRecordData.markSlot- Value
@self.stripWidth / self.layOutScanRecordData.codeCount
self.layOutScanRecordData.codeCounttimes- Object to Create
@self.codeMark- X
0- Y
0- Created As
mark
- Applies to
mark- Position Relative To
Screen- X
@self.stripLeft + (local.index + 0.5) * self.layOutScanRecordData.markSlot- Y
@self.markTop
- Variable
mark.code- Value
data('Safe Settings','Standard Safe','lowestCode') + local.index
- Label
"Scan Cards"- Color
#F3C569
- Component Value
self.layOutScanRecordData.cardSlot- Value
@self.rowWidth / data('Safe Settings','Standard Safe','scanBudget')
- Component Value
self.layOutScanRecordData.cardScale- Value
min(1, self.layOutScanRecordData.cardSlot / @self.cardRoom)
data('Safe Settings','Standard Safe','scanBudget')times- Object to Create
@self.scanCard- X
0- Y
0- Created As
card
- Applies to
card- Position Relative To
Screen- X
@self.rowLeft + (local.index + 0.5) * self.layOutScanRecordData.cardSlot- Y
@self.cardTop
- Applies to
card- Horizontal Scale
self.layOutScanRecordData.cardScale- Vertical Scale
self.layOutScanRecordData.cardScale
- Variable
card.scan- Value
local.index + 1
- Object to Create
@self.cardGuess- X
0- Y
0- Created As
guess
- Applies to
guess- Position Relative To
Screen- X
@self.rowLeft + (local.index + 0.5) * self.layOutScanRecordData.cardSlot- Y
@self.guessTop
- Applies to
guess- Horizontal Scale
self.layOutScanRecordData.cardScale- Vertical Scale
self.layOutScanRecordData.cardScale
- Variable
guess.scan- Value
local.index + 1
- Object to Create
@self.cardClue- X
0- Y
0- Created As
clue
- Applies to
clue- Position Relative To
Screen- X
@self.rowLeft + (local.index + 0.5) * self.layOutScanRecordData.cardSlot- Y
@self.clueTop
- Applies to
clue- Horizontal Scale
self.layOutScanRecordData.cardScale- Vertical Scale
self.layOutScanRecordData.cardScale
- 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.
X 0.8, Y 0.8Safe StateLay Out Scan RecordLay Out Scan Record DataCode MarkScan CardCard GuessCard ClueLast, 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.
Window / UIUI{data('Safe Settings','Standard Safe','lowestCode')}10Medium#7D96A5VectorWindow / UIUI{data('Safe Settings','Standard Safe','highestCode')}10Medium#7D96A5VectorPlace 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.
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.
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.
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.
