- Learn
- Build a Game
- Chess
- 9 Threats
9 Threats
By the end of this chapter, a Threats button sits in the middle of the row. Press it, and the Board shows what could happen next. Every Piece that could be captured turns red. One that nothing could capture back gets a ring, and a pinned one an amber bar.
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 10 from it.
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.
9.1 The Idea
Play your game from Chapter 8 for a while. Sooner or later the Computer takes one of your Pieces, and you never saw it coming. The Piece stood there for a whole Turn, open to capture, and nothing on the Board said so.
This chapter lets the Board say so. A Threat is a Capture that could happen next, on the very next Move. Press Threats, and the game is in Threats mode. Every Piece an enemy Piece could legally capture turns red, with a red outline around it. That’s an attacked Piece.
Two more marks tell you more. A Piece that’s unprotected has a ring inside its outline, and its red is a little stronger. If it’s captured, nothing of its own Side can capture back. A Piece that’s pinned gets a short amber bar under it. It stands between its own King and an enemy Piece, so moving it would put its King in Check. A small legend under the buttons names all three.
Here’s the hard part. To know whether a Piece is protected, the game has to ask what would happen after a Capture. It has to make the Capture, look, and take it back. A good chess player does this with folded hands, moving the Pieces only in their head. Your game will do the same, in the Position table, for a moment, and the Board on the screen never sees it.
9.2 The Plan
Here’s how the pieces work together. Read each line from left to right, once now for the shape, and again after the marks first show.
- Threats is pressed -> Report Threats sends threatsPressed -> Show Threats turns Threats mode on and calls Find Threats.
- A Turn starts -> turnStarted -> Show Threats calls Find Threats again.
- Find Threats -> Mark Captures for every Piece, Mark Pins for each King, Mark Unprotected Pieces -> the Threats table.
- Every Square, Threat Mark and Pin Mark hears positionChanged -> on the Square, Square Marks tints the red. On its Threat Mark, Show Threat Mark draws the outline and the ring. On its Pin Mark, Show Pin Mark draws the amber bar under the Piece’s foot.
9.3 Set Up
The three pictures come from the Starter Kit, as before. Leave the placements for now. The legend and the Threats button go in later in the chapter, once Show Threats exists, and the chapter says when.
9.4 Where the Threats Are Kept
A Threat belongs to a Square, so the game keeps one row for each.
9.4.1 Threats
One row of the "Threats" Data Table, which follows it. What Threats mode marks on each Square: one row per Square, a1 to h8, in the same order as the Position table. isAttacked: yes when the Piece standing there can be legally captured by an enemy Piece (the King: when it is in Check). isUnprotected: yes when it is attacked, is not a King, and no Capture of it can be answered by a legal recapture. pinnedBy: the Square of the enemy Queen, Rook, or Bishop that pins the Piece standing there to its own King; empty when it is not pinned. Find Threats fills it while Threats mode is on; otherwise every row is no and empty.
Threats has three columns, made from the fields of Threats Entry. isAttacked and isUnprotected are Yes or No. pinnedBy is Text, and it says more than yes. It holds the Square of the enemy Piece that pins this one. Nothing reads which Square yet. Chapter 10 will.
The table starts with no rows at all, not even one for a1. That’s the next piece’s job.
9.4.2 Board State
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 and chosenStrength: while the New Game panel is open, the Side and the Strength it shows as chosen. isShowingThreats: whether Threats mode is on (the Threats button; kept on this device).
Board State gets one more field, isShowingThreats. It’s the one place the game knows whether Threats mode is on.
9.4.3 Clear Threats
Marks nothing on any Square: the Threats table is emptied, then gets one row for each Square of the Position table, a1 to h8 in the same order, every cell its column's default (no, or empty). The Threats table has no rows of its own; Clear Threats makes them, each time it is used.
- Type
Text- Name
square
- Table
Threats
dataCount('Position')times- Variable
square- Value
dataRow('Position', local.index)
- Action Name
addedRow- Table
Threats- Row
square
Clear Threats wipes the slate. Every Square gets its row back, named as Position names it, and nothing is marked. Its Control Data Row has no Operation line, because it stays as it starts, on adding the row.
9.5 A Walk, Written Once
Before anything is marked, one old piece of Logic gets taken apart.
9.5.1 Find First Piece Along
Looks back from one Square along one direction of the Directions table, Square by Square, and finds the first Piece in the way. square: the Square looked from (a1 to h8). direction: the row (0 = first) of the Directions table. bySide: the Side whose Pieces move this way (a Pawn's direction is turned round by its Side's forward). firstPiece (returned): the Square of the first Piece, or empty when the edge of the Board, or the end of the direction's steps, comes first.
- Name
forward- Value
1
- Name
file- Value
0
- Name
rank- Value
0
- Name
stepsLeft- Value
0
- Type
Boolean- Name
isOnBoard- Value
false
- Name
index- Value
0
- Type
Boolean- Name
keepLooking- Value
true
- Type
Text- Name
firstPiece
- Variable
forward- Value
data('Sides', bySide, 'forward')
- Variable
file- Value
data('Position', square, 'file')
- Variable
rank- Value
data('Position', square, 'rank')
- Variable
stepsLeft- Value
data('Directions', direction, 'steps')
keepLooking == true && stepsLeft > 0- Variable
file- Operator
-=- Value
data('Directions', direction, 'fileStep')
- Variable
rank- Operator
-=- Value
data('Directions', direction, 'rankStep') * forward
- Variable
stepsLeft- Operator
-=- Value
1
- Variable
isOnBoard- Value
false
file >= 1 && file <= 8 && rank >= 1 && rank <= 8- Variable
isOnBoard- Value
true
- Variable
index- Value
(rank - 1) * 8 + (file - 1)
- Variable
keepLooking- Value
false
isOnBoard == true && data('Position', index, 'kind') == ""- Variable
keepLooking- Value
true
isOnBoard == true && data('Position', index, 'kind') != ""- Variable
firstPiece- Value
dataRow('Position', index)
- Value
firstPiece
Remember Find Attacker Along, from Chapter 3? It walks from a Square, step by step along a direction, until it meets a Piece. There it asks one question. Could that Piece capture along the line, onto the Square it started from? The pins in this chapter need the same walk. Their question at the end is different.
So the walk moves into a Function of its own, Find First Piece Along. Its walk is Find Attacker Along’s old walk, with the same blocks and values below the Declares. If you built Chapter 3, your hands know them. Only the end changes. Step 24 asks only whether a Piece stands there, and step 25 names its Square in firstPiece. Its Return Type is Text. The Return hands back firstPiece, and a call reads it as value.
Build it with Find Attacker Along open beside it. Read each old block, and build it again here by hand, one at a time.
9.5.2 Find Attacker Along
Almost all of Find Attacker Along is built again, so here is its whole new card.
Looks back from one Square along one direction of the Directions table for the first Piece in the way (Find First Piece Along). attackers (returned): 1 when that Piece belongs to bySide and is of the direction's Kind, so it could capture onto the Square, else 0 (for a Pawn, the direction is turned round by its Side's forward). square: the Square looked from (a1 to h8). direction: the row (0 = first) of the Directions table. bySide: the attacking Side.
- Type
Text- Name
piece
- Name
attackers- Value
0
- Action Name
pieceLook- square
square- direction
direction- bySide
bySide
- Variable
piece- Value
pieceLook.value
piece != "" && data('Position', piece, 'side') == bySide && data('Position', piece, 'kind') == data('Directions', direction, 'kind')- Variable
attackers- Value
1
- Value
attackers
Now Find Attacker Along gets short. It asks Find First Piece Along for the first Piece in the way, and checks it the way it always did. Is it on bySide? Is it the direction’s Kind? Then it could capture onto the Square, and attackers is 1.
Open your Find Attacker Along. Keep its Parameters and its Number Return Type, and delete the old line’s blocks. Rebuild the line from the card. Steps 3 and 4 ask Find First Piece Along and read pieceLook.value. The If joins three tests with &&, and they now ask about piece. The Return hands back attackers.
Run the game. The game still knows every rule it did before the walk moved. Now put a King in Check, to see the walk at work. Play e2 to e4, then your Bishop from f1 to c4, and take the Pawn on f7 with it, if the way is still open. Black’s King turns red, so the walk still works from its new home.
9.6 Pieces Under Attack
Now the marks begin. This part makes the red, and the next part the rings and bars.
9.6.1 Mark Captures
Marks, in the Threats table, every enemy Piece that the Piece on one Square can legally capture. It asks Find Legal Moves, so a Capture that would leave the capturing Side's own King attacked does not count. En passant is left to Find Threats, because the Pawn it takes does not stand on the Square moved to. from: the Square of the capturing Piece.
- Type
Text- Name
side
- Type
Text- Name
enemy
- Type
Text- Name
target
- Variable
side- Value
data('Position', from, 'side')
- Variable
enemy- Value
data('Sides', side, 'opponent')
- from
from- enPassantSquare
''
dataCount('Targets')times- Variable
target- Value
dataRow('Targets', local.index)
data('Targets', target, 'reachable') == true && data('Position', target, 'side') == enemy- Table
Threats- Row
target- Column
"isAttacked"- Value
"true"
How does the game know a Piece could be captured? It asks the rules you already wrote. Mark Captures takes one Piece, and calls Find Legal Moves for it, the same Find Legal Moves that marks your dots. Every reachable Square with an enemy Piece on it is a Capture. That Piece is attacked. Its row in Threats says so, in isAttacked. Its Value is "true", in quote marks, as in Chapter 3’s Look At Square, and in a Yes or No column the word lands as Yes.
A Capture that would leave the capturing King in Check isn’t legal. It doesn’t count. The rules of chess are written once, and this is that same writing at work.
9.6.2 Find Passed Pawn
Finds the Pawn that the Side to move can take en passant right now: the enemy Pawn that has just passed one of its Pawns by moving two Squares. It stands one Rank behind the en passant Square, as seen by the Side to move. gameRules: the Game Rules Object. passedPawn (returned): its Square, or empty when no Pawn can be taken en passant (Can Take En Passant).
boardStateData StructureBoard State- Type
Text- Name
enPassant
- Type
Text- Name
side
- Name
passedRank- Value
0
- Name
passedFile- Value
0
- Type
Text- Name
passedPawn
- Action Name
enPassantLook- gameRules
gameRules
enPassantLook.value == true- Variable
enPassant- Value
gameRules.boardState.enPassantSquare
- Variable
side- Value
gameRules.boardState.sideToMove
- Variable
passedRank- Value
data('Position', enPassant, 'rank')
- Variable
passedRank- Operator
-=- Value
data('Sides', side, 'forward')
- Variable
passedFile- Value
data('Position', enPassant, 'file')
- Variable
passedPawn- Value
dataRow('Position', (passedRank - 1) * 8 + passedFile - 1)
- Value
passedPawn
En passant is the one Capture where the Piece taken isn’t on the Square moved to. So Mark Captures leaves it out, and Find Passed Pawn finds it. Passed means here that it has gone past, two Squares at once, on the Move before. It isn’t the passed Pawn chess players talk about. Can Take En Passant from Chapter 5 says whether it can happen. If it can, the Pawn stands one Rank behind the en passant Square, as the Side to move sees it.
9.6.3 Find Threats
Fills the Threats table for the position on the Board: every Piece that an enemy Piece can legally capture is attacked (Mark Captures, for each Piece of both Sides), the King in Check is attacked (Board State's checkSquare), and so is a Pawn that the Side to move can take en passant (Find Passed Pawn); every Piece pinned to its King is marked (Mark Pins, for each King); then every attacked Piece with no legal recapture is unprotected (Mark Unprotected Pieces). gameRules: the Game Rules Object.
boardStateData StructureBoard State- Type
Text- Name
kingSquare
- Type
Text- Name
square
- Type
Text- Name
passedPawn
- Variable
kingSquare- Value
gameRules.boardState.checkSquare
kingSquare != ""- Table
Threats- Row
kingSquare- Column
"isAttacked"- Value
"true"
- Action Name
passedLook- gameRules
gameRules
- Variable
passedPawn- Value
passedLook.value
passedPawn != ""- Table
Threats- Row
passedPawn- Column
"isAttacked"- Value
"true"
dataCount('Position')times- Variable
square- Value
dataRow('Position', local.index)
data('Position', square, 'side') != ""- from
square
data('Position', square, 'kind') == "King"- king
square
- gameRules
gameRules- passedPawn
passedPawn
Find Threats fills the whole table. It clears it first. Next it marks the King in Check, from checkSquare. A Piece can give Check even while it’s pinned to its own King. Mark Captures wouldn’t count that Capture as legal, because it would leave the capturing King open. It’s Check all the same, so Find Threats marks that King itself. A passed Pawn is attacked too. Then the Repeat walks every Square, and Mark Captures runs for every Piece of both Sides.
Leave steps 16, 17 and 18 for now. Mark Pins and Mark Unprotected Pieces don’t exist yet, and this chapter comes back for them. Build steps 1 to 15, then step 19 after the Repeat.
Why step 19? Find Legal Moves marks the Targets table for every Piece it asks about. Clear Targets empties it again, or your game would show some Piece’s dots that nobody selected.
9.6.4 Show Threats
Turns Threats mode on and off, and keeps its marks up to date. When the game begins (Game Start) it takes the choice kept on this device (the Persistent Value "threats"), and shows the legend (the UI Group "Threats Legend") when Threats mode is on. At the start of every Turn ("turnStarted") it fills the Threats table again (Find Threats) while Threats mode is on. When the Threats button is pressed ("threatsPressed") on the Player's Turn or after the game has ended, it turns Threats mode over (Board State's isShowingThreats), fills or empties the Threats table, shows or hides the legend, keeps the choice on this device, and every Square redraws ("positionChanged").
Any Objectself.boardState- Action Name
savedThreats- Key
"threats"- Default
self.boardState.isShowingThreats
savedThreats.value == true- Component Value
self.boardState.isShowingThreats- Value
true
self.boardState.isShowingThreats == true- UI Group
"Threats Legend"
self.boardState.isShowingThreats == true- gameRules
self
- Action Name
threatsLook
threatsLook.value == trueself.boardState.isShowingThreats == true- Component Value
self.boardState.isShowingThreats- Value
false
- UI Group
"Threats Legend"- Visible
No
- Component Value
self.boardState.isShowingThreats- Value
true
- gameRules
self
- UI Group
"Threats Legend"
self.boardState.selected != ""- from
self.boardState.selected- enPassantSquare
self.boardState.enPassantSquare
- Action Name
persistentWriteResult- Key
"threats"- Value
self.boardState.isShowingThreats
- Custom Event
positionChanged
Show Threats runs Threats mode, from the Game Rules. It has four Events.
When the Game Starts comes before anything is drawn. Clear Threats gives every Square its row, so nothing reads an empty table. Then Load Persistent Value reads the choice kept on this computer, under 'threats', as New Game read the Side in Chapter 6. A kept true turns Threats mode on.
When Created shows the legend, if Threats mode is on. Set UI Group Visibility shows every Object in the group Threats Legend at once. Its Visible starts on Yes, so leave it. Its UI Group is a box you type in, not a list, so "Threats Legend" goes in now, with its quote marks, before any Legend Key wears it.
On Turn Started finds the threats again, at the start of every Turn. A Move changes them all.
On Threats Pressed is the button. Can Show Threats says whether it can be pressed now. If so, it turns isShowingThreats over, like Flip Board’s coin, from No to Yes or from Yes to No. Turned on, it finds the threats and shows the legend. Turned off, it clears them, and the legend hides with Visible on No. Is a Piece selected? Find Legal Moves runs for it again. Find Threats emptied the Targets its dots come from. Last, Save Persistent Value keeps the choice, and positionChanged tells every Square to look again. Those last three steps come after the inner If and its Else, still inside the first If.
9.6.5 Report Threats and Can Show Threats
Asks whether the Threats button can be used: on the Player's Turn (Game State Playing) or once the game has ended (Game Over), when no Move is being made. isOn (returned): true when it can.
- Type
Boolean- Name
isOn- Value
false
- Action Name
isPlaying- Game State
Playing
- Action Name
isOver- Game State
Game Over
isPlaying.value == true || isOver.value == true- Variable
isOn- Value
true
- Value
isOn
What the Threats button does: it tells whoever listens (the Game Rules) with "threatsPressed". After every change on the Board ("positionChanged"), which the Game Rules also send when the game begins, it asks Can Show Threats whether the button can be used now, and turns it on or off (Show Button On Or Off).
Any ObjectUI Interaction- Custom Event
threatsPressed
- Action Name
canUse
- button
self- isOn
canUse.value
Report Threats sends threatsPressed on UI Activated, as Report Hint sent its message. On positionChanged it asks Can Show Threats, named canUse, then hands canUse.value to Show Button On Or Off. The button keeps its own look up to date. Can Show Threats answers the button’s question. It’s yes on your Turn, and yes once the game is over too, so you can look back at how it ended. While the Computer thinks, it’s no.
9.6.6 Game Rules
Board StateSelect and MoveSelect and Move DataChoose PromotionChoose Promotion DataMake Computer MoveMake Computer Move DataUndoUndo DataGive A HintGive A Hint DataAttach Show Threats to the Game Rules.
9.6.7 Square Marks
Shows the highlight on this Square after every change (the Game Rules send "positionChanged" and are its Other): a tint when it was part of the last Move, when its Piece is attacked in Threats mode (the Threats table; a stronger red when it is unprotected), when its Piece is selected, when it is one of the Hint's two Squares (the Chosen Moves table, row Hint), or when its King is in Check (the most important one shows). The colors, and how strongly each shows, are its Parameters. While the Computer's Move slides ("slideStarted"), no highlight is shown.
Any Objectself.squareInfo- Alpha
0
self.squareInfo.name == other.boardState.lastFrom || self.squareInfo.name == other.boardState.lastTo- Value
@self.lastMoveColor
- Alpha
@self.lastMoveAlpha
data('Threats', self.squareInfo.name, 'isAttacked') == true- Value
@self.attackedColor
- Alpha
@self.attackedAlpha
data('Threats', self.squareInfo.name, 'isUnprotected') == true- Alpha
@self.unprotectedAlpha
self.squareInfo.name == other.boardState.selected- Value
@self.selectedColor
- Alpha
@self.selectedAlpha
self.squareInfo.name == data('Chosen Moves', 'Hint', 'from') || self.squareInfo.name == data('Chosen Moves', 'Hint', 'to')- Value
@self.hintColor
- Alpha
@self.hintAlpha
self.squareInfo.name == other.boardState.checkSquare- Value
@self.checkColor
- Alpha
@self.checkAlpha
- Alpha
0
Square Marks gains attackedColor, attackedAlpha and unprotectedAlpha as constant Shared Variables, with two Ifs at steps 5 to 9. The first If tints an attacked Square red: attackedColor, at attackedAlpha, 0.4. The second makes it stronger, 0.5, for an unprotected Piece. Nothing marks a Piece unprotected yet. That If waits for the next part of the chapter, and it does no harm to build it now.
Both go after the last Move’s If, and before the selected Square’s. Order matters, as it did for the Hint. Each If paints over the one before it. So a selected Piece shows its gold over the red, and a King in Check its own red.
Add both new Ifs apart from the line. Use the round handles to join the first after step 2’s If, on the main line. Join the second after the first. The selected Square’s If follows them both. Each new If gets its own Actions with Add Inside Then, joined on its True line.
9.6.8 Legend Key
Window / UIBottom CenterUIX 110, Y 18TextThreats Legend2Legend KeysX -25, Y 0X 12, Y 12Attacked12Semibold#6E7968CenterX 14, Y 0A Legend Key is one small picture and one word, side by side. Attacked sits next to a red square. It’s a UI Object in the group Threats Legend, and its Element Type is Text, because nobody presses it.
Now go to the Set up card and place the three keys, hidden, in a row under the buttons. Each sets its own Frame from the Legend Keys picture, and its own word. The words aren’t the same length, so each placement moves its little picture to sit close beside its word, on the left. Even Attacked moves it a little. Its X goes from the Object’s -25 to -23.5.
Now make Threats Button from Tool Button, the way you made Hint Button. Report Threats supplies its job.
Place Threats Button from the Set up card, with Threats as its Text. It fills the space in the middle of the row, at X 0, that Chapter 6 left.
Run the game. Press Threats.
The legend appears under the buttons, and the Board shows nothing at all. That’s right. At the start of a game, no Piece can reach an enemy one.
Now play e2 to e4. After the reply, move your Queen from d1 to h5, and look at Black’s side as it lands. The Pawns your Queen could take are red.
9.7 Unprotected and Pinned
The red tells you a Piece could be captured, but not whether losing it would hurt. This part answers two more questions. The chapter’s hard idea lives here too.
9.7.1 Can Capture
Answers whether the Piece on one Square can legally capture an enemy Piece: the Capture must be one of its Moves, and must not leave its own King attacked. from: the capturing Piece's Square. to: the Square it moves to. taken: the Square of the Piece it takes (the same as to, except en passant). enPassantSquare: the en passant Square when the Capture may be en passant, else empty. canCapture (returned): yes when it can.
- Type
Text- Name
side
- Type
Text- Name
enemy
- Type
Boolean- Name
canCapture- Value
false
- Variable
side- Value
data('Position', from, 'side')
- Variable
enemy- Value
data('Sides', side, 'opponent')
- from
from
- from
from- enPassantSquare
enPassantSquare
data('Targets', to, 'reachable') == true && data('Position', taken, 'side') == enemy- Action Name
kingLook- side
side
- from
from- to
to- taken
taken- kingSquare
kingLook.value- enemy
enemy
data('Targets', to, 'reachable') == true && data('Position', taken, 'side') == enemy- Variable
canCapture- Value
true
- Value
canCapture
Can Capture asks about one Capture only: this Piece, onto that Square. Find Moves and Find En Passant Move mark every Square it could go to, en passant included. Is the Square marked, with an enemy on it? Then Drop Move If Unsafe, from Chapter 3, unmarks it if it would leave its own King in Check. If the Square is still marked after that, the answer is yes.
Step 11 asks step 8’s question again, on the main line after it. Why is that safe? If step 8 said no, nothing in between ran, so step 11 says no too.
It’s Find Legal Moves in miniature. Asking about one Capture is quicker than finding every Move, and this game asks a great many.
9.7.2 Can Side Capture
Answers whether any Piece of one Side can legally capture the enemy Piece on a Square (Can Capture, for each of its Pieces in turn until one can). square: the Square (a1 to h8). bySide: the capturing Side. canCapture (returned): yes when one can.
- Type
Text- Name
piece
- Type
Boolean- Name
canCapture- Value
false
dataCount('Position')times- Variable
piece- Value
dataRow('Position', local.index)
data('Position', piece, 'side') == bySide && canCapture == false- Action Name
captureLook- from
piece- to
square- taken
square- enPassantSquare
''
- Variable
canCapture- Value
captureLook.value
- Value
canCapture
Can Side Capture asks the same of a whole Side. It walks every Square, and asks Can Capture of each Piece on bySide. As soon as one says yes, the If stops asking, and the answer is yes.
9.7.3 Can Recapture
Answers whether a Capture can be answered: the Piece on from legally captures, and then a Piece of the captured Side can legally capture back on the Square it moved to. Pinned Pieces cannot, since every Capture is a Legal Move. from: the capturing Piece's Square. to: the Square it moves to. taken: the Square of the Piece it takes (the same as to, except en passant). enPassantSquare: the en passant Square for an en passant Capture, else empty. canRecapture (returned): yes when the Capture can be made and answered.
- Type
Text- Name
takenKind
- Type
Text- Name
defender
- Type
Boolean- Name
canRecapture- Value
false
- Variable
takenKind- Value
data('Position', taken, 'kind')
- Variable
defender- Value
data('Position', taken, 'side')
- Action Name
captureLook- from
from- to
to- taken
taken- enPassantSquare
enPassantSquare
captureLook.value == true- Table
Position- Row
taken- Column
"kind"- Value
''
- Table
Position- Row
taken- Column
"side"- Value
''
- from
from- to
to
- Action Name
recaptureLook- square
to- bySide
defender
- Variable
canRecapture- Value
recaptureLook.value
- from
to- to
from
- Table
Position- Row
taken- Column
"kind"- Value
takenKind
- Table
Position- Row
taken- Column
"side"- Value
defender
- Value
canRecapture
Here’s the chapter’s one hard idea. Take it slowly.
A Piece is protected when, after an enemy captures it, its own Side can capture back. So the game has to look one Move ahead. Can Recapture does it the way a player does in their head.
First it notes the taken Piece’s Kind and Side, takenKind and defender, so it can put them back. Then it asks Can Capture whether the Capture is legal at all. If it is, the If makes it, for real, in the Position table. Two Set Data Cells lift the taken Piece off. Move Piece carries the capturing Piece onto its Square.
Now the question. Can Side Capture asks whether any Piece of the defender’s Side could capture back on that Square. Its answer is canRecapture.
After that, everything goes back where it was, in the opposite order. Move Piece carries the Piece home again. The last two Set Data Cells put the taken Piece back, its Kind and its Side.
It looks like the game moves Pieces to find out. It does. But only in the table, and only for a moment. Nothing sends positionChanged in between, so no Square, Piece or mark ever redraws. On the screen, the Board never moves.
And here’s a quiet thing it gets right. A pinned Piece can’t capture back, because its Capture would leave its King in Check. Can Capture already says no to that. So a pinned defender is no protection at all.
9.7.4 Is Unprotected
Answers whether an attacked Piece is unprotected: no enemy Capture of it can be answered by a legal recapture (Can Recapture, for each enemy Piece in turn until one Capture can be answered). square: the Piece's Square. passedPawn: the Pawn the Side to move can take en passant, else empty (Find Passed Pawn). enPassantSquare: the en passant Square. isUnprotected (returned): yes when it is unprotected.
- Type
Text- Name
side
- Type
Text- Name
enemy
- Type
Text- Name
attacker
- Type
Boolean- Name
isProtected- Value
false
- Type
Boolean- Name
isUnprotected- Value
false
- Variable
side- Value
data('Position', square, 'side')
- Variable
enemy- Value
data('Sides', side, 'opponent')
dataCount('Position')times- Variable
attacker- Value
dataRow('Position', local.index)
data('Position', attacker, 'side') == enemy && isProtected == false- Action Name
recaptureLook- from
attacker- to
square- taken
square- enPassantSquare
''
- Variable
isProtected- Value
recaptureLook.value
square == passedPawn && data('Position', attacker, 'kind') == "Pawn" && data('Position', attacker, 'side') == enemy && isProtected == false- Action Name
enPassantLook- from
attacker- to
enPassantSquare- taken
square- enPassantSquare
enPassantSquare
- Variable
isProtected- Value
enPassantLook.value
isProtected == false- Variable
isUnprotected- Value
true
- Value
isUnprotected
Is Unprotected asks Can Recapture about every enemy Piece that might capture this one. One Capture with an answer is enough, and isProtected is yes. If none has an answer, the Piece is unprotected.
The second If is en passant again. A passed Pawn is taken from a different Square, the en passant Square, so that Capture gets its own question.
9.7.5 Mark Unprotected Pieces
Marks, in the Threats table, every attacked Piece other than a King that is unprotected (Is Unprotected). It runs once every attacked Piece is marked. gameRules: the Game Rules Object. passedPawn: the Pawn the Side to move can take en passant, else empty (Find Passed Pawn).
boardStateData StructureBoard State- Type
Text- Name
enPassant
- Type
Text- Name
square
- Variable
enPassant- Value
gameRules.boardState.enPassantSquare
dataCount('Position')times- Variable
square- Value
dataRow('Position', local.index)
data('Threats', square, 'isAttacked') == true && data('Position', square, 'kind') != "King"- Action Name
unprotectedLook- square
square- passedPawn
passedPawn- enPassantSquare
enPassant
- Table
Threats- Row
square- Column
"isUnprotected"- Value
unprotectedLook.value
Mark Unprotected Pieces asks Is Unprotected about every attacked Piece, and writes the answer into its row. It runs after every attack is marked, so it only asks about the red ones. A King is left out. A King that’s attacked is in Check, and that has its own color.
9.7.6 Find Pin Along
Looks from a King along one line (a direction of the Directions table) for a pin: the first Piece on the line is one of the King's own, and the next Piece beyond it is an enemy that moves along this line (the direction's Kind). That own Piece is pinned: the Threats table's pinnedBy gets the enemy Piece's Square. king: the King's Square. direction: the row (0 = first) of the Directions table.
- Type
Text- Name
side
- Type
Text- Name
enemy
- Type
Text- Name
candidate
- Type
Text- Name
pinner
- Variable
side- Value
data('Position', king, 'side')
- Variable
enemy- Value
data('Sides', side, 'opponent')
- Action Name
firstLook- square
king- direction
direction- bySide
enemy
- Variable
candidate- Value
firstLook.value
candidate != "" && data('Position', candidate, 'side') == side- Action Name
beyondLook- square
candidate- direction
direction- bySide
enemy
- Variable
pinner- Value
beyondLook.value
pinner != "" && data('Position', pinner, 'side') == enemy && data('Position', pinner, 'kind') == data('Directions', direction, 'kind')- Table
Threats- Row
candidate- Column
"pinnedBy"- Value
pinner
A pin is a line. On it, from the King outward, stand one Piece of the King’s own Side and then an enemy that slides along that line. A Bishop on a diagonal, a Rook on a File, a Queen on either. Move the Piece in between, and the King is open.
Find Pin Along looks for that from a King, along one direction. Find First Piece Along finds the first Piece in the way, and calls it candidate. Is it the King’s own? Then it walks on from there, and the next Piece is pinner. Is pinner an enemy of the direction’s Kind? Then candidate is pinned, and its pinnedBy names pinner’s Square.
Both walks pass enemy as bySide, though the first Piece they look for is the King’s own. bySide only sets which way a direction points. A pinner moves toward the King, so the walk from the King looks back along the enemy’s way of moving.
Step 12’s If sits on the main line, after step 9’s, not inside it. Why is that safe? If the first Piece isn’t the King’s own, step 11 never runs. Then pinner stays empty, as it starts each time, and step 12 says no.
That’s the walk from the start of the chapter, used twice in a row, and the reason it moved out.
9.7.7 Mark Pins
Marks, in the Threats table, every Piece pinned to one King (Find Pin Along, for every line a Queen, Rook, or Bishop slides along). king: the King's Square.
dataCount('Directions')timesdata('Directions', local.index, 'steps') > 1- king
king- direction
local.index
Mark Pins tries every direction from one King. Only a direction whose steps is more than 1 can pin, because only a Piece that slides can pin. A Knight’s or a King’s one step never does.
9.7.8 Find Threats, the Last Three Steps
Go back to Find Threats. Add the three steps that waited. Their card is the one under Find Threats, back in the part on pieces under attack.
Steps 16 and 17 go inside the Repeat, at the end of its line. Right-click the Repeat and choose Add Inside Repeat for the If for a King, and join it. Its Add Inside Then adds Mark Pins, joined on the True line.
Step 18 goes after the whole Repeat, before Clear Targets. Add Mark Unprotected Pieces apart from the line, then use the round handles to join it after the Repeat. Clear Targets stays behind it.
9.7.9 Show Threat Mark
Outlines this Threat Mark's Square in Threats mode when the Piece standing there is attacked, with a double ring around it when it is unprotected (the Threats table), after every change (the Game Rules send "positionChanged"), and hides the mark while the Computer's Move slides ("slideStarted").
Any Objectself.squareInfodata('Threats', self.squareInfo.name, 'isAttacked') == true- Visible
Yes
- Visible
No
data('Threats', self.squareInfo.name, 'isUnprotected') == true- Frame
1
- Frame
0
- Visible
No
Show Threat Mark hears positionChanged, and reads its own Square’s row of Threats. An attacked Piece shows the mark. Any other hides it. An unprotected one gets Frame 1 of the picture, the outline with a second ring inside it. The rest get Frame 0, the outline alone.
While the Computer’s Piece slides, the mark hides, the way Square Marks hides its color.
9.7.10 Show Pin Mark
Shows the amber bar under this Pin Mark's Piece in Threats mode when the Piece is pinned to its own King (the Threats table's pinnedBy), after every change (the Game Rules send "positionChanged"), and hides it while the Computer's Move slides ("slideStarted").
Any Objectself.squareInfodata('Threats', self.squareInfo.name, 'pinnedBy') != ""- Visible
Yes
- Visible
No
- Visible
No
Show Pin Mark is the same idea, for one question. Is pinnedBy empty? If not, the bar shows, and if it is, the bar hides.
9.7.11 Place On Board
Place On Board’s Logic doesn’t change. There’s nothing on its card to build, and it names two more Objects to attach it to, the two marks next.
Keeps this Object on its Square (Square Info) when the Board is turned over: when the Game Rules say "boardFlipped" (they are its Other), it moves to where its Square is now drawn (Square Position). The Piece, the Square, the Move Mark, the Threat Mark, and the Pin Mark of each Square all use it. squareMeters: how wide one Square is (3.75 m: the Board art is 30 m across). boardLeft and boardBottom: where the Board's bottom-left corner is in the world (0 and 4.375 m: the game window is 140 px taller than the Board, for the status line and the buttons under it).
Any Objectself.squareInfo- Type
Boolean- Name
isFlipped- Value
false
- Variable
isFlipped- Value
other.boardState.isFlipped
- Action Name
mySquare- square
self.squareInfo.name- isFlipped
isFlipped- squareMeters
@self.squareMeters- boardLeft
@self.boardLeft- boardBottom
@self.boardBottom
- X
mySquare.x- Y
mySquare.y
9.7.12 Threat Mark and Pin Mark
X 3.75, Y 3.7511Threat MarksX 3.75, Y 3.75Square InfoX 3.75, Y 3.7511Pin MarksX 3.75, Y 3.75Square InfoThe Threat Mark and the Pin Mark each live on one Square, the way a Move Mark does. Each has a Square Info. Place On Board keeps it on its Square when the Board turns. Their Scene Depth Draw Order is 11. That’s higher than the Piece’s, so they’re drawn over it. The bar crosses the Piece’s foot, and the ring circles it.
9.7.13 Lay Out The Board
Makes the Objects that show each Square of the Board when the Scene starts: for every row of the Position table (one Square, a1 to h8), a Piece Object, a Square, a Move Mark, a Threat Mark and a Pin Mark are made on that Square (Square Position), and each is given the Square's name (its Square Info); a Square in the left or right column also gets a Rank Label, and one in the bottom or top row a File Label (turned over, the h-File is on the left and Rank 8 at the bottom). Then it says "boardLaidOut". pieceObject, squareObject, moveMarkObject, threatMarkObject, pinMarkObject: the Objects made on each Square; rankLabelObject, fileLabelObject: the labels. squareMeters: how wide one Square is.
Any Objectself.layOutTheBoardData- Name
boardBottom- Value
0
- Name
boardLeft- Value
0
- Variable
boardLeft- Value
self.transform.position.x - 4 * @self.squareMeters
- Variable
boardBottom- Value
self.transform.position.y - 4 * @self.squareMeters
dataCount('Position')times- Component Value
self.layOutTheBoardData.squareName- Value
dataRow('Position', local.index)
- Action Name
spot- square
self.layOutTheBoardData.squareName- isFlipped
No- squareMeters
@self.squareMeters- boardLeft
boardLeft- boardBottom
boardBottom
- Object to Create
@self.pieceObject- X
spot.x- Y
spot.y- Created As
piece
- Variable
piece.name- Value
self.layOutTheBoardData.squareName
- Object to Create
@self.squareObject- X
spot.x- Y
spot.y- Created As
square
- Variable
square.name- Value
self.layOutTheBoardData.squareName
- Object to Create
@self.moveMarkObject- X
spot.x- Y
spot.y- Created As
moveMark
- Variable
moveMark.name- Value
self.layOutTheBoardData.squareName
- Object to Create
@self.threatMarkObject- X
spot.x- Y
spot.y- Created As
threatMark
- Variable
threatMark.name- Value
self.layOutTheBoardData.squareName
- Object to Create
@self.pinMarkObject- X
spot.x- Y
spot.y- Created As
pinMark
- Variable
pinMark.name- Value
self.layOutTheBoardData.squareName
data('Position', self.layOutTheBoardData.squareName, 'file') == 1 || data('Position', self.layOutTheBoardData.squareName, 'file') == 8- Object to Create
@self.rankLabelObject- X
spot.x- Y
spot.y- Created As
rankLabel
- Variable
rankLabel.name- Value
self.layOutTheBoardData.squareName
data('Position', self.layOutTheBoardData.squareName, 'rank') == 1 || data('Position', self.layOutTheBoardData.squareName, 'rank') == 8- Object to Create
@self.fileLabelObject- X
spot.x- Y
spot.y- Created As
fileLabel
- Variable
fileLabel.name- Value
self.layOutTheBoardData.squareName
- Custom Event
boardLaidOut
Lay Out The Board makes the two marks on every Square, as it makes the Move Mark. Two new constant Object Shared Variables, threatMarkObject and pinMarkObject, and four new steps, 14 to 17.
The order they come in changes nothing on the Board. So click the Repeat’s last step that isn’t inside an If, add step 14, and join it. Go on from there. If your step numbers in this one Logic don’t match the card’s, that’s all right. Here the order doesn’t matter.
9.7.14 Board
X 30, Y 30Walnut BoardX 30, Y 30Lay Out The BoardLay Out The Board DataPieceRank LabelFile LabelSquareMove MarkThreat MarkPin MarkOn the Board’s Shared Variables card, choose Threat Mark and Pin Mark for the two new Values. Both start as None buttons. Press each and pick its Object, as in Chapter 1.
Run the game. Press Threats if it’s off, and play e2 to e4, then d1 to h5, as before.
Your Queen lands, and the Board shows you at once. The Pawn on f7 is red, with a red outline, and under it, most likely, lies a short amber bar. Your Queen, that Pawn and Black’s King stand on one diagonal. If the Pawn stepped off that line, its King would be in Check at once, so it has to stay.
Your Board can see one Move ahead now.
Watch for each of these.
- Start a new game as White, and press the Threats button in the middle of the row. The legend shows under the buttons: Attacked, Unprotected, Pinned. Nothing on the Board is marked.
- Play e2 to e4, wait for the reply, then play d1 to h5. As your Queen lands, the Pawns it could take turn red, with a red outline.
- The Pawn on f7 has an amber bar under it, unless Black’s first reply moved its f-Pawn or g-Pawn. It’s pinned to its King.
- Leave one of your Pieces where the Computer could take it, with nothing of yours able to take it back. Its outline gets a ring, and its red is stronger. A Pawn on e4, with a Black Knight on f6 and nothing guarding it, is one.
- Click a red Piece. Its Square turns gold, and its outline stays.
- Press Threats again. Every mark goes, and so does the legend.
- Play e2 to e4, and look at Threats while the words say Thinking…. It’s faded.
- Turn Threats on, and close the game. Run it again. Threats mode is still on, and the legend is back.
Threats asks the rules you already wrote. Mark Captures asks Find Legal Moves what every Piece could take. Can Recapture makes a Capture in the table, asks who could take back, and puts it all back. Find Pin Along walks out from each King. The answers go into one row per Square, and three pieces of Logic draw them.
Underneath is one idea. A question about the future can be asked of the present. Change the table, ask, and change it back, all before anything is drawn on the screen.
9.8 If It Didn’t Work
This chapter has many small Functions, and most of them call each other, sometimes three deep. A slip in one can hide behind the others. Watch what the Board does. Think it through from there.
A block drawn in red, with a number on the Errors bar? Open the bar. It names the block and what it misses. Compare that block with its card.
A King in Check no longer turns red, after the first part? Look at Find Attacker Along’s If. Its three tests ask about piece, and piece must be declared as Text.
Threats stays faded on your Turn? Look at Report Threats’ On Position Changed. Both calls need joining, with canUse.value in Show Button On Or Off’s isOn. Threats lights up, but a press does nothing? Check Show Threats on the Game Rules, where it needs to be attached. Perhaps the placement still picks Hint Button instead of Threats Button.
The legend never shows? Each Legend Key’s Group must be Threats Legend, the same words Show Threats names.
Dots appear for a Piece you didn’t select? Find Threats’ last step, Clear Targets, may be missing or Unattached.
Pieces jump to the wrong Squares, or vanish, when Threats is on? Can Recapture didn’t put everything back. Look at its last three steps. Move Piece there carries the Piece back, so its from and to are swapped from the first Move Piece’s.
No outline, ring or bar anywhere, though the red shows? Look at Lay Out The Board’s four new steps, and at the Board, where its two new Shared Variables need Threat Mark and Pin Mark. A ring never shows? Find Threats’ step 18 may still be missing.
The marks stay behind when you press Flip, while every Piece moves to its new Square? Place On Board may not be attached to that mark’s Object.
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.
The Board shows three things now. What else would help a player see the game? Perhaps a Piece attacked twice, wearing a second outline so you can tell it from one attacked once. Or the Squares the enemy could move to, faintly tinted, so a King knows where not to step. The Threats table has room for another column, and Square Marks for another If. What your Board shows is up to you.
Still stuck? Search the manual, or ask in the Myth Engine Discord (opens a new tab).
Your Board shows what could happen next. In Chapter 10, the threats get lines you can follow, from each pinning Piece to its King.
