- Learn
- Build a Game
- Chess
- 12 Game Menu and Keyboard
By the end of this chapter, a ••• button opens the Game menu. It lists every Move of the game, the way chess players write it, with How to play and Resign beside them. And the whole game plays from the keyboard: the arrow keys, Enter and Escape. This is the last chapter. When it’s done, the game at the top of the Welcome page is yours.
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, the whole game. Open it and compare.
To open it, right-click the downloaded file, choose Extract All... and then Extract. Start Myth Engine, choose File › Open Project... and pick the .mythproj file in the new folder. CREDITS.md and the licenses folder beside it come with the game.
12.1 The Idea
Thirty Moves into a long game, you stop. How did it get like this? Where did your Bishop go, and when did the Computer’s Knight slip in on d4? The Board only shows you now. It has forgotten the rest.
Chess players have an old answer to this, and it’s a pencil and a sheet of paper. After every Move, each player writes it down in a few letters: e4, Nf3, exd5, O-O. That way of writing is called standard notation. A letter names the Kind, K, Q, R, B or N, and a Pawn has none. Then comes the Square it lands on. An x marks a Capture, + a Check and # a Checkmate. Castling is O-O on the King’s side and O-O-O on the Queen’s.
This chapter gives your game that pencil. A ••• button opens the Game menu. It lists every Move, twelve full Moves to a page, with How to play and Resign under the list.
Here’s the hard part, and it’s about timing. A Move’s name has to be written down before the Move is made. Once a Pawn has landed on d5, the Board can’t tell you whether something stood there a moment ago. Think of a person keeping score at a tournament. They write the Move as the hand lets go, while they can still see where it came from.
Then the keyboard. Since Chapter 2, every Move has started with a click. This chapter adds keys, and the keys don’t need new rules. An amber outline, the Focus Mark, moves from Square to Square with the arrow keys. Enter presses the Square under it, and the game hears the same message a click sends.
12.2 The Plan
Here’s how the pieces work together. Read each line from left to right.
- The ••• button is pressed -> Report Game Menu sends gameMenuPressed -> Game Menu opens the Game menu, and sends gameMenuOpened.
- Move Pages hears gameMenuOpened -> it picks the newest page, and sends movePageChanged -> each Show Recorded Move shows its Move.
- A Square is clicked -> Select and Move -> Record Move -> Name The Move writes the Move’s name in the Move History -> then Make Move makes it.
- An arrow key is pressed -> the Command Move Focus -> Keyboard Focus steps the Focus Mark -> Enter is pressed -> squareClicked -> Select and Move, as for a click.
12.3 Set Up
Left ArrowRight ArrowUp ArrowDown ArrowHomeEndEnter, SpaceEscapeLeft ArrowRight ArrowEnter, SpaceEscapeMove Focus, Focus Row Start, Focus Row End, Press Square, CancelChoose Piece, Promote, Cancel Choice#F4F1E9ART names two pictures from the Starter Kit, already in your project. The Focus Outline marks a Square. The Promotion Outline goes around a Piece of the Promotion choice.
COMMANDS and INPUT CONTEXTS are the game’s keys. Make them all now, in Game Settings’ Input Manager, the way Keys: Commands and Input Contexts shows. Your Logic will name a Command, never a key. Press Square doesn’t care whether you pressed Enter or Space, and the game never asks.
Go in the card’s order. The first five Commands go in Default, which is selected when the Input Manager opens. Every project has Default already. The card marks it changed only because it gets these Commands, and it stays Active at Start. Then add the Input Context Promotion Choice. It starts Inactive at Start, as the card says, and it’s selected once it’s made. So the last three Commands land in it. Choose Piece is a 2D Axis with only Left and Right, so leave its Up and Down on No Input Assigned.
Why two sets of keys? While the Promotion choice is open, the arrow keys should move between its four Pieces, not across the Board. Only one set is on at a time, and the Logic switches them.
GAME SETTINGS has one line. In Display, Page Background (Outside the Game) takes a Color, #F4F1E9. Myth fits the game to any window, and in a window wider than the game, a band shows beside it. That band is the page background. Now it’s the same paper as the page under the Board.
The placements come later, part by part.
12.5 How to Play
The game teaches itself now, in three short paragraphs.
12.5.1 How To Play Panel and Panel Paragraph
Window / UICenterUIX 600, Y 380TextHow To PlayCancel30Square Highlight#EFE6CFX 600, Y 380How to play26Semibold#503A2BCenterX 0, Y 150Window / UICenterUIX 520, Y 80TextHow To PlayA few words on a panel.16#503A2BWrap to Width520The How To Play Panel is the third cream card of the chapter, 600 by 380. The Panel Paragraph is a block of words that wraps inside its own width, 520 pixels. Its Text Bounds is Wrap to Width, so a long sentence breaks onto a new line instead of running off the panel. Its own words, A few words on a panel., are only a stand-in. Each placement types its own.
12.5.2 Show How To Play and Close How To Play
What the How to play button of the Game menu does: the Game menu closes and the How to play panel opens in its place, a menu that holds the game while it is open.
Any Object- Operation
Hide- Menu Element
Game Menu Panel
- Menu Element
How To Play Panel
- Menu Element
How To Play Panel
What the Done button of the How to play panel does: it closes the panel, and the game goes on.
Any Object- Operation
Hide- Menu Element
How To Play Panel
Show How To Play works like Ask To Resign. The Game menu hides, and the panel opens in its place. Done runs Close How To Play.
Make two more variants from Panel Button: How To Play Button and Close How To Play Button. Each has its own Logic.
Now place the next seven lines, from How To Play Button down to Close How To Play Button. They’re the button in the Game menu, the panel’s Backdrop, the panel, its three paragraphs and its Done. Type each paragraph’s words as the card has them, one placement at a time. The Threats paragraph’s apostrophe is the curly kind. Your keyboard’s straight one works too. It only looks a little different, so copy the words from the card if you’d like them to match. The panel keeps its cream, and the two button variants keep their bases’ tan.
Run the game, press •••, then How to play. Three paragraphs explain selecting, Threats and the keyboard. Press Done.
12.6 Every Move Gets a Name
Now the pencil. This part writes each Move’s name into the Move History. It shows nothing yet. The names wait in the table until the next part draws them.
Take this part slowly. This is the chapter’s hard idea.
12.6.1 Kinds, Castling and Move History
One row of the "Kinds" Data Table, which follows it. One row per Kind of Piece, one column per Side. White and Black: the frame of the Piece Set art that shows this Kind for that Side (every Piece Set art keeps the same frame order). The columns are named for the Sides, so the Side of a Piece picks its column. promotionWords: what the Hint message adds when its Move promotes a Pawn to this Kind (empty for the King and the Pawn, which a Pawn never becomes). letter: the letter a Move of this Kind starts with in the Move History (none for a Pawn). promotionMark: what a Move that promotes a Pawn to this Kind ends with (empty for the King and the Pawn).
06K17, promote to queenQ=Q410, promote to rookR=R28, promote to bishopB=B39, promote to knightN=N511One row of the "Castling" Data Table, which follows it. The four castlings, one row per Side and side of the Board. kingFrom and kingTo: where the King starts and lands (it moves two Squares toward the Rook). rookFrom and rookTo: where the Rook starts and lands (on the Square the King crosses). rookPasses: the Square next to the Rook's, which the Rook crosses (on the King's side it is also where the King lands). isAllowed: yes while neither the King nor this Rook has moved and the Rook has not been taken; a new game starts with every castling allowed. notation: how the Move History writes this castling (O-O on the King's side, O-O-O on the Queen's).
e1g1h1f1g1YesO-Oe1c1a1d1b1YesO-O-Oe8g8h8f8g8YesO-Oe8c8a8d8b8YesO-O-OOne row of the "Move History" Data Table, which follows it. Every Move of the game so far, in the order they were made, one row per Move (the first Move is row 0; each row is named by its number). from and to: the Squares the Piece moved from and to (a1 to h8). landingKind: the Kind of the Piece once it had landed: its own Kind, or, for a Promotion, the Kind the Pawn became. The number of rows is the number of Moves made. Undo takes rows off the end and plays the rest again. The table starts empty. The other columns are the Move's name as chess players write it (standard algebraic notation), in the order it is read: letter, the Kind's letter (none for a Pawn), or O-O or O-O-O for castling; fromFile and fromRank, the File and Rank it came from, written only when needed to tell two Pieces apart (and the File of a Pawn that captures); capture, x for a Capture; square, the Square it went to (none for castling); promotion, what a Pawn became (=Q); checkMark, + for Check or # for Checkmate. The Move's number is not kept: rows 0 and 1 are Move 1 (White's and Black's), rows 2 and 3 Move 2, and so on.
First, give each table’s Entry the new fields its card names, before filling the table’s new columns. The table gains those columns with it. Kinds gets each Kind’s letter, and the mark a Pawn gets when it’s promoted to it, such as =Q. A Pawn’s letter stays empty, because a Pawn’s Move is named by its Square alone. Castling gets its two names, O-O and O-O-O.
The Move History gets seven new columns, one for each piece of a name. Read across a row, and you can spell out the Move: letter, fromFile, fromRank, capture, square, promotion, checkMark. Most of them stay empty for most Moves, and Nf3 fills only letter and square.
12.6.2 Find Other Pieces Reaching
What Find Other Pieces Reaching returns. isFileNeeded: true when the Move's name must say the File the Piece came from. isRankNeeded: true when it must say the Rank.
Asks, before a Move is made, whether another Piece of the same Kind and Side as the one on from could also make a Legal Move to the Square to, as standard notation needs to know (a Pawn is never asked: its Capture is named by its File). from, to: the Move's Squares. enPassantSquare: the Square a Pawn may take en passant now (Board State). isFileNeeded (returned): true when the name must say the File the Piece came from (another such Piece reaches to from another File, or from the same File and the same Rank). isRankNeeded (returned): true when the name must say the Rank it came from (another such Piece reaches to from the same File). The Targets table is left empty.
- Type
Text- Name
square
- Type
Boolean- Name
isReached- Value
false
- Type
Boolean- Name
isSameFile- Value
false
- Type
Boolean- Name
isSameRank- Value
false
- Type
Boolean- Name
isFileNeeded- Value
false
- Type
Boolean- Name
isRankNeeded- Value
false
dataCount('Position')times- Variable
square- Value
dataRow('Position', local.index)
data('Position', square, 'kind') == data('Position', from, 'kind') && data('Position', square, 'side') == data('Position', from, 'side') && square != from && data('Position', from, 'kind') != "Pawn"- from
square- enPassantSquare
enPassantSquare
data('Targets', to, 'reachable') == true- Variable
isReached- Value
true
data('Targets', to, 'reachable') == true && data('Position', square, 'file') == data('Position', from, 'file')- Variable
isSameFile- Value
true
data('Targets', to, 'reachable') == true && data('Position', square, 'rank') == data('Position', from, 'rank')- Variable
isSameRank- Value
true
isReached == trueisSameFile == true && isSameRank == true- Variable
isFileNeeded- Value
true
- Variable
isRankNeeded- Value
true
isSameFile == true- Variable
isRankNeeded- Value
true
- Variable
isFileNeeded- Value
true
- isFileNeeded
isFileNeeded- isRankNeeded
isRankNeeded
Here’s a puzzle notation has to solve. Say both of your Knights could reach d2, one from b1 and one from f3. Nd2 doesn’t say which one went. So chess players add the File it came from: Nbd2. If both stood on the same File, they add the Rank instead.
Find Other Pieces Reaching asks that question before the Move is made, while every Piece is still in place. The Repeat walks all sixty-four rows of Position. It looks for another Piece of the same Kind and Side, and never a Pawn, because a Pawn’s Capture always names its File anyway. For each one it finds, Find Legal Moves asks where that Piece could go. If it could reach the same Square, isReached becomes true. Steps 14 and 16 also note whether it shares the File or the Rank.
After the Repeat, Clear Targets tidies up, since Find Legal Moves filled the Targets table. Then the last If decides, once isReached is true. Most of the time the File is enough. If the other Piece shares the File, the Rank is needed instead. With three such Pieces, one may share the File and another the Rank, and then the name needs both. Its Return Type is Find Other Pieces Reaching Result. Two fields come back: isFileNeeded and isRankNeeded.
12.6.3 Name The Move
Writes a Move's name, as chess players write it (standard algebraic notation: e4, Nf3, exd5, O-O, e8=Q, Rad1), into its row of the Move History, before the Move is made: the name depends on the position the Move is made in. The check mark (+ or #) is written when the next Turn starts (Start The Turn, Look For Game End). from, to: the Move's Squares. landingKind: the Kind the Piece lands as. enPassantSquare: the Square a Pawn may take en passant now. row: the Move's row in the Move History.
- Type
Text- Name
movedKind
- Type
Text- Name
castling
- Variable
movedKind- Value
data('Position', from, 'kind')
- Table
Move History- Row
row- Column
"letter"- Value
data('Kinds', movedKind, 'letter')
- Table
Move History- Row
row- Column
"square"- Value
to
data('Position', to, 'kind') != ""- Table
Move History- Row
row- Column
"capture"- Value
data('Words', 'Capture Mark', 'text')
movedKind == "Pawn" && data('Position', from, 'file') != data('Position', to, 'file')- Table
Move History- Row
row- Column
"capture"- Value
data('Words', 'Capture Mark', 'text')
- Table
Move History- Row
row- Column
"fromFile"- Value
data('Position', from, 'fileLetter')
landingKind != movedKind- Table
Move History- Row
row- Column
"promotion"- Value
data('Kinds', landingKind, 'promotionMark')
dataCount('Castling')times- Variable
castling- Value
dataRow('Castling', local.index)
movedKind == "King" && data('Castling', castling, 'kingFrom') == from && data('Castling', castling, 'kingTo') == to- Table
Move History- Row
row- Column
"letter"- Value
data('Castling', castling, 'notation')
- Table
Move History- Row
row- Column
"square"- Value
''
- Action Name
others- from
from- to
to- enPassantSquare
enPassantSquare
others.isFileNeeded == true- Table
Move History- Row
row- Column
"fromFile"- Value
data('Position', from, 'fileLetter')
others.isRankNeeded == true- Table
Move History- Row
row- Column
"fromRank"- Value
data('Position', from, 'rank')
Name The Move writes a name into one row of the Move History. Everything it reads is the Board before the Move.
It starts with the plain case: the Kind’s letter, then the Square it lands on. Then it looks for each extra mark. Something stands on to? That’s a Capture, and the capture column takes x. A Pawn that changes File is capturing too, even en passant, where its Square is empty. So it gets x, and its own File letter, as in exd5. A promoted Pawn gets its mark.
Castling is a King’s Move, so the Repeat over Castling checks each row. When the King goes from kingFrom to kingTo, its name becomes that row’s notation, and the square goes empty. Last, Find Other Pieces Reaching says whether the name needs a File or a Rank.
What about Check? That can’t be known yet. Whether a King is in Check depends on the Board after the Move. So the + waits, and Start The Turn writes it, further down.
12.6.4 Record Move
Writes a Move at the end of the Move History table just before it is made: the Squares it goes from and to, the Kind the Piece lands as (its own Kind, or the Kind a promoted Pawn becomes), and its name as chess players write it (Name The Move), which depends on the position before the Move. Each Logic that makes a Move calls it just before Make Move (Select and Move, Choose Promotion, Make Computer Move). gameRules: the Game Rules Object, whose Board State holds the en passant Square. from, to: the Move's Squares. landingKind: the Kind the Piece lands as.
boardStateData StructureBoard State- Name
lastRow- Value
0
- Action Name
addedRow- Table
Move History- Row
dataCount('Move History')
- Variable
lastRow- Value
dataCount('Move History') - 1
- Table
Move History- Row
lastRow- Column
'from'- Value
from
- Table
Move History- Row
lastRow- Column
'to'- Value
to
- Table
Move History- Row
lastRow- Column
'landingKind'- Value
landingKind
- from
from- to
to- landingKind
landingKind- enPassantSquare
gameRules.boardState.enPassantSquare- row
lastRow
Record Move used to write the Move after it was made, reading lastFrom and lastTo. Now it’s called before, so it can’t read them. They aren’t set yet. It takes the Move as three Parameters instead: from, to and landingKind.
Delete the Declares for fromSquare and toSquare, and the two Changes that read lastFrom and lastTo. Keep lastRow. Add the three Parameters on the Start block. The full card shows the shorter line.
The Values of steps 4 to 6 now read from, to and landingKind. Each one names a Parameter, so type it bare over the old Value, as Look At Square did in Chapter 3. Step 7 is new. It hands the Move to Name The Move, with the new row.
12.6.5 Start Turn
Starts each Turn: the first one (White's) when the game begins, and the next one after every Move, giving the Turn to the other Side (Pass The Turn). Each Turn starts with Start The Turn: it asks whether the Side to move is in Check, then tells the Game Rules that the Turn has started ("turnStarted").
Any Objectself.boardState- gameRules
self
- Custom Event
positionChanged
- gameRules
self
- gameRules
self
- Component Value
self.boardState.selected- Value
''
- Component Value
self.boardState.inspected- Value
''
- gameRules
self
Start Turn used to call Record Move after each Move. Delete that call, step 1 of On Move Made, because the Move makers do it now, before they move.
12.6.6 The Three Move Makers
Decides what a click on a Square means. A click on a Square the selected Piece may move to writes it down (Record Move) and makes it (Make Move: castling and en passant included) and tells the Game Rules "moveMade" (Start Turn then starts the other Side's Turn); when the Move takes a Pawn to its last Rank, it opens the Promotion choice instead (Game State Promotion), and Choose Promotion makes the Move. A click on a Piece of the Side to move selects it and marks its Legal Moves (Find Legal Moves); any other click clears the selection. In Threats mode a click that is not a Move also inspects the Piece clicked, of either Side (Inspect Piece). Then it tells everyone "positionChanged". Its Other is the clicked Square. Every click forgets the Hint (Forget The Hint). Escape (the Command Cancel) clears the selection and the Inspected Piece while the game is being played.
Any Objectself.boardStateself.selectAndMoveData- Type
Text- Name
selectedSquare
- Type
Text- Name
clickedSquare
- Variable
clickedSquare- Value
other.squareInfo.name
- Variable
selectedSquare- Value
self.boardState.selected
selectedSquare != "" && data('Targets', clickedSquare, 'reachable') == true- Component Value
self.selectAndMoveData.side- Value
self.boardState.sideToMove
data('Position', selectedSquare, 'kind') == "Pawn" && data('Position', clickedSquare, 'rank') == data('Sides', self.selectAndMoveData.side, 'promotionRank')- gameRules
self- square
clickedSquare
- gameRules
self- from
selectedSquare- to
clickedSquare- landingKind
data('Position', selectedSquare, 'kind')
- gameRules
self- from
selectedSquare- to
clickedSquare
- Custom Event
moveMade
data('Position', clickedSquare, 'side') == self.boardState.sideToMove && clickedSquare != selectedSquare- Component Value
self.boardState.selected- Value
clickedSquare
- from
clickedSquare- enPassantSquare
self.boardState.enPassantSquare
- Component Value
self.boardState.selected- Value
''
- gameRules
self- square
clickedSquare
- Custom Event
positionChanged
- Action Name
isPlaying- Game State
Playing
isPlaying.value == true- Component Value
self.boardState.selected- Value
''
- Component Value
self.boardState.inspected- Value
''
- Custom Event
positionChanged
Closes the Promotion choice when the Player presses one of its buttons ("promotionChosen"; the button is its Other): the panel is hidden and the game is played again (Game State Playing). A Kind then writes down the waiting Pawn Move (Record Move) and makes it (Make Move), puts the chosen Kind on the Square, and tells the Game Rules "moveMade"; Cancel makes no Move, and the Pawn stays selected. Then everyone hears "positionChanged".
Any Objectself.boardStateself.choosePromotionData- Action Name
isChoosing- Game State
Promotion
isChoosing.value == true- Component Value
self.choosePromotionData.chosenKind- Value
other.promotionChoice.kind
- Component Value
self.choosePromotionData.landingSquare- Value
self.boardState.promotionSquare
- Component Value
self.boardState.promotionSquare- Value
''
- UI Group
"Promotion"- Visible
No
- Game State
Playing
self.choosePromotionData.chosenKind != ""- gameRules
self- from
self.boardState.selected- to
self.choosePromotionData.landingSquare- landingKind
self.choosePromotionData.chosenKind
- gameRules
self- from
self.boardState.selected- to
self.choosePromotionData.landingSquare
- Table
Position- Row
self.choosePromotionData.landingSquare- Column
"kind"- Value
self.choosePromotionData.chosenKind
- Custom Event
moveMade
- gameRules
self
- Custom Event
positionChanged
Plays the Computer's Turns. When a Turn goes on ("gameContinues", from Look For Game End) and the Side to move is the Player's opponent (the Sides table), it asks the Computer for a Move (Ask The Computer: the Board waits in Computer's Turn while the Stockfish Bridge asks Stockfish at the Strength in Board State). When Stockfish answers ("computerMoveChosen"; the Move is in the Chosen Moves table) and it is still the Computer's Turn and the Move is a Legal Move, the Move slides first (Plan The Slide fills the Slides table; everyone hears "slideStarted"). When it has landed ("pieceLanded", from the Moving Piece), it is made if it is still the Computer's Turn, the way the Player's Moves are made: Record Move, Make Move, the Kind it lands as (a promoted Pawn becomes the Kind Stockfish named), then "moveMade" (Start Turn starts the Player's Turn) and "positionChanged". When Stockfish gives no Move, or one that is not legal, or does not answer within its thinking time and spareSeconds more (the answerWait timer), the position stays and the Computer is marked unavailable (Mark Computer Unavailable) until the Player presses Retry ("retryPressed"), which asks again. spareSeconds: how much longer than its thinking time the Computer may take to answer, starting Stockfish included. A Player whose device asks for less motion (the system setting "reduce motion", the Myth Global prefersReducedMotion) sees the Move made at once, with no slide.
Any Objectself.boardStateself.makeComputerMoveDataself.boardState.sideToMove == data('Sides', self.boardState.playerSide, 'opponent')- gameRules
self- spareSeconds
@self.spareSeconds
- Action Name
isThinking- Game State
Computer's Turn
isThinking.value == true- Timer Name
"answerWait"
- Component Value
self.makeComputerMoveData.fromSquare- Value
data('Chosen Moves', 'Computer', 'from')
- Component Value
self.makeComputerMoveData.toSquare- Value
data('Chosen Moves', 'Computer', 'to')
- from
self.makeComputerMoveData.fromSquare- enPassantSquare
self.boardState.enPassantSquare
data('Targets', self.makeComputerMoveData.toSquare, 'reachable') == true$prefersReducedMotion == 1- Custom Event
pieceLanded
- from
self.makeComputerMoveData.fromSquare- to
self.makeComputerMoveData.toSquare
- Custom Event
slideStarted
- gameRules
self
- Action Name
isLanding- Game State
Computer's Turn
isLanding.value == true- Component Value
self.makeComputerMoveData.fromSquare- Value
data('Chosen Moves', 'Computer', 'from')
- Component Value
self.makeComputerMoveData.toSquare- Value
data('Chosen Moves', 'Computer', 'to')
- Component Value
self.makeComputerMoveData.landingKind- Value
data('Chosen Moves', 'Computer', 'landingKind')
- Game State
Playing
- gameRules
self- from
self.makeComputerMoveData.fromSquare- to
self.makeComputerMoveData.toSquare- landingKind
self.makeComputerMoveData.landingKind
- gameRules
self- from
self.makeComputerMoveData.fromSquare- to
self.makeComputerMoveData.toSquare
- Table
Position- Row
self.makeComputerMoveData.toSquare- Column
"kind"- Value
self.makeComputerMoveData.landingKind
- Custom Event
moveMade
- Custom Event
positionChanged
- Action Name
isStillThinking- Game State
Computer's Turn
isStillThinking.value == true- gameRules
self
- Action Name
isUnavailable- Game State
Computer Unavailable
isUnavailable.value == true- gameRules
self- spareSeconds
@self.spareSeconds
Three pieces of Logic make Moves: Select and Move for yours, Choose Promotion for a Pawn you promote, and Make Computer Move for the Computer’s. Each one gets the same new step, a call to Record Move, right before Make Move. Each of these cards opens with a long Description. Nothing reads a Description, so read past it to the green lines, here and on every Changes card of this chapter.
Each call joins before Make Move, with the round handles, as Actions, one at a time shows. Add it inside the same panel first. Choose Promotion uses step 8’s If. Its Add Inside Then adds Record Move there, ready to join before the old Make Move. Join it to that If’s True handle, before Make Move.
Select and Move’s call goes in step 10’s Else, on the False side of its inner If. Its Add Inside Else adds Record Move in that panel. Join it there first. Make Move stays behind it, followed by the Trigger. In Make Computer Move, add the call inside On Piece Landed’s step 2, and join it after Set Game State.
Each call hands over the Kind the Piece lands as. For Select and Move, that’s the selected Piece’s own Kind, read from Position. For Choose Promotion, it’s chosenKind. For the Computer, it’s landingKind, from the Chosen Moves table.
Select and Move also gets a second Event, When Cancel Is Pressed: a Command Pressed Event for the Command Cancel, which is Escape. While the game is being played, Escape clears the selection and the Inspected Piece. Build it now, while you’re here. It needs no other part.
12.6.7 Start The Turn
Starts the Turn of the Side to move, in the position on the Board: marks a King in Check and sets the status line (Look For Check), marks the Move that gave Check with + in the Move History, then tells the Game Rules "turnStarted" (Look For Game End asks whether the game is over; if it goes on, Make Computer Move asks whether it is the Computer's Turn). gameRules: the Game Rules Object.
boardStateData StructureBoard State- gameRules
gameRules
gameRules.boardState.checkSquare != "" && dataCount('Move History') > 0- Table
Move History- Row
dataCount('Move History') - 1- Column
"checkMark"- Value
data('Words', 'Check Mark', 'text')
- Applies to
gameRules- Custom Event
turnStarted
Now the +. Start The Turn begins every Turn by asking Look For Check whether the Side to move is in Check. If its checkSquare isn’t empty, the last Move gave Check. Step 3 writes the Words’ Check Mark into that Move’s row.
Mind the two names. Start The Turn is this Function. Start Turn, two pieces up, is the Logic Component that calls it.
12.6.8 Look For Game End
Ends the game at the start of every Turn (Start Turn sends "turnStarted") when the Side to move has no Legal Move (Is Out Of Moves), when neither Side can give Checkmate (Is Material Insufficient), when the same position has come timesSeenForDraw times (Record Position, Is Position Repeated), or when fiftyMoveCountForDraw Moves in a row have had no Capture and no Pawn Move (the fifty-move rule; Make Move counts them). The status line shows the result (Show Result), a Checkmate is marked # in the Move History, and the game is over (Game State Game Over), so the Board no longer answers and the final position stays. Otherwise it tells the Game Rules "gameContinues", and the Turn goes on. timesSeenForDraw: 3, threefold repetition. fiftyMoveCountForDraw: 100, fifty Moves by each Side.
Any Objectself.boardState- Action Name
outOfMoves- gameRules
self
- Action Name
material
- gameRules
self
- Action Name
repetition- timesForDraw
@self.timesSeenForDraw
outOfMoves.value == true || material.value == true || repetition.value == true || self.boardState.fiftyMoveCount >= @self.fiftyMoveCountForDraw- gameRules
self- isOutOfMoves
outOfMoves.value- isMaterialInsufficient
material.value- isRepeated
repetition.value
outOfMoves.value == true && self.boardState.checkSquare != "" && dataCount('Move History') > 0- Table
Move History- Row
dataCount('Move History') - 1- Column
"checkMark"- Value
data('Words', 'Checkmate Mark', 'text')
- Game State
Game Over
- Custom Event
gameContinues
And the #. A Checkmate is a Check with no way out. Look For Game End already knows both halves. If the Side to move has no Legal Move and its King is in Check, the new step 8 writes the Checkmate Mark over the +.
Run the game, and play a few Moves. It plays exactly as before. That’s right. The names are in the Move History, out of sight, and the next part brings them out. If one comes out wrong there, Name The Move is the place to look.
12.7 The Move List
Now the menu shows the game, twelve full Moves at a time.
12.7.1 Board State and Move Notation
What the Game Rules remember about the game besides the Position table. sideToMove: the Side whose Turn it is (White moves first). selected: the Square of the selected Piece (empty when none is selected). lastFrom and lastTo: the two Squares of the last Move (empty before the first Move). checkSquare: the Square of the King in Check (empty when the Side to move is not in Check). enPassantSquare: the Square a Pawn has just passed over with a two-Square Move, where an enemy Pawn may take it en passant on the very next Move (empty otherwise). promotionSquare: while the Promotion choice is open, the Square the selected Pawn will be promoted on (empty otherwise). playerSide: the Side the Player plays (White unless chosen otherwise), so the end of the game can say who won. fiftyMoveCount: the Moves made since the last Capture or Pawn Move, both Sides counted, for the fifty-move rule. isFlipped: whether the Board is drawn turned over, Black at the bottom (false: White at the bottom, a1 at the bottom-left). strength: the Strength the Computer plays at (Gentle, Casual, Club, Strong, or Expert; Casual unless chosen otherwise). chosenSide: while the New Game panel is open, the Side it shows as chosen. chosenStrength: while the New Game or the Settings panel is open, the Strength it shows as chosen. isShowingThreats: whether Threats mode is on (the Threats button; kept on this device). inspected: in Threats mode, the Square of the Piece the Player last clicked to look at it, of either Side (empty when none is inspected). boardFinish: the Board Finish shown, a row of the Board Finishes table (Walnut unless chosen otherwise). pieceSet: the Piece Set shown, a row of the Piece Sets table (Carved walnut unless chosen otherwise). firstShownMove: the first full Move the Game menu's Move List shows, counted from 0 (0 is Move 1); set when the menu opens and by Earlier and Later (Move Pages). movesPerPage: how many full Moves one page of the Move List shows (12: the list has 12 lines).
The words of one Move in the Game menu's Move List, as chess players write it. number: the full Move it belongs to (1 is the first). The other fields are the Move History's columns of the same names: letter (the Kind's letter, or O-O and O-O-O), fromFile and fromRank (only when needed), capture (x), square, promotion (=Q), checkMark (+ or #).
Board State’s card opens with a long Description again, so read past it to the green lines. Board State gets two fields at the end. The first, firstShownMove, is the first full Move the page shows, counted from 0, so 0 is Move 1. The second, movesPerPage, is 12, one for each line of the list.
Move Notation holds the words of one Move: its number, and the Move History’s seven columns of the same names.
12.7.2 Move Words
Window / UICenterUIX 140, Y 26TextGame Menu{number}. {letter}{fromFile}{fromRank}{capture}{square}{promotion}{checkMark}18Medium#503A2BWrap to Width140Move NotationMove Words writes one Move. Its Text is a template, like the Hint Message’s in Chapter 8, and each {name} shows a field of its own Move Notation. For the game’s first Move, 1. e4, the number is 1, the letter is empty, and the square is e4. Its Text Bounds is Wrap to Width, 140 pixels. With it, the words start at the left edge of their box, like a column in a table.
12.7.3 Show Recorded Move
Shows one Move of the game in the Game menu's Move List, as chess players write it, read from its row of the Move History into this Object's own Move Notation, which its words show. Each line of the list is two placements, White's Move and Black's. When the page changes ("movePageChanged", from the Game Rules), the Move shown is the line's full Move on that page; a line past the newest Move shows nothing. line: the line of the list, 0 (top) to 11. isBlackMove: true for the placement that shows Black's Move of the line (false: White's, with the move number).
Any Objectself.moveNotationUI Interaction- Name
row- Value
0
- Variable
row- Value
(other.boardState.firstShownMove + self.line) * 2
self.isBlackMove == true- Variable
row- Operator
+=- Value
1
row < dataCount('Move History')- Component Value
self.moveNotation.number- Value
objectComponentProperty("otherObject","boardState","firstShownMove") + instanceVar("self","line") + 1
- Component Value
self.moveNotation.letter- Value
data('Move History', row, 'letter')
- Component Value
self.moveNotation.fromFile- Value
data('Move History', row, 'fromFile')
- Component Value
self.moveNotation.fromRank- Value
data('Move History', row, 'fromRank')
- Component Value
self.moveNotation.capture- Value
data('Move History', row, 'capture')
- Component Value
self.moveNotation.square- Value
data('Move History', row, 'square')
- Component Value
self.moveNotation.promotion- Value
data('Move History', row, 'promotion')
- Component Value
self.moveNotation.checkMark- Value
data('Move History', row, 'checkMark')
- Visible
No
Show Recorded Move fills one Move Words from the Move History. Each line of the list is two placements, White’s Move and Black’s. White moves first, so rows 0 and 1 are Move 1, rows 2 and 3 are Move 2, and so on.
So step 2 takes the page’s first Move and adds the line. Doubled, that’s White’s row. Step 4 adds one for Black. If that row exists, steps 6 to 13 copy it in, and the words show. A line past the newest Move hides.
Step 6 reads firstShownMove too, written the long way: objectComponentProperty("otherObject","boardState","firstShownMove") is other.boardState.firstShownMove. It’s only a longer spelling, so no rule was missed, and you can type it as the card shows it.
Steps 14 and 16 show and hide the words. The first Set UI Element Visibility keeps its starting Visible, on. The other takes No, as the card shows. The note uses them too.
Add Move Notation to its Data Structures, and UI Interaction to its Components. The Game Rules are its Other. They send movePageChanged and keep the page in Board State, ready for every line to read. Two Per Instance Variables tell the placements apart: line, 0 at the top to 11, and isBlackMove.
12.7.4 No Moves Note and Show No Moves Note
Window / UICenterUIX 300, Y 26TextGame MenuNo moves yet.18Italic#503A2BCenterShows "No moves yet." in the Game menu's Move List while no Move has been made, and hides it once there is one, each time the page is drawn ("movePageChanged", from the Game Rules).
Any ObjectUI InteractiondataCount('Move History') == 0- Visible
No
A new game has an empty list. The No Moves Note says so, No moves yet., in italic. Show No Moves Note shows it while the Move History has no rows, and hides it once there’s one.
12.7.6 Move Pages
What Move Pages remembers on the Game Rules. newestMove: the newest full Move, counted from 0. newestPage: the page that holds it, counted from 0.
Chooses which full Moves the Game menu's Move List shows, movesPerPage at a time (Board State's firstShownMove): the page that holds the newest Move when the menu opens ("gameMenuOpened"), the page before when Earlier is pressed ("earlierMovesPressed"), and the page after when Later is pressed ("laterMovesPressed"). Each time it tells everyone "movePageChanged", so the list shows the page.
Any Objectself.boardStateself.movePagesData- Component Value
self.boardState.firstShownMove- Value
0
dataCount('Move History') > 0- Component Value
self.movePagesData.newestMove- Value
floor((dataCount('Move History') - 1) / 2)
- Component Value
self.movePagesData.newestPage- Value
floor(self.movePagesData.newestMove / self.boardState.movesPerPage)
- Component Value
self.boardState.firstShownMove- Value
self.movePagesData.newestPage * self.boardState.movesPerPage
- Custom Event
movePageChanged
- Component Value
self.boardState.firstShownMove- Operator
-=- Value
self.boardState.movesPerPage
- Custom Event
movePageChanged
- Component Value
self.boardState.firstShownMove- Operator
+=- Value
self.boardState.movesPerPage
- Custom Event
movePageChanged
Move Pages keeps the page in Board State. Its newestMove and newestPage wait in Move Pages Data, added to its Data Structures as movePagesData. When the menu opens, it finds the page with the newest Move. Step 3 halves the last row, rounded down by floor, to get the newest full Move. Step 4 divides that by movesPerPage to get its page, and step 5 turns the page back into its first Move.
Earlier takes movesPerPage off, and Later adds it. Each Event ends with movePageChanged, and every line, the note and both buttons draw the page.
The Values read self.boardState.movesPerPage, the number of lines the page holds, as the card shows it.
Now attach Move Pages to the Game Rules, the last Logic Component it gets. Remember gameMenuOpened, from the Game menu? This is who it was for.
The last pair of buttons turns the pages, Earlier back and Later forward. Make their two variants from Panel Button before placing them. Each one adds two jobs. Its own Report sends the press, and Show Page Button shows it only with a page to turn to. Earlier sets the Shared Variable direction to -1, and Later keeps its 1. Their bases give them the tan.
-1Now place the Move List, the next twenty-nine lines of PLACE THESE. They’re back on the Set up card, from the first Move Words down to Later Moves Button. It’s the longest stretch of placing in the book, and it goes faster once you see the pattern.
The Move Words come in pairs, one pair for each line. White’s is at X -60, and Black’s at X 90. Each pair sits 26 pixels below the one before, from Y 260 down to -26. Each placement sets its line, from none on the first pair (it stays 0) to 11 on the last. White’s placements keep the Object’s words. Black’s type the same template without the number, and switch isBlackMove on. Type it once, then copy it and paste it into the rest of Black’s.
The No Moves Note goes in the spot of the first line, and two Panel Labels above, the headings White and Black. Run the game, and start a new game. Press ••• before you move, and No moves yet. sits under the headings. Close it, play three Moves, and open it again. Your Moves are there, numbered, the way a chess player would write them.
12.8 Playing From the Keyboard
Now the last piece: no mouse at all.
12.8.1 Focus Mark
X 3.75, Y 3.7513Focus OutlineX 3.75, Y 3.75Square InfoKeyboard FocusKeyboard Focus DataThe Focus Mark is an amber outline, one Square in size, 3.75 meters. Unlike the panels, it lives in the world, on the Board, at a Scene Depth Draw Order of 13.
It carries a Square Info, like every Square. That’s the trick of this part. To Select and Move, the Focus Mark looks like a Square. It says squareClicked, and Select and Move reads its name exactly as it reads a clicked Square’s.
12.8.2 Move The Focus Mark
Moves the Focus Mark to where its Square (its Square Info) is drawn now, as Place On Board moves a Piece (Square Position). focusMark: the Focus Mark. isFlipped: whether the Board is drawn turned over. squareMeters, boardLeft and boardBottom: the Board's size and corner, as Place On Board has them.
squareInfoData StructureSquare Info- Action Name
focusedSquare- square
focusMark.squareInfo.name- isFlipped
isFlipped- squareMeters
squareMeters- boardLeft
boardLeft- boardBottom
boardBottom
- Applies to
focusMark- X
focusedSquare.x- Y
focusedSquare.y
Move The Focus Mark puts the outline on its Square. Square Position, from Chapter 1, turns a Square’s name into a place on the Board, flipped or not. Set Position takes it there.
12.8.3 Step The Focus
Moves the focus by a number of Squares on the screen, and stops at the Board's edges: the Focus Mark's Square becomes the one reached, the mark is shown, and it moves there (Move The Focus Mark). focusMark: the Focus Mark. fileStep: Squares to the right; rankStep: Squares up (negative: left, down). isFlipped: whether the Board is drawn turned over. squareMeters, boardLeft and boardBottom: the Board's size and corner.
squareInfoData StructureSquare Info- Name
across- Value
0
- Name
up- Value
0
- Name
file- Value
0
- Name
rank- Value
0
- Name
row- Value
0
- Variable
across- Value
fileStep
- Variable
up- Value
rankStep
isFlipped == true- Variable
across- Operator
*=- Value
-1
- Variable
up- Operator
*=- Value
-1
- Variable
file- Value
data('Position', focusMark.squareInfo.name, 'file')
- Variable
file- Operator
+=- Value
across
- Variable
file- Value
clamp(file, 1, 8)
- Variable
rank- Value
data('Position', focusMark.squareInfo.name, 'rank')
- Variable
rank- Operator
+=- Value
up
- Variable
rank- Value
clamp(rank, 1, 8)
- Variable
row- Value
(rank - 1) * 8 + (file - 1)
- Component Value
focusMark.squareInfo.name- Value
dataRow('Position', row)
- Applies to
focusMark- Visible
true
- focusMark
focusMark- isFlipped
isFlipped- squareMeters
squareMeters- boardLeft
boardLeft- boardBottom
boardBottom
Step The Focus moves the focus by a number of Squares, counted on the screen. Right is always right, the way you see it.
That’s why steps 6 to 10 come first, after the Declares. On a flipped Board, File a is on the right and Rank 1 at the top. So a step right on the screen is a step toward File a, and both steps turn round. Steps 9 and 10 multiply by -1 with the Operator *=, in the same list as = and +=.
Next, clamp keeps the new File and Rank between 1 and 8. That’s the Board’s edge. Press Left on File a, and the focus stays put. Step 17 turns File and Rank into a row of Position. Position’s rows run a1 to h1, then a2 to h2, eight to a Rank. So (rank - 1) * 8 counts the Ranks below, and file - 1 the steps along. Step 18 names the Square. Last, the outline shows and moves.
12.8.4 Keyboard Focus
What Keyboard Focus remembers on the Focus Mark. isFlipped: whether the Board is drawn turned over, so the arrow keys move the focus the way the screen shows.
Lets the Player play with the keyboard: keeps the focused Square (this Object's Square Info) and draws the Focus Mark on it. The arrow keys (Move Focus) move the focus one Square on the screen, Home and End (Focus Row Start, Focus Row End) to the ends of its row, always stopping at the Board's edges, and the way the Board is drawn turns them round ("boardFlipped", from the Game Rules). Enter or Space (Press Square) presses the focused Square as a click does ("squareClicked"), while the game is being played. The focus starts on the Square in front of the Player's King when each game begins ("gameStarted", from the Game Rules), and shows itself from the first key. These keys are the Input Context Default: while the Promotion choice is open they rest, and the focus stays where it is (Choose With Keys turns them off and the choice's own keys on). startFile: the File the focus starts on (5: File e). squareMeters, boardLeft, boardBottom: the Board as Place On Board draws it.
Any Objectself.squareInfoself.keyboardFocusData- Name
row- Value
0
- Name
rank- Value
0
- Component Value
self.keyboardFocusData.isFlipped- Value
other.boardState.isFlipped
- Variable
rank- Value
data('Sides', other.boardState.playerSide, 'pawnRank')
- Variable
row- Value
(rank - 1) * 8 + (@self.startFile - 1)
- Component Value
self.squareInfo.name- Value
dataRow('Position', row)
- focusMark
self- isFlipped
self.keyboardFocusData.isFlipped- squareMeters
@self.squareMeters- boardLeft
@self.boardLeft- boardBottom
@self.boardBottom
- Component Value
self.keyboardFocusData.isFlipped- Value
other.boardState.isFlipped
- focusMark
self- isFlipped
self.keyboardFocusData.isFlipped- squareMeters
@self.squareMeters- boardLeft
@self.boardLeft- boardBottom
@self.boardBottom
- focusMark
self- fileStep
-1- rankStep
0- isFlipped
self.keyboardFocusData.isFlipped- squareMeters
@self.squareMeters- boardLeft
@self.boardLeft- boardBottom
@self.boardBottom
- focusMark
self- fileStep
1- rankStep
0- isFlipped
self.keyboardFocusData.isFlipped- squareMeters
@self.squareMeters- boardLeft
@self.boardLeft- boardBottom
@self.boardBottom
- focusMark
self- fileStep
0- rankStep
1- isFlipped
self.keyboardFocusData.isFlipped- squareMeters
@self.squareMeters- boardLeft
@self.boardLeft- boardBottom
@self.boardBottom
- focusMark
self- fileStep
0- rankStep
-1- isFlipped
self.keyboardFocusData.isFlipped- squareMeters
@self.squareMeters- boardLeft
@self.boardLeft- boardBottom
@self.boardBottom
- focusMark
self- fileStep
-7- rankStep
0- isFlipped
self.keyboardFocusData.isFlipped- squareMeters
@self.squareMeters- boardLeft
@self.boardLeft- boardBottom
@self.boardBottom
- focusMark
self- fileStep
7- rankStep
0- isFlipped
self.keyboardFocusData.isFlipped- squareMeters
@self.squareMeters- boardLeft
@self.boardLeft- boardBottom
@self.boardBottom
- Action Name
isPlaying- Game State
Playing
isPlaying.value == true- Visible
Yes
- Custom Event
squareClicked
Keyboard Focus remembers the Board’s direction in Keyboard Focus Data. Add it to Data Structures with the slot name keyboardFocusData, so the calls can read that direction. It has nine Events, and most are one call. When a game starts, the focus goes to File e of your Pawns’ Rank, in front of your King: e2 for White, e7 for Black. When the Board flips, it follows its Square to its new place.
Each arrow key is its own 2D Direction Pressed Event, for the Command Move Focus. Choose Move Focus from the Command list. The next window chooses the direction. Pick the card’s Left, Right, Up or Down, and that key gets its own Event. Each calls Step The Focus with one step. Home and End are Command Pressed Events, and they step seven Squares. A row has eight Squares, so seven steps always reach its end, and the clamp stops it there.
Enter or Space is Press Square. It asks first whether the game is being played. On the Computer’s Turn, or after the end, a key counts no more than a click would. Then it shows the outline and sends squareClicked.
One thing surprises people. The outline is hidden until you press a key. Then it appears and moves, both at once.
12.8.5 New Game
Starts every game with the Side and the Strength the Player chose last, and lets the Player start a new game. When the game begins (Game Start, before the first Turn), it takes the choices saved on this device (the Strength through Use The Last Strength, which it uses again when a saved game has been brought back, "savedGameLoaded") (Persistent Values), when the game has them (the Sides and Strengths tables; otherwise, and the first time, Board State's own playerSide and strength), turns the Board over for a Player who plays Black, and tells everyone "gameStarted". When the Player presses New Game ("newGamePressed"), it opens the New Game panel, a menu that holds the game while it is open, showing the current choices as chosen and asking "Replace the current game?" when a game is in progress and has Moves. Each Side or Strength pressed ("sideChosen", "strengthChosen"; the button is its Other) becomes the chosen one. Play ("playPressed") saves the choices, forgets the saved game (the slot "auto"), and restarts the game, which starts again from the beginning with them.
Any Objectself.boardState- Action Name
savedSide- Key
"playerSide"- Default
self.boardState.playerSide
- Action Name
isKnownSide- Table
Sides- Row
savedSide.value
isKnownSide.value == true- Component Value
self.boardState.playerSide- Value
savedSide.value
- gameRules
self
self.boardState.playerSide == "Black"- Component Value
self.boardState.isFlipped- Value
true
- Custom Event
gameStarted
- Component Value
self.boardState.chosenSide- Value
self.boardState.playerSide
- Component Value
self.boardState.chosenStrength- Value
self.boardState.strength
- Menu Element
New Game Panel
- Menu Element
New Game Panel
- Action Name
isOver- Game State
Game Over
self.boardState.lastFrom != "" && isOver.value == false- Element
Replace Question
- Element
Replace Question- Visible
No
- Custom Event
choiceChanged
- Component Value
self.boardState.chosenSide- Value
other.panelOption.option
- Custom Event
choiceChanged
- Component Value
self.boardState.chosenStrength- Value
other.panelOption.option
- Custom Event
choiceChanged
- Action Name
persistentWriteResult- Key
"playerSide"- Value
self.boardState.chosenSide
- Action Name
persistentWriteResult2- Key
"strength"- Value
self.boardState.chosenStrength
- Slot
"auto"
- gameRules
self
New Game gets one step at the end of When the Game Starts. Once your Side is known, gameStarted tells the Focus Mark which Square to start on, e2 or e7.
12.8.6 New Game Panel and Settings Panel
Window / UICenterUIX 600, Y 420TextNew GameCancel30Square Highlight#EFE6CFX 600, Y 420New game26Semibold#503A2BCenterX 0, Y 170Window / UICenterUIX 600, Y 620TextSettingsCancel30Square Highlight#EFE6CFX 600, Y 620Settings26Semibold#503A2BCenterX 0, Y 270The two panels of earlier chapters get the Cancel Command too. Pick Cancel in each UI Interaction. Now Escape closes every panel in the game, as its Cancel or Done does.
12.8.7 Promotion Focus and Move The Promotion Focus
What one button of the Promotion panel stands for. kind: the Kind the Pawn becomes (Queen, Rook, Bishop, or Knight); empty for the button that cancels the Move. The Promotion Focus carries one too: the Piece the keys are on (Choose With Keys).
Window / UICenterUIX 134, Y 158TextPromotion22Promotion OutlineX 134, Y 158Promotion ChoiceChoose With KeysChoose With Keys DataPuts the Promotion Focus on the Piece at one place of the Promotion choice: its Promotion Choice becomes that Kind, and its outline is drawn over that Piece's button. promotionFocus: the Promotion Focus. place: 1 to 4, the Queen, Rook, Bishop and Knight, as their buttons stand from left to right. firstPieceX and pieceY: where the Queen's button stands, in screen pixels right of and above the middle of the screen; pieceSpacing: how far apart the buttons stand.
promotionChoiceData StructurePromotion Choice- Component Value
promotionFocus.promotionChoice.kind- Value
dataRow('Kinds', place)
- Applies to
promotionFocus- Position Relative To
Screen- X
firstPieceX + (place - 1) * pieceSpacing- Y
pieceY
The Promotion choice needs keys of its own. A Pawn reaches the last Rank, and four Pieces wait in a row. The Promotion Focus is a rounded outline around one of them, in the Promotion UI Group. It shows and hides with the choice. Its Element Type is Text, so a click on the Piece under it still reaches that Piece’s button.
It carries a Promotion Choice, like the four buttons, and its kind is the Piece it’s on. On Promotion Choice’s card, only the Description changes, to say so. Nothing reads a Description, so there’s nothing to build there.
Move The Promotion Focus puts it on one of four places, 1 for the Queen to 4 for the Knight. Its dataRow reads the Kinds table by number. Counted from Row 0, the way the card counts, rows 1 to 4 are Queen, Rook, Bishop and Knight, the buttons’ own order. In the editor’s numbers, that’s rows 2 to 5.
Then Set Position moves the outline on the screen, 130 pixels for each place. Its Position Relative To is a list. Pick Screen, so X and Y count in pixels from the middle of the screen, as the Description says.
12.8.8 Choose With Keys
What Choose With Keys remembers on the Promotion Focus. place: which Piece of the Promotion choice the outline is on, 1 (the Queen) to 4.
Lets the Player choose the Promotion with the keyboard. While the Promotion choice is open the keys choose among its Pieces instead of playing on the Board: the Input Context Promotion Choice is on and Default (the Board's keys) is off, turned when the choice opens or closes and when a saved game comes back ("positionChanged", from the Game Rules). Left and Right (the Command Choose Piece) move this outline from Piece to Piece, from the Queen to the Knight; Enter or Space (Promote) promotes the Pawn to the Piece it is on: this Object carries that Kind in its Promotion Choice, so the Game Rules (Choose Promotion) hear "promotionChosen" from it as from a button. The outline starts on the Queen each time the choice opens. firstPieceX and pieceY: where the Queen's button stands, in screen pixels right of and above the middle of the screen; pieceSpacing: how far apart the buttons stand.
Any Objectself.promotionChoiceself.chooseWithKeysData- Action Name
isChoosing- Game State
Promotion
isChoosing.value == true- Input Context
Default- Active
No
- Input Context
Promotion Choice
- Component Value
self.chooseWithKeysData.place- Value
1
- promotionFocus
self- place
self.chooseWithKeysData.place- firstPieceX
@self.firstPieceX- pieceY
@self.pieceY- pieceSpacing
@self.pieceSpacing
- Input Context
Promotion Choice- Active
No
- Input Context
Default
- Component Value
self.chooseWithKeysData.place- Value
clamp(self.chooseWithKeysData.place - 1, 1, 4)
- promotionFocus
self- place
self.chooseWithKeysData.place- firstPieceX
@self.firstPieceX- pieceY
@self.pieceY- pieceSpacing
@self.pieceSpacing
- Component Value
self.chooseWithKeysData.place- Value
clamp(self.chooseWithKeysData.place + 1, 1, 4)
- promotionFocus
self- place
self.chooseWithKeysData.place- firstPieceX
@self.firstPieceX- pieceY
@self.pieceY- pieceSpacing
@self.pieceSpacing
- Custom Event
promotionChosen
Choose With Keys switches the keys. Choose With Keys Data holds place, added to Data Structures as chooseWithKeysData. The button positions are constant Shared Variables. On every positionChanged, it asks whether the Promotion choice is open. If it is, Set Input Context Active turns Default off, then Promotion Choice on. Steps 4 and 9 have no Active line. Active starts on, and that’s what they need. The arrow keys now mean the four Pieces, and the outline starts on the Queen. When the choice closes, it switches back.
Left and Right step place, and clamp keeps it from 1 to 4, the Queen to the Knight. Enter or Space sends promotionChosen. Choose Promotion reads this Object as its other, so it gets the Kind the outline is on, as if you’d clicked that button.
12.8.9 Cancel With Escape
Lets Escape (the Command Cancel Choice) cancel the Promotion choice, as a click on its Cancel button does: the button tells whoever listens (the Game Rules) "promotionChosen", and the listener reads its Promotion Choice, empty for Cancel, as Other. Cancel Choice is one of the Promotion choice's own keys, live only while the choice is open (Choose With Keys turns them on).
Any Object- Custom Event
promotionChosen
Window / UICenterUIX 160, Y 40Promotion21Square Highlight#E2D5B4X 160, Y 40Cancel18Semibold#503A2BCenterPromotion ChoiceThe Promotion choice isn’t a menu. It’s part of the game, so no Cancel Command closes it. Its Cancel button answers Escape itself. Cancel With Escape sends promotionChosen. The button’s Promotion Choice is empty, and Choose Promotion takes an empty Kind as Cancel. Attach it to Cancel Promotion.
Last, the two placements: the Focus Mark on e2, and the Promotion Focus on the Queen’s button. Both start hidden.
Run the game. Press New Game, choose White, and press Play. Now leave the mouse alone. Press Up. The amber outline appears on e3, one Square up from e2. Press Down, then Enter. Your e-Pawn’s Square turns gold, and its dots appear. Press Up twice, then Enter. The Pawn moves to e4.
Your game plays from the keyboard now, from the first Move to the last.
Watch for each of these.
- Press •••. The game dims, and the Game menu opens with the headings White and Black, and No moves yet.
- Play a few Moves, then press ••• again. Each Move is written down, the number before White’s Move: 1. e4, with Black’s reply beside it.
- Capture something with a Pawn. Its name has an x and its own File, such as exd5. Give a Check, and the Move ends in +.
- Press Escape. The Game menu closes, as Done would. Escape closes New Game, Settings, How to play and Resign? the same way.
- In the Game menu, press How to play. Three short paragraphs explain selecting a Piece, Threats mode, and how to play the whole game with the keyboard.
- Press Resign, then Cancel, and the game goes on. Resign again, and confirm. You lose · Resignation appears, and Resign fades in the menu.
- Start a new game, and press an arrow key. The outline appears, moving one Square from e2, and it stops at the Board’s edges. Home and End take it to the ends of its row.
- Flip the Board. Right still moves the outline right on the screen.
- Move a Pawn to its last Rank with the keys, and the outline waits on the Queen. Left and Right move it, and Enter promotes. Or press Escape instead, and the choice closes with the Pawn still selected.
The ••• button opens a menu that holds the game. Every Move is named as it’s made, before the Board changes, and its + or # is added when the next Turn starts. The Move List reads the names from the Move History, twelve full Moves to a page. The Focus Mark carries a Square Info and sends squareClicked, and two Input Contexts take turns with the keys.
Underneath, nothing in this chapter changed a rule. A key sends the message a click sends. Escape does what Cancel does, and Enter on the outline what a click on its button does. When a new way to play arrives, give it a door the game already has.
12.9 If It Didn’t Work
This chapter touches pieces from every part of the game. Run it after each part, and the trouble stays small.
The ••• button does nothing? Its placement should pick Game Menu Button, whose Logic is Report Game Menu. Is Game Menu attached to the Game Rules?
The Game menu opens, but the Board still takes clicks under it? Its Panel Backdrop needs the Group Game Menu, on its placement.
Resign never fades? Look at Ask To Resign’s On Position Changed. Can Resign and Show Button On Or Off both need joining, with canUse.value handed to isOn.
The list always says No moves yet.? Perhaps Move Pages isn’t attached, or Game Menu’s step 3 sends to another Object. It should go to its own, with Applies to left on Self.
Every line shows the same Move? Each pair of placements needs its own line, 1 to 11. A Black Move that starts with a number has White’s template, so type Black’s without {number}. and switch its isBlackMove on.
A Knight’s Move reads f3 with no N, or a name comes out empty? Look at the three Move makers. Record Move must come before Make Move. After the Move, from is an empty Square, and Name The Move finds no Piece there to name.
Record Move sits outside the Else in Select and Move? Cut that call. Right-click step 10’s Else, choose Paste, then join the call first on its line, before Make Move. The three steps should now match their card.
The arrow keys do nothing? Open the Input Manager. The five Commands must be in Default, spelled as the card has them. Perhaps a panel is still open, and keys rest while a menu holds the game.
Right moves left on a flipped Board? Look at steps 8 to 10 of Step The Focus, where both Changes multiply by -1 with the Operator *=.
The Promotion keys move across the Board instead? Promotion Choice needs its three Commands, and Choose With Keys must be on the Promotion Focus. If the outline sits beside the Pieces, compare firstPieceX, pieceY and pieceSpacing with where Chapter 4 placed the four buttons.
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 the finished game, if you’d like to compare.
The recipe ends here, but your game doesn’t have to. What would you add to it? Perhaps a clock for each Side, counting down, or a row of captured Pieces beside the Board. Or a second Player at the same keyboard, with the Computer resting. Every one of those is built from the same blocks you’ve used for twelve chapters.
Or close this project and start a new one. A game no one has made yet, of your own. You know how to begin now: an empty page, one table, and the first piece of Logic.
Still stuck? Search the manual, or ask in the Myth Engine Discord (opens a new tab).
In Chapter 1, the page was empty. It was the color of paper, with nothing on it. Then you laid out a Board, Square by Square, and set thirty-two Pieces on it.
Since then, the game has learned whose Turn it is and which Moves are legal. It knows when a King is lost, and when nobody can win. It writes down every Move. The Computer’s thinking came from outside. Every rule that judges its Moves, and yours, you built with your own hands.
Play it to the end. When the last Checkmate comes, the game will say so under the Board, in the words you gave it.
Free demo
Free
- No time limit
- Build, save and playtest your games
- Follow every game guide and build its game
Publishing needs the full version.
Try the free demoFull version
$84.99 USD, once
No subscription, no paid upgrades, every update included.
Everything in the free demo, plus:
- Publish your game as one HTML file, a web folder, or desktop and mobile projects you build and sign
- Share it or sell it, with no royalties, however much it earns
- Let players build their own version of your game, within the parts you allow (Builder Mode)
Windows 10/11, 64-bit · One person, on computers you own or control · Checkout and downloads through itch.io · Made by 3D Mage
License agreementMac, refunds and other questionsWhy pay for Myth Engine when free engines exist?
