- Learn
- Build a Game
- Chess
- 5 The End of the Game
5 The End of the Game
By the end of this chapter, a game of chess can end. Checkmate, Stalemate and the three Draws by rule each say so under the Board, and the last position stays where it is, with nothing left to move.
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 6 from it.
To open it, start Myth Engine, choose File › Open Project... and pick the downloaded file.
5.1 The Idea
Play your Board from Chapter 4 for a moment. Push White’s f2 Pawn to f3, and Black’s e7 Pawn to e5. Then g2 to g4. Black’s Queen sweeps from d8 all the way to h4, and White’s King on e1 turns red. Check.
Click any White Piece, and its Square turns gold with no dots at all. The King has nowhere to go, and nothing can come between. Chess calls that Checkmate, and the game is over. But your game doesn’t know it. It sits there, saying Check, waiting for a Move that can never come.
This chapter teaches the game to notice the end. After every Move, at the start of each Turn, it asks one question. Can this game go on?
There are five ways it can’t. In Checkmate, the Side to move is in Check with no Legal Move, and it loses. In Stalemate, the Side to move has no Legal Move but isn’t in Check. That’s a Draw, a game nobody wins. The other three Draws are rules about the whole game, not one Move:
- Insufficient material: neither Side has enough Pieces left to give Checkmate, such as two Kings alone.
- Threefold repetition: the same position has come round for the third time.
- The fifty-move rule: fifty Moves by each Side with no Capture and no Pawn Move.
Two of these need a memory. The fifty-move rule needs a count of quiet Moves. Threefold repetition needs every position the game has seen. Think of a photo album. At the start of each Turn, the game takes a picture of the Board and puts it in the album. To ask whether a position came before, it leafs back through the pages.
When the game ends, the sign on the door from Chapter 4 turns one more time. It says Game Over, and the Board stops answering.
5.2 The Plan
Here’s how the pieces work together. Read each line from left to right, and come back after The last word.
- A Move is made -> Make Move counts it toward the fifty, and asks Update En Passant.
- Start Turn gives the Turn away -> it sends turnStarted.
- Look For Game End hears it -> Is Out Of Moves, Is Material Insufficient, Record Position and Is Position Repeated each answer.
- Record Position -> Can Take En Passant, then a new page in Position History.
- Is Position Repeated -> Is Same Position compares two pages.
- Any answer says the game is over -> Show Result picks the words, and the Game State becomes Game Over.
You’ll build the memory first, and then Make Move gets tidier. Next come the questions, one Function each, and last the Logic Component that asks them.
5.3 Set Up
Playing, Promotion, Game OverThe Game State list grows by one, Game Over. Add it after Promotion, the way The Set up card says. That’s all. Nothing new goes on the screen in this chapter, because the result will show in the Status Line you already have.
5.4 What the Game Rules Remember Now
Board State gets two more fields.
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), so the end of the game can say who won. fiftyMoveCount: the Moves made since the last Capture or Pawn Move, both Sides counted (the fifty-move rule).
playerSide is the Side you play, so the end can say who won. For now you’re always White. You’ll choose in Chapter 6. fiftyMoveCount counts the quiet Moves, the ones with no Capture and no Pawn Move. Both Sides’ Moves count.
Its Description changes too. If you typed one, the card gives its new words in full.
Words gets six new rows, one for each ending.
Your moveCheckPromotion: choose a PieceYou win · CheckmateYou lose · CheckmateDraw · StalemateDraw · Insufficient materialDraw · Fifty-move ruleDraw · Threefold repetitionAdd them in the card’s order, Win By Checkmate to Draw By Threefold Repetition. The middle dot in each text is part of the words, as in You win · Checkmate. Copy it from the card if your keyboard has none.
Last, the photo album.
One row of the "Position History" Data Table, which follows it. Every position since the last Capture or Pawn Move, recorded at the start of each Turn, so the game can tell when one comes for the third time. Each position takes 64 rows, one per Square in the order of the Position table (a1, b1, ... h8): the first position is rows 0 to 63, the next 64 to 127, and so on. Each row is named by its number. kind and side: the Piece on that Square (empty when none). castlingsAllowed: how many castlings were still allowed then (a castling once ended never comes back, so the same number means the same castlings). The table starts empty.
Position History starts with no rows at all. The game fills it while it plays, so you make only its three fields in Position History Entry. The table gets its columns from them.
Each picture takes 64 rows, one for each Square in Position’s order. So the first picture is rows 0 to 63, and the second starts at row 64, right after it. Its kind and side say what stands on each Square. And the last column? It holds how many castlings are still allowed. Why that? A King that has lost its castling isn’t in the same position, even with every Piece where it was.
5.5 A Tidier Make Move
Make Move carries a lot by now. The en passant half moves into a Function of its own, and Make Move gains the count of quiet Moves.
5.5.1 Update En Passant
You’ve built all of this before. Update En Passant is the en passant part of Chapter 4’s Make Move, word for word. It catches the passed Pawn. It empties the old enPassantSquare, and it writes a new one when a Pawn rushes two Squares.
Keeps en passant up to date for one Move, before the Move is made. A Pawn moving onto the en passant Square takes the enemy Pawn that passed over it. Then the en passant Square is set for the next Move: the Square a Pawn passes over with a two-Square Move, else empty. gameRules: the Game Rules Object, whose Board State holds the en passant Square. from and to: the Squares of the Move (a1 to h8).
boardStateData StructureBoard State- Type
Text- Name
kind
- Name
fromFile- Value
0
- Name
fromRank- Value
0
- Name
toFile- Value
0
- Name
toRank- Value
0
- Name
passedPawn- Value
0
- Name
passedRank- Value
0
- Type
Text- Name
passedSquare
- Variable
kind- Value
data('Position', from, 'kind')
- Variable
fromFile- Value
data('Position', from, 'file')
- Variable
fromRank- Value
data('Position', from, 'rank')
- Variable
toFile- Value
data('Position', to, 'file')
- Variable
toRank- Value
data('Position', to, 'rank')
kind == "Pawn" && to == gameRules.boardState.enPassantSquare- Variable
passedPawn- Value
(fromRank - 1) * 8 + (toFile - 1)
- Table
Position- Row
passedPawn- Column
'kind'- Value
''
- Table
Position- Row
passedPawn- Column
"side"- Value
''
- Component Value
gameRules.boardState.enPassantSquare- Value
''
kind == "Pawn" && abs(toRank - fromRank) == 2- Variable
passedRank- Value
(fromRank + toRank) / 2
- Variable
passedSquare- Value
dataRow('Position', (passedRank - 1) * 8 + (fromFile - 1))
- Component Value
gameRules.boardState.enPassantSquare- Value
passedSquare
Step 16’s Column is 'kind', in its quote marks, as in Chapter 4.
Build it before you take anything out of Make Move, so your game keeps working all the way through. Its eight Declares are the ones Make Move had, and its Parameters are Make Move’s too. You built these steps once already, in Chapter 4.
5.5.2 Make Move
Now Make Move itself.
Makes one Legal Move on the Position table, with everything it brings: the fifty-move rule's count, the Rook's part of castling (Update Castling), en passant (Update En Passant), and the last Move's Squares. gameRules: the Game Rules Object, whose Board State it updates. from and to: the Squares of the Move (a1 to h8).
boardStateData StructureBoard Statedata('Position', from, 'kind') == "Pawn" || data('Position', to, 'kind') != ""- Component Value
gameRules.boardState.fiftyMoveCount- Value
0
- Component Value
gameRules.boardState.fiftyMoveCount- Operator
+=- Value
1
- from
from- to
to
- gameRules
gameRules- from
from- to
to
- from
from- to
to
- Component Value
gameRules.boardState.lastFrom- Value
from
- Component Value
gameRules.boardState.lastTo- Value
to
Here is Make Move’s whole card as it ends this chapter. Most of Chapter 4’s work now lives in Update En Passant.
Take Make Move apart first. Delete its eight Declare blocks first, then old steps 9 to 13, the five Change Variables. Deleting the old step 15 If takes its three steps with it. So does deleting the step 21 If, with 22 to 24. Old step 20 goes too.
Four steps are left: Update Castling, Move Piece, and the two that write the last Move. The new steps go in the middle of that line, in two places.
The four new steps go first, before Update Castling. They count a quiet Move. If the Piece moving is a Pawn, or the Square it lands on holds a Piece, the count goes back to 0. Otherwise it goes up by one. Add the If from the Action List, and use the round handles to join it after Start. It goes ahead of Update Castling. Step 2 goes in its Then, with Add Inside Then. Add its Else, with step 4. A Change Component Value has an Operator list beside its Component Value, as a Change Variable has, and step 4’s is +=. Update Castling stays after the If, on the line from Start.
Step 6, the call to Update En Passant, goes between Update Castling and Move Piece. It’s the same move once more. Add the call apart from the line, and use the round handles to join it after Update Castling. Move Piece stays right behind the call, before the two steps that remember the Move.
Run the game. It plays the way it did. Try the en passant from Chapter 4, e2 to e4, a7 to a6, e4 to e5, and d7 to d5. Your Pawn on e5 can still go to d6, and d5 empties. The count of quiet Moves runs where you can’t see it, and soon something will read it.
5.6 Is There a Move Left?
Now the questions begin. The first one is the most important.
5.6.1 Is Out Of Moves
Is Out Of Moves asks whether the Side to move has any Legal Move at all.
It starts by believing the worst. Its answer, isOutOfMoves, is Yes, and it stays Yes until a single Legal Move proves otherwise. Then it goes down Position, one Square at a time. On each Square that holds a Piece of the Side to move, it asks Find Legal Moves, as a click would. If even one dot appears in Targets, the answer becomes No, and the Squares after that are skipped.
Asks whether the Side to move has no Legal Move at all, which ends the game. gameRules: the Game Rules Object, whose Board State says whose Turn it is and where en passant is possible. isOutOfMoves (returned): true when no Piece of the Side to move has a Legal Move. It leaves the Targets table empty.
boardStateData StructureBoard State- Type
Text- Name
side
- Type
Text- Name
enPassant
- Type
Text- Name
square
- Type
Boolean- Name
isOutOfMoves- Value
true
- Variable
side- Value
gameRules.boardState.sideToMove
- Variable
enPassant- Value
gameRules.boardState.enPassantSquare
dataCount('Position')times- Variable
square- Value
dataRow('Position', local.index)
isOutOfMoves == true && data('Position', square, 'side') == side- from
square- enPassantSquare
enPassant
dataFind('Targets', 'reachable', true) != (-1)- Variable
isOutOfMoves- Value
false
- Value
isOutOfMoves
Step 10’s If is where the skipping happens. Its first test asks whether isOutOfMoves is still true. Once one Move is found, the If says no on every later Square, so Find Legal Moves doesn’t run again.
Step 12 looks for a dot with dataFind('Targets', 'reachable', true). That finds the first row of Targets whose reachable is Yes, and gives -1 when there’s none. Type the whole condition, including the != (-1) after the search.
Clear Targets wipes the dots it made. Nobody sees them. They were only a question. Its RETURN TYPE is Boolean, and its Return hands back isOutOfMoves. Calls read it as value.
5.6.2 Show Result
Show Result turns the answers into words under the Board.
Shows how the game ended in the status line ($status), once the game has ended. When the Side to move has no Legal Move: Checkmate if its King is in Check (the Player wins or loses), else Stalemate (a Draw). Otherwise the game ended as a Draw by rule: insufficient material, else threefold repetition, else the fifty-move rule. gameRules: the Game Rules Object, whose Board State says whose Turn it is, where Check is, and which Side the Player plays. isOutOfMoves: the Side to move has no Legal Move. isMaterialInsufficient: neither Side can give Checkmate. isRepeated: the same position has come for the third time.
boardStateData StructureBoard State- Type
Text- Name
side
- Type
Text- Name
checkSquare
- Variable
side- Value
gameRules.boardState.sideToMove
- Variable
checkSquare- Value
gameRules.boardState.checkSquare
isOutOfMoves == true && checkSquare != "" && side == gameRules.boardState.playerSide- Variable
$status- Value
data('Words', 'Loss By Checkmate', 'text')
isOutOfMoves == true && checkSquare != ""- Variable
$status- Value
data('Words', 'Win By Checkmate', 'text')
isOutOfMoves == true- Variable
$status- Value
data('Words', 'Draw By Stalemate', 'text')
isMaterialInsufficient == true- Variable
$status- Value
data('Words', 'Draw By Insufficient Material', 'text')
isRepeated == true- Variable
$status- Value
data('Words', 'Draw By Threefold Repetition', 'text')
- Variable
$status- Value
data('Words', 'Draw By Fifty Move Rule', 'text')
It’s a staircase of Ifs. Each one sits in the Else of the one before. Read it from the top. Out of Moves, in Check, and it’s your Turn? You lose. Out of Moves and in Check, on the other Side’s Turn? You win. Out of Moves with no Check is Stalemate. After that come the three Draws by rule, in the card’s order.
Each new test is an Else If. Add it with Add Else If, and put the last answer in an Else. The words come from Words. Each row’s name goes in quote marks, as in Look For Check.
5.7 Draws by Rule
Next come the three rules about the whole game. None of them needs a dot.
5.7.1 Is Material Insufficient
Is Material Insufficient counts the Pieces left on the Board that aren’t Kings, and sorts them. It walks Position once, keeping four tallies: every Piece but a King in otherPieces, the Knights, and the Bishops on dark and on light Squares.
Asks whether neither Side has enough Pieces left to give Checkmate, which ends the game as a Draw: only the two Kings; a King and one Knight or one Bishop against a King; or Kings and Bishops whose Bishops all stand on Squares of one shade. isInsufficient (returned): true when that is so.
- Type
Text- Name
kind
- Name
file- Value
0
- Name
rank- Value
0
- Name
squareShade- Value
0
- Name
otherPieces- Value
0
- Name
knights- Value
0
- Name
darkBishops- Value
0
- Name
lightBishops- Value
0
- Type
Boolean- Name
isInsufficient- Value
false
dataCount('Position')times- Variable
kind- Value
data('Position', local.index, 'kind')
- Variable
file- Value
data('Position', local.index, 'file')
- Variable
rank- Value
data('Position', local.index, 'rank')
- Variable
squareShade- Value
(file + rank) % 2
kind != "" && kind != "King"- Variable
otherPieces- Operator
+=- Value
1
kind == "Knight"- Variable
knights- Operator
+=- Value
1
kind == "Bishop" && squareShade == 0- Variable
darkBishops- Operator
+=- Value
1
kind == "Bishop" && squareShade == 1- Variable
lightBishops- Operator
+=- Value
1
otherPieces == 1 && knights == 1- Variable
isInsufficient- Value
true
otherPieces == darkBishops || otherPieces == lightBishops- Variable
isInsufficient- Value
true
- Value
isInsufficient
How does a Square know its shade? Step 14 adds its file and rank and keeps what’s left over after dividing by 2, with %. On a1, 1 and 1 make 2, which leaves 0. So 0 is dark. On b1, 2 and 1 make 3, which leaves 1, and b1 is light.
The two Ifs at the end decide. A lone Knight can’t give Checkmate with only its King to help, and neither can Bishops that all stand on one shade. Look closely at step 25. When only the two Kings are left, otherPieces is 0, and so is darkBishops. 0 equals 0, so bare Kings are a Draw too, with no If of their own.
5.7.2 Can Take En Passant
The photo album has one odd rule. A position where en passant could be played is never the same as one where it can’t. That chance lasts only one Move. So the game asks first: is en passant possible right now?
Asks whether the Side to move may take en passant right now: an enemy Pawn has just passed over the en passant Square, and a Pawn of the Side to move may take it there without leaving its own King attacked. gameRules: the Game Rules Object, whose Board State says whose Turn it is and where en passant is possible. canTake (returned): true when such a Move is a Legal Move. It leaves the Targets table empty.
boardStateData StructureBoard State- Type
Text- Name
side
- Type
Text- Name
enPassant
- Type
Text- Name
square
- Type
Boolean- Name
canTake- Value
false
- Variable
side- Value
gameRules.boardState.sideToMove
- Variable
enPassant- Value
gameRules.boardState.enPassantSquare
dataCount('Position')times- Variable
square- Value
dataRow('Position', local.index)
enPassant != "" && data('Position', square, 'kind') == "Pawn" && data('Position', square, 'side') == side- from
square- enPassantSquare
enPassant
dataFind('Targets', 'reachable', true) != (-1)- Variable
canTake- Value
true
- Value
canTake
Can Take En Passant works like Is Out Of Moves, smaller. It goes down Position, and for each Pawn of the Side to move, it asks Find En Passant Move. Its step 12 is the one difference in shape. It sits after the Repeat, not inside it, so it waits until the Repeat is done, and only then asks whether any dot appeared. If one did, canTake is true. It clears Targets before it starts and after it ends, so no dot is ever left behind for you to see. The Board never shows its question.
5.7.3 Record Position
Record Position puts one picture in the album, at the start of each Turn.
Adds the position on the Board to the Position History table at the start of a Turn: for every Square, the Piece on it and how many castlings are still allowed. After a Capture or a Pawn Move (and at the start of a game) the table starts over, because no earlier position can come again. A position in which en passant can be taken is not recorded: en passant is possible only on the very next Move, so that position can never come again either. gameRules: the Game Rules Object, whose Board State holds the fifty-move rule's count, whose Turn it is, and the en passant Square.
boardStateData StructureBoard State- Name
castlingsStillAllowed- Value
0
- Type
Text- Name
square
- Name
lastRow- Value
0
gameRules.boardState.fiftyMoveCount == 0- Table
Position History
- Action Name
enPassantLook- gameRules
gameRules
dataCount('Castling')timesdata('Castling', local.index, 'isAllowed') == true- Variable
castlingsStillAllowed- Operator
+=- Value
1
enPassantLook.value != truedataCount('Position')times- Variable
square- Value
dataRow('Position', local.index)
- Action Name
addedRow- Table
Position History- Row
dataCount('Position History')
- Variable
lastRow- Value
dataCount('Position History') - 1
- Table
Position History- Row
lastRow- Column
"kind"- Value
data('Position', square, 'kind')
- Table
Position History- Row
lastRow- Column
"side"- Value
data('Position', square, 'side')
- Table
Position History- Row
lastRow- Column
"castlingsAllowed"- Value
castlingsStillAllowed
It begins with a clean-out. When fiftyMoveCount is 0, the last Move was a Capture or a Pawn Move, or the game has only begun. No position from before it can ever come back, because a Pawn never moves backward and a captured Piece never returns. So Reset Data Table empties the album. It starts fresh.
Next, it asks Can Take En Passant, and counts the castlings still allowed. When en passant is possible, there’s no picture this Turn. Otherwise, for each of the 64 Squares, Control Data Row adds a row at the end of the table. Its Row, dataCount('Position History'), is the number one past the last row. Then three Set Data Cells fill it in.
5.7.4 Is Same Position
Is Same Position lays two pictures from the album side by side. It compares them Square by Square.
Asks whether two positions recorded in the Position History table are the same: the same Piece on every Square and the same number of castlings allowed. earlier and later: the two positions' numbers in the table (0 is the first one recorded). isSame (returned): true when they are the same.
- Name
squares- Value
0
- Name
earlierRow- Value
0
- Name
laterRow- Value
0
- Name
sameSquares- Value
0
- Type
Boolean- Name
isSame- Value
false
- Variable
squares- Value
dataCount('Position')
squarestimes- Variable
earlierRow- Value
earlier * squares + local.index
- Variable
laterRow- Value
later * squares + local.index
data('Position History', earlierRow, 'kind') == data('Position History', laterRow, 'kind') && data('Position History', earlierRow, 'side') == data('Position History', laterRow, 'side') && data('Position History', earlierRow, 'castlingsAllowed') == data('Position History', laterRow, 'castlingsAllowed')- Variable
sameSquares- Operator
+=- Value
1
sameSquares == squares- Variable
isSame- Value
true
- Value
isSame
Its Parameters earlier and later are page numbers in the album, starting at 0. Page 2’s rows start at 2 times 64, so step 8 finds each Square’s row on one page with earlier * squares + local.index, and step 9 on the other. Every Square that matches in kind, side and castlings adds one to sameSquares. All 64 match? Then the two pictures are the same.
5.7.5 Is Position Repeated
Is Position Repeated takes the newest picture and leafs back through the album.
Asks whether the position recorded last in the Position History table has now been seen timesForDraw times, which ends the game as a Draw. Only every other position before it can be the same, because the Sides take turns: those had the same Side to move. timesForDraw: how many times the same position must be seen (3, threefold repetition). isRepeated (returned): true when it has been seen that often.
- Name
lastPosition- Value
0
- Name
earlier- Value
0
- Name
timesSeen- Value
1
- Type
Boolean- Name
isRepeated- Value
false
- Variable
lastPosition- Value
dataCount('Position History') / dataCount('Position') - 1
floor(lastPosition / 2)times- Variable
earlier- Value
lastPosition - 2 * (local.index + 1)
- Action Name
positionLook- earlier
earlier- later
lastPosition
positionLook.value == true- Variable
timesSeen- Operator
+=- Value
1
timesSeen >= timesForDraw- Variable
isRepeated- Value
true
- Value
isRepeated
It leafs back two pages at a time. Why skip one? The Sides take turns, so the page right before has the other Side to move. That’s never the same position, even with every Piece in place.
Step 5 finds the newest page’s number: the album’s rows, divided by 64, minus 1. The Repeat then runs once for every second page before it. timesSeen starts at 1, for the newest picture itself, and each match adds one. When it reaches timesForDraw, 3, the answer is yes. That’s the Draw.
5.8 The Last Word
Now one Logic Component asks all the questions, at the start of every Turn.
5.8.1 Start Turn
Start Turn tells the rest of the game when a Turn begins, with a new Custom Event, turnStarted.
Starts each Turn: the first one (White's) when the game begins, and the next one after every Move (Select and Move or Choose Promotion sends "moveMade"). It gives the Turn to the other Side with nothing selected and no Moves marked, and asks whether that Side is in Check (Look For Check), which sets the Check mark and the status line. Then it tells the Game Rules "turnStarted" (Look For Game End listens).
Any Objectself.boardState- gameRules
self
- Custom Event
turnStarted
- Custom Event
positionChanged
- Type
Text- Name
side
- Variable
side- Value
self.boardState.sideToMove
- Component Value
self.boardState.sideToMove- Value
data('Sides', side, 'opponent')
- Component Value
self.boardState.selected- Value
''
- gameRules
self
- Custom Event
turnStarted
In When Created, step 2 goes between Look For Check and positionChanged. That’s the middle of a line once more. Add the new block apart from the line, and join it after Look For Check with the round handles. In On Move Made, step 7 goes at the end, after Look For Check. Both are Trigger Custom Event on Object, with no Applies to line, so they ring the bell for the Game Rules alone.
5.8.2 Look For Game End
Look For Game End is the one that listens. It hears turnStarted and asks its questions, one after another. If any answer says the game is over, two things happen inside one If, in its Then: Show Result writes the words, and the sign turns to Game Over. Nothing else happens on an ordinary Turn.
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), and the game is over (Game State Game Over), so the Board no longer answers and the final position stays. 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
- Game State
Game Over
It asks its four questions in a row. Each call has its own Action Name: outOfMoves, material and repetition. Step 5’s If joins four tests with ||, because any one answer is enough. Each call’s one answer is read as value: outOfMoves.value, material.value and repetition.value. The last test asks whether fiftyMoveCount is at fiftyMoveCountForDraw yet.
Steps 6 and 7 both go in the If’s Then. Step 6, the call to Show Result, hands it all four: gameRules and the three answers, in the card’s order. Step 7, Set Game State, turns the sign to Game Over.
Its Shared Variables are two constant Numbers, so neither changes during play. The first, timesSeenForDraw, is 3, for threefold. The second, fiftyMoveCountForDraw, is 100. The rule counts fifty Moves by each Side, and Make Move counts both Sides’ Moves.
Board StateSelect and MoveSelect and Move DataChoose PromotionChoose Promotion DataAttach Look For Game End to the Game Rules. Every Turn now starts with a look at the whole game, before anyone moves.
5.8.3 Report Click
Only the Description changes on this card. There’s nothing to build.
When the Player clicks this Square while the game is being played (Game State Playing), it tells whoever listens (the Game Rules) with "squareClicked"; the listener reads which Square it was as Other. While the Promotion choice is open, and after the game has ended, the Board does not answer.
Any Object- Action Name
isPlaying- Game State
Playing
isPlaying.value == true- Custom Event
squareClicked
Its steps already send squareClicked only while the sign says Playing. That was Chapter 4’s promise for the Promotion panel, and it keeps a finished game still too. If you typed its Description, add the new words about the end of the game.
5.8.4 Choose Promotion
One last change, for a rare moment. What if a Pawn promotes and gives Checkmate in the same Move?
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 makes the waiting Pawn Move (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
- Table
Position- Row
self.choosePromotionData.landingSquare- Column
"kind"- Value
self.choosePromotionData.chosenKind
- Custom Event
moveMade
- gameRules
self
- Component Value
self.boardState.promotionSquare- Value
''
- UI Group
"Promotion"- Visible
No
- Game State
Playing
- Custom Event
positionChanged
Choose Promotion used to close the panel last, after the Move. But the new Turn starts inside the Move, when moveMade is sent, and Look For Game End may turn the sign to Game Over right then. A Set Game State to Playing after that would turn it back, and the game would go on after its end. So the three closing steps move up, before the Move is made. Here, the order is everything.
Delete the three struck steps from the bottom of the line. Then put them in again as steps 5 to 7, between step 4 and the If, with Add Inside Then on the outer If. They join the end of its line first, after the inner If. Use the round handles to join them after step 4, in order, ahead of that inner If. Step 7’s list shows Playing as Playing · Initial, as in Chapter 4.
Run the game. Play f2 to f3, e7 to e5, g2 to g4, and the Queen from d8 to h4.
The Queen lands, and at once the words under the Board change to You lose · Checkmate, while White’s King sits on its red Square. Click a Piece, any Piece. Nothing answers.
The game is over, and the Board keeps it exactly as it ended.
Watch for each of these.
- Play f2 to f3, e7 to e5, g2 to g4, and the Queen from d8 to h4. You lose · Checkmate, and no Piece answers a click.
- In a new game, play e2 to e4 and e7 to e5. The Queen goes from d1 to h5, and Black’s Knight from b8 to c6. Next, the Bishop goes from f1 to c4, and Black’s other Knight comes out from g8 to f6, too late. Last, the Queen captures on f7. You win · Checkmate, with Black’s King red on e8.
- In a new game, move the Knights out and home again, twice: g1 to f3, g8 to f6, f3 to g1, f6 to g8, then all four once more. After the eighth Move: Draw · Threefold repetition.
- Stalemate takes a famous short game, ten Moves for White and nine for Black. In a new game, play them in pairs, White first:
- e2 to e3, a7 to a5.
- Queen d1 to h5, Rook a8 to a6.
- The Queen captures the Pawn on a5, and Black’s h-Pawn goes from h7 to h5.
- h2 to h4, Rook a6 to h6.
- The Queen captures on c7, and f7 goes to f6.
- The Queen captures on d7. Black’s King steps to f7.
- The Queen captures on b7, and Black’s Queen comes out from d8 to d3.
- The Queen captures on b8. Queen d3 to h7.
- The Queen captures the Bishop on c8, and Black’s King steps from f7 to g6.
- Last, the Queen goes from c8 to e6.
- Promote a Pawn, as in Chapter 4. The panel closes, and the game goes on as before.
- Insufficient material and the fifty-move rule take a long game to reach by hand. They’re built, and a long game will meet them.
Every Turn now starts with one question. Look For Game End asks whether there’s a Legal Move left, whether either Side could still give Checkmate, and whether the game has been here twice before. The album of positions answers the last one, and a count of quiet Moves answers the fifty-move rule. Show Result says how it ended, and the sign says Game Over.
Underneath, the end of the game is only one more state. Nobody told the Squares. They were already listening to the sign from Chapter 4, so when the sign changed to Game Over, they stopped.
5.9 If It Didn’t Work
This chapter has many small Functions, and a slip in one can hide in a long game. Watch what the game does, and think it through.
A block drawn in red, with a number on the Errors bar? Open the bar. It names the block and what it misses. Compare it with the card.
The game says Check after the Queen reaches h4, but never Checkmate? Look For Game End may not be hearing turnStarted. Perhaps it isn’t attached to the Game Rules. Perhaps Start Turn’s new steps say something a letter off: both must read turnStarted, with a capital S.
The game ends after the very first Move? Look first at Look For Game End’s step 7, which belongs inside the If, right under Show Result. Next, Is Out Of Moves may never find a dot. Look at its step 10: && joins both tests. Or step 12’s If may sit inside step 10’s If instead of after it.
You lose · Checkmate when you won? Show Result’s step 5 asks three things, and the last compares side with gameRules.boardState.playerSide. Perhaps playerSide in Board State has no default, White.
The words say a result, but the Board still answers clicks? Look at Look For Game End’s step 7, and the Game State list in the Set up. Game Over has to be there to be picked.
The Knights dance back and forth, and no Draw comes? Look at Record Position. Its Control Data Row adds a row only when the Row is dataCount('Position History'), one past the end. Or look at Is Position Repeated’s step 5, which divides by dataCount('Position'). Are its Table names spelled the same as yours?
After an en passant capture, is a strange Piece left behind on the Board again? Look at Update En Passant’s step 16, whose Column needs 'kind' with its quote marks, as Make Move’s did.
Each of these is one small fix. Make it, run the game again, and you’re back with the chapter. The catch-up download in the Goal is this chapter’s game, finished, if you’d like to compare.
Every ending is one If in Look For Game End and one row in Words. What ending would you add? Some players like a race: the first King to reach one of the four middle Squares, d4, d5, e4 or e5, wins. Or a game that ends after a set number of Moves, with the most Pieces left winning. Your game knows how to stop now, and what it stops for is up to you.
Still stuck? Search the manual, or ask in the Myth Engine Discord (opens a new tab).
A game can end now, and the Board remembers how. In Chapter 6, a row of buttons arrives, and the first one starts a new game.
