Build a GameCreating Chess

10  Pin Lines and Arrows

Goal

By the end of this chapter, Threats mode draws on the Board. A dashed amber line runs from each pinning Piece to the King behind. Click any Piece, of either Side, and arrows show what it attacks and what attacks it. The Threats button looks pressed while the mode is on.

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

Chess, Chapter 10 finishedZip of a Myth project · 11.9 MB

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.

10.1 The Idea

Play your game from Chapter 9, with Threats on. Play e2 to e4, and after Black’s reply, your Queen from d1 to h5. Unless Black has moved its f-Pawn or g-Pawn, an amber bar appears under its Pawn on f7. It’s pinned. But by what? You trace the diagonal with a finger, from your Queen to Black’s King, and there it is. The Board knew all along. It kept the answer in pinnedBy, and Chapter 9 promised that something would read it.

This chapter reads it, and draws it. A dashed amber line runs from the pinning Piece to the King it threatens, across the pinned Piece in between.

A red outline leaves the same question open. It says a Piece could be captured, not by whom. So this chapter adds arrows. Click any Piece in Threats mode, yours or the Computer’s, and you inspect it. Its Square turns teal, and arrows fan out from it. Arrows run out along every line it could capture on, red where they reach an enemy Piece and teal otherwise. A red arrow comes in from every enemy Piece that could capture it.

The hard part is the drawing. Lines and arrows aren’t Objects, so they don’t stay on the Board. They come from a Draw Event, an Event that runs every frame. What it draws lasts one frame. It works like a flip book: each page is drawn fresh, and the last page is already gone. So the Draw Event has to be quick. The arrows are worked out once, each time the Board changes, into a table. Every frame, the Draw Event only reads that table.

10.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 lines first show.

  • A click on a Piece in Threats mode -> Select and Move -> Inspect Piece names it in Board State’s inspected -> positionChanged -> Square Marks tints its Square teal.
  • The Board changes -> positionChanged -> Draw Threat Lines calls Find Arrows -> one row per arrow in the Arrows table.
  • Every frame -> Draw Threat Lines’ Draw Event -> Draw Arrow for each row of Arrows, Draw Dashed Line for each pin in Threats.
  • The same positionChanged -> Show Threats Button Pressed draws the Threats button pressed, or plain.

10.3 Set Up

There’s nothing new to place in the Scene. Threats Button gains its own Show Threats Button Pressed Logic in the chapter’s last part. Every placement of that variant gains it too.

10.4 Looking at Any Piece

First, the Board learns to look at a Piece without moving it.

10.4.1 Board State

Data StructureBoard StateAddDelete

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). 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).

sideToMoveTextdefault White
selectedTextdefault (empty)
lastFromTextdefault (empty)
lastToTextdefault (empty)
checkSquareTextdefault (empty)
enPassantSquareTextdefault (empty)
promotionSquareTextdefault (empty)
playerSideTextdefault White
fiftyMoveCountNumberdefault 0
isFlippedBooleandefault false
strengthTextdefault Casual
chosenSideTextdefault (empty)
chosenStrengthTextdefault (empty)
isShowingThreatsBooleandefault false
inspectedTextdefault (empty)

Board State gets one more field at the end, inspected, for the Square of the inspected Piece.

10.4.2 Inspect Piece

FunctionInspect Piece

In Threats mode, inspects the Piece standing on a Square the Player clicked (Board State's inspected), whichever Side it belongs to, so its arrows can be shown without selecting it for a Move. A click on an empty Square, or any click outside Threats mode, inspects nothing. gameRules: the Game Rules Object. square: the Square that was clicked.

PARAMETERS
NameTypeStarts at
gameRulesObjectStructural
Required Components
Slot NameboardState
Component TypeData Structure
Required SchemaBoard State
squareText
ACTIONS
1IfgameRules.boardState.isShowingThreats == true && data('Position', square, 'kind') != ""
Then
2Change Component Value
Component Value
gameRules.boardState.inspected
Value
square
3Otherwise
4Change Component Value
Component Value
gameRules.boardState.inspected
Value
''

Inspect Piece asks two things. Is Threats mode on? Is there a Piece on the clicked Square, of either Side? Then that Square is inspected. Any other click inspects nothing, and the empty '' clears it.

10.4.3 Select and Move

Logic ComponentSelect and MoveAttach to: Game RulesAddDelete

Decides what a click on a Square means. A click on a Square the selected Piece may move to makes that Move (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).

Attach toAny Object
DATA STRUCTURES
Board Stateself.boardState
Select and Move Dataself.selectAndMoveData
On Square Clicked
WhensquareClickedCustom Event
1Declare Variable
Type
Text
Name
selectedSquare
2Declare Variable
Type
Text
Name
clickedSquare
3Forget The HintFunction
4Change Variable
Variable
clickedSquare
Value
other.squareInfo.name
5Change Variable
Variable
selectedSquare
Value
self.boardState.selected
6IfselectedSquare != "" && data('Targets', clickedSquare, 'reachable') == true
Then
7Change Component Value
Component Value
self.selectAndMoveData.side
Value
self.boardState.sideToMove
8Ifdata('Position', selectedSquare, 'kind') == "Pawn" && data('Position', clickedSquare, 'rank') == data('Sides', self.selectAndMoveData.side, 'promotionRank')
Then
9Open Promotion ChoiceFunction
gameRules
self
square
clickedSquare
10Otherwise
11Make MoveFunction
gameRules
self
from
selectedSquare
to
clickedSquare
12Trigger Custom Event on Object
Custom Event
moveMade
13Otherwise
14Ifdata('Position', clickedSquare, 'side') == self.boardState.sideToMove && clickedSquare != selectedSquare
Then
15Change Component Value
Component Value
self.boardState.selected
Value
clickedSquare
16Find Legal MovesFunction
from
clickedSquare
enPassantSquare
self.boardState.enPassantSquare
17Otherwise
18Change Component Value
Component Value
self.boardState.selected
Value
''
19Clear TargetsFunction
20Inspect PieceFunction
gameRules
self
square
clickedSquare
21Trigger Custom Event
Custom Event
positionChanged

Select and Move calls Inspect Piece on every click that isn’t a Move. Those clicks go down the outer Else, and the new step goes last on its line. Add Inspect Piece apart from the line. Use the round handles to join it after step 14’s If, inside the outer Else. It follows that If and its own Else, on the same line.

So a click on your own Piece does two things: it selects the Piece and inspects it. Click it again, and step 18 clears the selection. The Piece stays inspected.

10.4.4 Start Turn, Go Back To The Start and Show Threats

Logic ComponentStart TurnAttach to: Game RulesAddDelete

Starts each Turn: the first one (White's) when the game begins, and the next one after every Move, which it first writes down in the Move History (Record Move) before 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").

Attach toAny Object
DATA STRUCTURES
Board Stateself.boardState
When Created
WhenCreate
1Start The TurnFunction
gameRules
self
2Trigger Custom Event
Custom Event
positionChanged
On Move Made
WhenmoveMadeCustom Event
1Record MoveFunction
gameRules
self
2Pass The TurnFunction
gameRules
self
3Change Component Value
Component Value
self.boardState.selected
Value
''
4Change Component Value
Component Value
self.boardState.inspected
Value
''
5Clear TargetsFunction
6Start The TurnFunction
gameRules
self
FunctionGo Back To The StartAddDelete

Puts the game back as it is when a game starts: the starting position, every castling allowed again, no position seen yet, and nothing marked (the tables as they were made), and Board State as a game starts: White to move, no en passant Square, no Moves counted for the fifty-move rule, no last Move, and nothing selected or inspected. gameRules: the Game Rules Object.

PARAMETERS
NameTypeStarts at
gameRulesObjectStructural
Required Components
Slot NameboardState
Component TypeData Structure
Required SchemaBoard State
ACTIONS
1Reset Data Table
Table
Position
2Reset Data Table
Table
Castling
3Reset Data Table
Table
Position History
4Clear TargetsFunction
5Change Component Value
Component Value
gameRules.boardState.sideToMove
Value
'White'
6Change Component Value
Component Value
gameRules.boardState.enPassantSquare
Value
''
7Change Component Value
Component Value
gameRules.boardState.fiftyMoveCount
Value
0
8Change Component Value
Component Value
gameRules.boardState.lastFrom
Value
''
9Change Component Value
Component Value
gameRules.boardState.lastTo
Value
''
10Change Component Value
Component Value
gameRules.boardState.selected
Value
''
11Change Component Value
Component Value
gameRules.boardState.inspected
Value
''
Logic ComponentShow ThreatsAttach to: Game RulesAddDelete

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) and ends any inspecting, fills or empties the Threats table, shows or hides the legend, keeps the choice on this device, and every Square redraws ("positionChanged").

Attach toAny Object
DATA STRUCTURES
Board Stateself.boardState
When the Game Starts
WhenGame Start
1Clear ThreatsFunction
2Load Persistent Value
Action Name
savedThreats
Key
"threats"
Default
self.boardState.isShowingThreats
3IfsavedThreats.value == true
Then
4Change Component Value
Component Value
self.boardState.isShowingThreats
Value
true
When Created
WhenCreate
1Ifself.boardState.isShowingThreats == true
Then
2Set UI Group Visibility
UI Group
"Threats Legend"
On Turn Started
WhenturnStartedCustom Event
1Ifself.boardState.isShowingThreats == true
Then
2Find ThreatsFunction
gameRules
self
On Threats Pressed
WhenthreatsPressedCustom Event
1Can Show ThreatsFunction
Action Name
threatsLook
2IfthreatsLook.value == true
Then
3Change Component Value
Component Value
self.boardState.inspected
Value
''
4Ifself.boardState.isShowingThreats == true
Then
5Change Component Value
Component Value
self.boardState.isShowingThreats
Value
false
6Clear ThreatsFunction
7Set UI Group Visibility
UI Group
"Threats Legend"
Visible
No
8Otherwise
9Change Component Value
Component Value
self.boardState.isShowingThreats
Value
true
10Find ThreatsFunction
gameRules
self
11Set UI Group Visibility
UI Group
"Threats Legend"
12Ifself.boardState.selected != ""
Then
13Find Legal MovesFunction
from
self.boardState.selected
enPassantSquare
self.boardState.enPassantSquare
14Save Persistent Value
Action Name
persistentWriteResult
Key
"threats"
Value
self.boardState.isShowingThreats
15Trigger Custom Event
Custom Event
positionChanged

Three places forget the inspected Piece, each with one Change Component Value and an empty ''. A Move forgets it, because the Board is new. So does Undo, through Go Back To The Start, and so does turning Threats mode on or off.

In Start Turn, step 4 goes between two old steps. Add it apart from the line, then join it after step 3 with the round handles. Clear Targets stays behind it. In Go Back To The Start, step 11 comes last.

Show Threats’ step 3 goes first inside step 2’s If. Its Add Inside Then adds Change Component Value, apart from the old steps. Use the round handles to join it to the If’s True handle. The old If follows it.

10.4.5 Show Highlight

FunctionShow Highlight

Shows a Square's highlight in one color: tints the Square and sets how strongly the tint shows. square: the Square Object to highlight. color: the color. alpha: how strongly it shows, from 0 (not at all) to 1 (solid).

PARAMETERS
NameTypeStarts at
squareObject
colorColor#ffffff
alphaNumber1
ACTIONS
1Change Sprite Tint Property
Applies to
square
Value
color
2Set Alpha
Applies to
square
Alpha
alpha

Square Marks is about to tint its Square six different ways, and each one takes two steps: a tint, then how strongly it shows. That’s twelve steps that differ only in their two values. This Function holds the pair once, in two steps of its own. Its square is an Object with nothing listed under it, so leave it as it comes. It only tints that Object and sets its Alpha.

10.4.6 Square Marks

Logic ComponentSquare MarksAttach to: SquareAddDelete

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 inspected in Threats mode, 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 the Square's Shared Variables (Show Highlight shows them). While the Computer's Move slides ("slideStarted"), no highlight is shown.

Attach toAny Object
DATA STRUCTURES
Square Infoself.squareInfo
SHARED VARIABLES
NameTypeKindStarting Value
selectedColorColorConstant#f4d273
selectedAlphaNumberConstant0.44
lastMoveColorColorConstant#e9c44f
lastMoveAlphaNumberConstant0.32
checkColorColorConstant#cd493f
checkAlphaNumberConstant0.66
hintColorColorConstant#61a9b8
hintAlphaNumberConstant0.35
attackedColorColorConstant#d64b46
attackedAlphaNumberConstant0.4
unprotectedAlphaNumberConstant0.5
inspectedColorColorConstant#56a59d
inspectedAlphaNumberConstant0.27
On Position Changed
WhenpositionChangedCustom Event
1Set Alpha
Alpha
0
2Ifself.squareInfo.name == other.boardState.lastFrom || self.squareInfo.name == other.boardState.lastTo
Then
3Change Sprite Tint Property
Value
@self.lastMoveColor
4Set Alpha
Alpha
@self.lastMoveAlpha
3Show HighlightFunction
square
self
color
@self.lastMoveColor
alpha
@self.lastMoveAlpha
4Ifdata('Threats', self.squareInfo.name, 'isAttacked') == true
Then
6Change Sprite Tint Property
Value
@self.attackedColor
7Set Alpha
Alpha
@self.attackedAlpha
5Show HighlightFunction
square
self
color
@self.attackedColor
alpha
@self.attackedAlpha
6Ifdata('Threats', self.squareInfo.name, 'isUnprotected') == true
Then
7Set Alpha
Alpha
@self.unprotectedAlpha
8Ifself.squareInfo.name == other.boardState.inspected
Then
9Show HighlightFunction
square
self
color
@self.inspectedColor
alpha
@self.inspectedAlpha
10Ifself.squareInfo.name == other.boardState.selected
Then
11Change Sprite Tint Property
Value
@self.selectedColor
12Set Alpha
Alpha
@self.selectedAlpha
11Show HighlightFunction
square
self
color
@self.selectedColor
alpha
@self.selectedAlpha
12Ifself.squareInfo.name == data('Chosen Moves', 'Hint', 'from') || self.squareInfo.name == data('Chosen Moves', 'Hint', 'to')
Then
14Change Sprite Tint Property
Value
@self.hintColor
15Set Alpha
Alpha
@self.hintAlpha
13Show HighlightFunction
square
self
color
@self.hintColor
alpha
@self.hintAlpha
14Ifself.squareInfo.name == other.boardState.checkSquare
Then
17Change Sprite Tint Property
Value
@self.checkColor
18Set Alpha
Alpha
@self.checkAlpha
15Show HighlightFunction
square
self
color
@self.checkColor
alpha
@self.checkAlpha
On Slide Started
WhenslideStartedCustom Event
1Set Alpha
Alpha
0

Square Marks hands each pair of steps to the new Function. In each If, delete the two struck steps, so its True line is empty. Add Inside Then adds the Function. Join it there, with self, the color that If used before, and its alpha. Step 7, the kept Set Alpha in the unprotected If, stays as it was. It only makes the red a little stronger.

One If is new, step 8, for the teal, and it goes before the selected Square’s If. Add the new If apart from the line. Join it after step 6’s If with the round handles. The selected Square’s If follows. Add Inside Then adds the Function to the new If’s True line, ready to join.

Why there? Each If paints over the one before. So a Piece that’s selected and inspected at once shows its gold, and the teal shows only on a Piece that isn’t selected. Its inspectedAlpha is 0.27, a soft wash, lighter than any other mark.

Run the game, and press Threats. Click Black’s Knight on b8, and its Square turns teal. Now click your Pawn on e2. It turns gold, with its dots. Click the same Pawn a second time, and the dots go while its Square turns teal.

10.5 Arrows, Worked Out Once

Now the arrows. This part fills a table, and nothing draws it yet. The game plays as before until the next part, where the arrows appear.

10.5.1 Arrows

Data StructureArrows Entry

One row of the "Arrows" Data Table, which follows it. The arrows Threats mode shows for the selected or inspected Piece, one row per arrow (each row is named by its number). from and to: the Squares the arrow runs from and to (a1 to h8). color: the arrow's color, with how strongly it shows (#rrggbbaa). Find Arrows fills it after every change; Draw Threat Lines draws it. Empty when no arrow is shown.

fromTextdefault (empty)
toTextdefault (empty)
colorColordefault #000000
Data TableArrowsData Structure: Arrows Entry

Arrows has one row for each arrow: the Square it starts from, the Square it points to, and its color. Its columns come from Arrows Entry. It starts with no rows. Find Arrows fills it each time the Board changes.

10.5.2 Find Attacker Along

Data StructureFind Attacker Along Result

What Find Attacker Along returns. attackers: 1 when the first Piece in the way belongs to the attacking Side and is of the direction's Kind, else 0. attacker: the Square of that Piece, or empty.

attackersNumberdefault 0
attackerTextdefault (empty)
FunctionFind Attacker Along

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). attacker (returned): the Square of that attacking Piece, or empty when there is none. square: the Square looked from (a1 to h8). direction: the row (0 = first) of the Directions table. bySide: the attacking Side.

PARAMETERS
NameTypeStarts at
squareText
directionNumber0
bySideText
RETURN TYPEFind Attacker Along Result (a Data Structure)
ACTIONS
1Declare Variable
Type
Text
Name
piece
2Declare Variable
Name
attackers
Value
0
3Declare Variable
Type
Text
Name
attacker
4Find First Piece AlongFunction
Action Name
pieceLook
square
square
direction
direction
bySide
bySide
5Change Variable
Variable
piece
Value
pieceLook.value
6Ifpiece != "" && data('Position', piece, 'side') == bySide && data('Position', piece, 'kind') == data('Directions', direction, 'kind')
Then
7Change Variable
Variable
attackers
Value
1
8Change Variable
Variable
attacker
Value
piece
9Return
attackers
attackers
attacker
attacker

The answer has grown. Find Attacker Along now returns a Data Structure, Find Attacker Along Result. It always knew whether some Piece could capture onto the Square. Now it names that Piece’s Square too. The Return has two fields, attackers and attacker, and step 8 writes the new name.

Change its Return Type to that Data Structure, and add the new Declare, attacker. The full card shows the line with both returned fields.

Is Square Attacked reads this answer too. Its step 6 now takes attackerLook.attackers in place of attackerLook.value. The count stays the same.

FunctionIs Square AttackedAddDelete

Answers whether any Piece of one Side could capture onto a Square. square: the Square (a1 to h8). bySide: the attacking Side (White or Black). attacked (returned): yes when at least one of its Pieces attacks the Square. attackers (returned): how many do.

PARAMETERS
squareText
bySideText
RETURN TYPEIs Square Attacked Result (a Data Structure)
ACTIONS
1Declare Variable
Name
attackerCount
Value
0
2Declare Variable
Type
Boolean
Name
attacked
Value
false
3RepeatdataCount('Directions')times
Each time
4Ifdata('Directions', local.index, 'canCapture') == true
Then
5Find Attacker AlongFunction
Action Name
attackerLook
square
square
direction
local.index
bySide
bySide
6Change Variable
Variable
attackerCount
Operator
+=
Value
attackerLook.attackers
Value
attackerLook.value
7IfattackerCount > 0
Then
8Change Variable
Variable
attacked
Value
true
9Return
attacked
attacked
attackers
attackerCount

10.5.3 Find Farthest Square Along

FunctionFind Farthest Square Along

Follows one direction of the Directions table from a Piece, Square by Square, as far as the Piece reaches that way: up to the first Piece in the way (of either Side), the edge of the Board, or the end of the direction's steps. from: the Square of the Piece (a1 to h8). direction: the row (0 = first) of the Directions table (a Pawn's direction is turned round by its Side's forward). farthest (returned): the last Square reached, or empty when the first step leaves the Board.

PARAMETERS
NameTypeStarts at
fromText
directionNumber0
RETURN TYPEText
ACTIONS
1Declare Variable
Type
Text
Name
side
2Declare Variable
Name
forward
Value
1
3Declare Variable
Name
file
Value
0
4Declare Variable
Name
rank
Value
0
5Declare Variable
Name
stepsLeft
Value
0
6Declare Variable
Type
Boolean
Name
isOnBoard
Value
false
7Declare Variable
Name
index
Value
0
8Declare Variable
Type
Boolean
Name
keepGoing
Value
true
9Declare Variable
Type
Text
Name
farthest
10Change Variable
Variable
side
Value
data('Position', from, 'side')
11Change Variable
Variable
forward
Value
data('Sides', side, 'forward')
12Change Variable
Variable
file
Value
data('Position', from, 'file')
13Change Variable
Variable
rank
Value
data('Position', from, 'rank')
14Change Variable
Variable
stepsLeft
Value
data('Directions', direction, 'steps')
15WhilekeepGoing == true && stepsLeft > 0
Each time
16Change Variable
Variable
file
Operator
+=
Value
data('Directions', direction, 'fileStep')
17Change Variable
Variable
rank
Operator
+=
Value
data('Directions', direction, 'rankStep') * forward
18Change Variable
Variable
stepsLeft
Operator
-=
Value
1
19Change Variable
Variable
isOnBoard
Value
false
20Iffile >= 1 && file <= 8 && rank >= 1 && rank <= 8
Then
21Change Variable
Variable
isOnBoard
Value
true
22Change Variable
Variable
index
Value
(rank - 1) * 8 + (file - 1)
23Change Variable
Variable
keepGoing
Value
false
24IfisOnBoard == true
Then
25Change Variable
Variable
farthest
Value
dataRow('Position', index)
26IfisOnBoard == true && data('Position', index, 'kind') == ""
Then
27Change Variable
Variable
keepGoing
Value
true
28Return
Value
farthest

An arrow along a line should run as far as the Piece reaches. Find Farthest Square Along follows one direction from a Piece, Square by Square. It stops at the first Piece in the way, of either Side, or at the edge, or when the direction’s steps run out. The last Square it reached is farthest, returned as Text.

Its walk is close to Find First Piece Along’s, from Chapter 9, with one change. It goes forward, the way the Piece moves, so file and rank step with +=. The old walk looked back, with -=.

Steps 25 and 26 read Position by a row number, index, which step 22 works out. So a1 is row 0, b1 is row 1, and so on, up to h8, the last. dataRow gives that row’s name, which is the Square’s own.

10.5.4 Add Arrow

FunctionAdd Arrow

Adds one arrow at the end of the Arrows table. from and to: the Squares it runs from and to (a1 to h8). color: its color, with how strongly it shows.

PARAMETERS
NameTypeStarts at
fromText
toText
colorColor#ffffff
ACTIONS
1Declare Variable
Name
lastRow
Value
0
2Control Data Row
Action Name
addedRow
Table
Arrows
Row
dataCount('Arrows')
3Change Variable
Variable
lastRow
Value
dataCount('Arrows') - 1
4Set Data Cell
Table
Arrows
Row
lastRow
Column
'from'
Value
from
5Set Data Cell
Table
Arrows
Row
lastRow
Column
'to'
Value
to
6Set Data Cell
Table
Arrows
Row
lastRow
Column
'color'
Value
color

Add Arrow writes one arrow. Control Data Row adds a row at the end of Arrows. Its Operation stays on adding the row. Its Row, dataCount(‘Arrows’), is one past the last, so the row is always new. lastRow is its number, and three Set Data Cells fill it in. The color column is Color. It holds the color’s code, eight digits and all, and Set Draw Color reads it later.

10.5.5 Add Arrow Along

FunctionAdd Arrow Along

Adds the arrow a Piece shows along one of its directions, to the farthest Square it reaches that way (Find Farthest Square Along): in toEnemyColor when an enemy Piece stands there, else in otherColor (an empty Square, or a Piece of its own Side that it protects). No arrow when the direction leaves the Board at once. from: the Square of the Piece. direction: the row (0 = first) of the Directions table.

PARAMETERS
NameTypeStarts at
fromText
directionNumber0
toEnemyColorColor#ffffff
otherColorColor#ffffff
ACTIONS
1Declare Variable
Type
Text
Name
enemy
2Declare Variable
Type
Text
Name
farthest
3Declare Variable
Type
Color
Name
color
Value
'#ffffff'
4Change Variable
Variable
enemy
Value
data('Sides', data('Position', from, 'side'), 'opponent')
5Find Farthest Square AlongFunction
Action Name
reachLook
from
from
direction
direction
6Change Variable
Variable
farthest
Value
reachLook.value
7Change Variable
Variable
color
Value
otherColor
8Ifdata('Position', farthest, 'side') == enemy
Then
9Change Variable
Variable
color
Value
toEnemyColor
10Iffarthest != ""
Then
11Add ArrowFunction
from
from
to
farthest
color
color

Add Arrow Along draws a Piece’s arrow along one direction, out to the Square Find Farthest Square Along names. An enemy Piece there makes it red, toEnemyColor. Anything else makes it teal, otherColor: an empty Square, or a Piece of its own Side that it protects. If the first step leaves the Board, farthest is empty, and there’s no arrow at all.

A Knight, a King or a Pawn reaches only one Square each way, so it gets one short arrow for each Square it attacks. A Queen, a Rook or a Bishop gets one long arrow for each direction.

10.5.6 Add Threat Arrow

FunctionAdd Threat Arrow

Adds an arrow from an enemy Piece that attacks a Piece, when it could really capture it: the Capture is a Legal Move (Can Capture), or the Piece attacked is a King. from: the Square of the attacking Piece (empty: no arrow). to: the Square it would move to. taken: the Square of the Piece it would take (not to for en passant). enPassantSquare: the en passant Square, for an en passant Capture, else empty. color: the arrow's color.

PARAMETERS
NameTypeStarts at
fromText
toText
takenText
enPassantSquareText
colorColor#ffffff
ACTIONS
1Declare Variable
Type
Boolean
Name
isThreat
Value
false
2Iffrom != ""
Then
3Can CaptureFunction
Action Name
captureLook
from
from
to
to
taken
taken
enPassantSquare
enPassantSquare
4Change Variable
Variable
isThreat
Value
captureLook.value
5Ifdata('Position', taken, 'kind') == "King"
Then
6Change Variable
Variable
isThreat
Value
true
7IfisThreat == true
Then
8Add ArrowFunction
from
from
to
to
color
colorInput

Add Threat Arrow adds an arrow coming in, from an enemy Piece. It only counts if the Capture is a Legal Move, so Can Capture from Chapter 9 decides.

Here’s the one exception. An enemy Piece that attacks a King is giving Check, even when it’s pinned to its own King and Can Capture says no. That’s the arrow you’d most want to see. So step 6 makes isThreat true for a King, whatever Can Capture said.

10.5.7 Find Arrows Toward

FunctionFind Arrows Toward

Adds an arrow from every enemy Piece that can capture a Piece (Add Threat Arrow): each direction a Piece captures along is looked along backwards from it for an enemy attacker (Find Attacker Along). A Pawn that can be taken en passant also gets an arrow from each enemy Pawn that can take it, to the en passant Square. gameRules: the Game Rules Object. target: the Square of the Piece. color: the arrows' color.

PARAMETERS
NameTypeStarts at
gameRulesObjectStructural
Required Components
Slot NameboardState
Component TypeData Structure
Required SchemaBoard State
targetText
colorColor#ffffff
ACTIONS
1Declare Variable
Type
Text
Name
enemy
2Declare Variable
Type
Text
Name
enPassant
3Change Variable
Variable
enemy
Value
data('Sides', data('Position', target, 'side'), 'opponent')
4Find Passed PawnFunction
Action Name
passedLook
gameRules
gameRules
5IfpassedLook.value == target
Then
6Change Variable
Variable
enPassant
Value
gameRules.boardState.enPassantSquare
7RepeatdataCount('Directions')times
Each time
8Ifdata('Directions', local.index, 'canCapture') == true
Then
9Find Attacker AlongFunction
Action Name
attackerLook
square
target
direction
local.index
bySide
enemy
10Add Threat ArrowFunction
from
attackerLook.attacker
to
target
taken
target
enPassantSquare
''
color
colorInput
11Ifdata('Directions', local.index, 'canCapture') == true && data('Directions', local.index, 'kind') == "Pawn" && enPassant != ""
Then
12Find Attacker AlongFunction
Action Name
enPassantLook
square
enPassant
direction
local.index
bySide
enemy
13Add Threat ArrowFunction
from
enPassantLook.attacker
to
enPassant
taken
target
enPassantSquare
enPassant
color
colorInput

Find Arrows Toward finds the arrows that come in, trying every direction a Piece can capture along. For each one, Find Attacker Along looks back from the target, as it does for Check, and its new attacker names the enemy at the far end. Add Threat Arrow adds that arrow.

Steps 4 to 6 and step 11 are en passant, again. A Pawn that can be taken en passant is taken from another Square, the en passant Square. Steps 4 to 6 ask Find Passed Pawn whether this Piece is that Pawn, and if so, enPassant names the Square. Step 11 then gives each enemy Pawn that attacks it an arrow there too.

10.5.8 Find Arrows

FunctionFind Arrows

Fills the Arrows table for the position on the Board. In Threats mode the selected Piece, or, when none is selected, the inspected Piece, shows an arrow along each direction it captures along, to the farthest Square it reaches (Add Arrow Along), and an arrow from every enemy Piece that can capture it (Find Arrows Toward). Outside Threats mode, or with no such Piece, the table stays empty. gameRules: the Game Rules Object. toEnemyColor, otherColor: the colors of its own arrows to an enemy Piece and to anything else. attackerColor: the color of the arrows from enemy Pieces.

PARAMETERS
NameTypeStarts at
gameRulesObjectStructural
Required Components
Slot NameboardState
Component TypeData Structure
Required SchemaBoard State
toEnemyColorColor#ffffff
otherColorColor#ffffff
attackerColorColor#ffffff
ACTIONS
1Declare Variable
Type
Text
Name
focus
2Reset Data Table
Table
Arrows
3Change Variable
Variable
focus
Value
gameRules.boardState.selected
4Iffocus == ""
Then
5Change Variable
Variable
focus
Value
gameRules.boardState.inspected
6IfgameRules.boardState.isShowingThreats != true
Then
7Change Variable
Variable
focus
Value
''
8RepeatdataCount('Directions')times
Each time
9Iffocus != "" && data('Directions', local.index, 'kind') == data('Position', focus, 'kind') && data('Directions', local.index, 'canCapture') == true
Then
10Add Arrow AlongFunction
from
focus
direction
local.index
toEnemyColor
toEnemyColor
otherColor
otherColor
11Iffocus != ""
Then
12Find Arrows TowardFunction
gameRules
gameRules
target
focus
color
attackerColor
13IfgameRules.boardState.selected != ""
Then
14Find Legal MovesFunction
from
gameRules.boardState.selected
enPassantSquare
gameRules.boardState.enPassantSquare
15Otherwise
16Clear TargetsFunction

Find Arrows is the one that runs on each change. It empties Arrows first. Which Piece shows its arrows? The selected one, or, with none selected, the inspected one. Outside Threats mode, none at all, and the table stays empty.

Next, the Repeat walks the Directions table. Each row of that Piece’s Kind that captures gets Add Arrow Along. After that, Find Arrows Toward adds the arrows coming in.

The last If cleans up after itself. Can Capture tried its Captures in the Targets table, where your dots come from. So the selected Piece’s Legal Moves are marked again. With no Piece selected, Clear Targets empties the table, so no stray dots show.

10.6 Drawing Every Frame

Now the table becomes lines on the screen. Draw counts in screen pixels, so first the Board’s meters need turning into pixels.

10.6.1 Square Screen Point

Data StructureSquare Screen Point Result

What Square Screen Point returns: x and y, the center of the Square on the game screen, in screen pixels from the screen's bottom-left corner.

xNumberdefault 0
yNumberdefault 0
FunctionSquare Screen Point

Answers where the center of a Square is on the game screen, in screen pixels counted from the bottom-left corner of the screen, as the Draw Actions count them. It asks Square Position where the Square is in the world, in meters, and turns meters into pixels: the camera never moves, so the world's (0, 0) is the screen's bottom-left corner. square: the Square (a1 to h8). isFlipped, squareMeters, boardLeft, boardBottom: as for Square Position. pixelsPerMeter: the project's Pixels Per Unit (32). It returns x and y: the Square's center on the screen.

PARAMETERS
NameTypeStarts at
squareText
isFlippedBooleanNo
squareMetersNumber3.75
boardLeftNumber0
boardBottomNumber4.375
pixelsPerMeterNumber32
RETURN TYPESquare Screen Point Result (a Data Structure)
ACTIONS
1Declare Variable
Name
x
Value
0
2Declare Variable
Name
y
Value
0
3Square PositionFunction
Action Name
worldPoint
square
square
isFlipped
isFlipped
squareMeters
squareMeters
boardLeft
boardLeft
boardBottom
boardBottom
4Change Variable
Variable
x
Value
worldPoint.x * pixelsPerMeter
5Change Variable
Variable
y
Value
worldPoint.y * pixelsPerMeter
6Return
x
x
y
y

Square Screen Point answers where a Square’s center is on the screen. Square Position already knows where that is in the world. It answers in meters. This Function returns its own x and y in Square Screen Point Result. Its pixelsPerMeter is your project’s Pixels Per Unit, 32. Multiply the meters by it, and they become pixels on the screen.

Why does a plain multiplication work? The camera never moves. So the world’s corner at 0, 0 is the screen’s bottom-left corner, and that’s where Draw counts from.

10.6.2 Draw Dashed Line

FunctionDraw Dashed Line

Draws one dashed line, in the current Draw Color, from one point on the game screen toward another. Use it in a Draw Event. fromX, fromY: where the line starts; toX, toY: where it points to (screen pixels, from the bottom-left corner). startGap: how far after the start the first dash begins; endGap: how far before the end the last dash stops. dashLength: how long each dash is; dashSpacing: how far one dash starts after the one before (more than dashLength leaves a gap). width: how thick the line is. All in screen pixels.

PARAMETERS
NameTypeStarts at
fromXNumber0
fromYNumber0
toXNumber0
toYNumber0
startGapNumber0
endGapNumber0
dashLengthNumber0
dashSpacingNumber0
widthNumber2
ACTIONS
1Declare Variable
Name
distance
Value
0
2Declare Variable
Name
stepX
Value
0
3Declare Variable
Name
stepY
Value
0
4Declare Variable
Name
along
Value
0
5Declare Variable
Name
lastPoint
Value
0
6Declare Variable
Name
dashEnd
Value
0
7Change Variable
Variable
distance
Value
hypot(toX - fromX, toY - fromY)
8Change Variable
Variable
along
Value
startGap
9Change Variable
Variable
lastPoint
Value
distance - endGap
10Ifdistance > 0
Then
11Change Variable
Variable
stepX
Value
(toX - fromX) / distance
12Change Variable
Variable
stepY
Value
(toY - fromY) / distance
13Whilealong < lastPoint
Each time
14Change Variable
Variable
dashEnd
Value
along + dashLength
15IfdashEnd > lastPoint
Then
16Change Variable
Variable
dashEnd
Value
lastPoint
17Draw Line
X 1
fromX + stepX * along
Y 1
fromY + stepY * along
X 2
fromX + stepX * dashEnd
Y 2
fromY + stepY * dashEnd
Line Width
width
18Change Variable
Variable
along
Operator
+=
Value
dashSpacing

Draw Dashed Line draws one dashed line between two points. First it finds the distance, with hypot. Next come stepX and stepY: how far one pixel along the line moves across, and how far up.

A Draw Line is one straight stroke, from X 1, Y 1 to X 2, Y 2, in the Draw Color of the moment. It’s in Advanced Mode, with Set Draw Color and the Draw Event.

The While draws dash after dash. Its Variable along says how far down the line it has got. Each time round, Draw Line draws one dash, from along to along plus dashLength, and along moves on by dashSpacing. A dash that would run past lastPoint is cut short there. When along reaches lastPoint, the line is done.

10.6.3 Draw Arrow

FunctionDraw Arrow

Draws one arrow, in the current Draw Color, from one point on the game screen toward another: a straight shaft and a head of two short lines at its end. Use it in a Draw Event. fromX, fromY: where the arrow starts; toX, toY: where it points to (screen pixels, from the bottom-left corner). startGap: how far after the start the shaft begins; endGap: how far before the end the head's tip is. headLength: how far back the head reaches along the shaft. headSpread: how far each side of the head reaches out, as a share of headLength. width: how thick the lines are. All but headSpread in screen pixels.

PARAMETERS
NameTypeStarts at
fromXNumber0
fromYNumber0
toXNumber0
toYNumber0
startGapNumber0
endGapNumber0
headLengthNumber0
headSpreadNumber0.6
widthNumber2
ACTIONS
1Declare Variable
Name
distance
Value
0
2Declare Variable
Name
stepX
Value
0
3Declare Variable
Name
stepY
Value
0
4Declare Variable
Name
tipX
Value
0
5Declare Variable
Name
tipY
Value
0
6Declare Variable
Name
backX
Value
0
7Declare Variable
Name
backY
Value
0
8Declare Variable
Name
sideX
Value
0
9Declare Variable
Name
sideY
Value
0
10Change Variable
Variable
distance
Value
hypot(toX - fromX, toY - fromY)
11Ifdistance > 0
Then
12Change Variable
Variable
stepX
Value
(toX - fromX) / distance
13Change Variable
Variable
stepY
Value
(toY - fromY) / distance
14Change Variable
Variable
tipX
Value
toX - stepX * endGap
15Change Variable
Variable
tipY
Value
toY - stepY * endGap
16Change Variable
Variable
backX
Value
tipX - stepX * headLength
17Change Variable
Variable
backY
Value
tipY - stepY * headLength
18Change Variable
Variable
sideX
Value
stepY * headLength * headSpread
19Change Variable
Variable
sideY
Value
stepX * headLength * headSpread
20Draw Line
X 1
fromX + stepX * startGap
Y 1
fromY + stepY * startGap
X 2
tipX
Y 2
tipY
Line Width
width
21Draw Line
X 1
tipX
Y 1
tipY
X 2
backX - sideX
Y 2
backY + sideY
Line Width
width
22Draw Line
X 1
tipX
Y 1
tipY
X 2
backX + sideX
Y 2
backY - sideY
Line Width
width

Draw Arrow is the same idea, with three strokes in place of dashes. It finds the distance and the steps across and up, as Draw Dashed Line does. The tip stops endGap short of the end. That keeps the arrow off the Piece it points at. The head’s two strokes start at the tip and lean back, one to each side.

The two side steps swap stepX and stepY. That’s what turns a step along the line into a step across it.

10.6.4 Draw Threat Lines

Data StructureDraw Threat Lines Data

What Draw Threat Lines remembers on the Game Rules while it draws. pinned and pinner: the Squares of a pinned Piece and of the Piece that pins it. arrowFrom and arrowTo: the Squares of the arrow being drawn.

pinnedTextdefault (empty)
pinnerTextdefault (empty)
arrowFromTextdefault (empty)
arrowToTextdefault (empty)
Logic ComponentDraw Threat LinesAttach to: Game Rules

Draws the lines of Threats mode over the Board: the arrows of the selected or inspected Piece (the Arrows table, which Find Arrows fills on every "positionChanged": red to an enemy Piece it attacks and teal to any other Square it reaches, and red from each enemy Piece that can capture it), and a dashed amber line from every Piece that pins an enemy Piece to its King, to that King (the Threats table's pinnedBy, which Find Threats fills only while Threats mode is on). The Draw Event runs every frame; nothing is drawn while the Computer's Move slides ("slideStarted") or the Promotion choice is open (Game State Promotion), until the next "positionChanged". arrowToEnemyColor, arrowToOtherColor, arrowFromAttackerColor: the arrows' colors. arrowWidth, arrowGapAtStart, arrowGapAtEnd, arrowHeadLength: the arrows' sizes, in Squares (0.035, 0.22, 0.27, 0.11); arrowHeadSpread: how far each side of a head reaches out, as a share of its length (0.6). pinColor: the pin lines' color. pinLineWidth, pinDashLength, pinDashSpacing: the dashes, in Squares (0.03, 0.12, 0.22). pinGapAtStart, pinGapAtKing: how far from the pinning Piece's and the King's centers the line starts and stops, in Squares. squareMeters, boardLeft, boardBottom: where the Squares are (as for Place On Board). pixelsPerMeter: the project's Pixels Per Unit.

Attach toAny Object
DATA STRUCTURES
Board Stateself.boardState
Draw Threat Lines Dataself.drawThreatLinesData
SHARED VARIABLES
NameTypeKindStarting Value
pinColorColorConstant#c68a20cc
pinLineWidthNumberConstant0.03
pinDashLengthNumberConstant0.12
pinDashSpacingNumberConstant0.22
pinGapAtStartNumberConstant0.3
pinGapAtKingNumberConstant0.25
arrowToEnemyColorColorConstant#b6403ab0
arrowToOtherColorColorConstant#257f7690
arrowFromAttackerColorColorConstant#b6403acc
arrowWidthNumberConstant0.035
arrowGapAtStartNumberConstant0.22
arrowGapAtEndNumberConstant0.27
arrowHeadLengthNumberConstant0.11
arrowHeadSpreadNumberConstant0.6
squareMetersNumberConstant3.75
boardLeftNumberConstant0
boardBottomNumberConstant4.375
pixelsPerMeterNumberConstant32
When Drawn
WhenDraw
ModeDraw The Lines (Starting Mode)
1Declare Variable
Name
squarePixels
Value
0
2Change Variable
Variable
squarePixels
Value
@self.squareMeters * @self.pixelsPerMeter
3RepeatdataCount('Arrows')times
Each time
4Change Component Value
Component Value
self.drawThreatLinesData.arrowFrom
Value
data('Arrows', local.index, 'from')
5Change Component Value
Component Value
self.drawThreatLinesData.arrowTo
Value
data('Arrows', local.index, 'to')
6Set Draw Color
Color
data('Arrows', local.index, 'color')
7Square Screen PointFunction
Action Name
arrowStart
square
self.drawThreatLinesData.arrowFrom
isFlipped
self.boardState.isFlipped
squareMeters
@self.squareMeters
boardLeft
@self.boardLeft
boardBottom
@self.boardBottom
pixelsPerMeter
@self.pixelsPerMeter
8Square Screen PointFunction
Action Name
arrowEnd
square
self.drawThreatLinesData.arrowTo
isFlipped
self.boardState.isFlipped
squareMeters
@self.squareMeters
boardLeft
@self.boardLeft
boardBottom
@self.boardBottom
pixelsPerMeter
@self.pixelsPerMeter
9Draw ArrowFunction
fromX
arrowStart.x
fromY
arrowStart.y
toX
arrowEnd.x
toY
arrowEnd.y
startGap
@self.arrowGapAtStart * squarePixels
endGap
@self.arrowGapAtEnd * squarePixels
headLength
@self.arrowHeadLength * squarePixels
headSpread
@self.arrowHeadSpread
width
@self.arrowWidth * squarePixels
10Set Draw Color
Color
@self.pinColor
11RepeatdataCount('Threats')times
Each time
12Change Component Value
Component Value
self.drawThreatLinesData.pinned
Value
dataRow('Threats', local.index)
13Ifdata('Threats', self.drawThreatLinesData.pinned, 'pinnedBy') != ""
Then
14Change Component Value
Component Value
self.drawThreatLinesData.pinner
Value
data('Threats', self.drawThreatLinesData.pinned, 'pinnedBy')
15Find KingFunction
Action Name
kingLook
side
data('Position', self.drawThreatLinesData.pinned, 'side')
16Square Screen PointFunction
Action Name
pinnerPoint
square
self.drawThreatLinesData.pinner
isFlipped
self.boardState.isFlipped
squareMeters
@self.squareMeters
boardLeft
@self.boardLeft
boardBottom
@self.boardBottom
pixelsPerMeter
@self.pixelsPerMeter
17Square Screen PointFunction
Action Name
kingPoint
square
kingLook.value
isFlipped
self.boardState.isFlipped
squareMeters
@self.squareMeters
boardLeft
@self.boardLeft
boardBottom
@self.boardBottom
pixelsPerMeter
@self.pixelsPerMeter
18Draw Dashed LineFunction
fromX
pinnerPoint.x
fromY
pinnerPoint.y
toX
kingPoint.x
toY
kingPoint.y
startGap
@self.pinGapAtStart * squarePixels
endGap
@self.pinGapAtKing * squarePixels
dashLength
@self.pinDashLength * squarePixels
dashSpacing
@self.pinDashSpacing * squarePixels
width
@self.pinLineWidth * squarePixels
When Drawn
WhenDraw
ModeDraw Nothing
(no Actions)
On Slide Started
WhenslideStartedCustom Event
1Switch Mode
Event
When Drawn
Mode
Draw Nothing
On Position Changed
WhenpositionChangedCustom Event
1Find ArrowsFunction
gameRules
self
toEnemyColor
@self.arrowToEnemyColor
otherColor
@self.arrowToOtherColor
attackerColor
@self.arrowFromAttackerColor
2Check Game State
Action Name
isChoosing
Game State
Promotion
3IfisChoosing.value == true
Then
4Switch Mode
Event
When Drawn
Mode
Draw Nothing
5Otherwise
6Switch Mode
Event
When Drawn
Mode
Draw The Lines

Draw Threat Lines is where the drawing happens, on the Game Rules. It’s the chapter’s biggest card. Its colors and sizes are constant Shared Variables. The Squares being drawn wait in Draw Threat Lines Data. Add that Data Structure to the Logic, with drawThreatLinesData as its slot name.

The four colors let the Board show through, and each has eight digits after the #. The first six are the color. The last two are how strongly it shows. So cc is about four fifths, and a lower pair lets more of the Board through. Type all eight into the Value’s HEX box, and Myth Engine keeps them.

The sizes are in Squares, so 1 is one Square. A dash, pinDashLength, is 0.12 of one, and a new dash starts every 0.22, pinDashSpacing. Step 2 turns Squares into pixels: squareMeters times pixelsPerMeter.

Build the Draw Event first, named When Drawn, with two Modes: Draw The Lines and Draw Nothing. It’s one Draw Event. The card shows its bar once for each Mode. The two Switch Modes further down pick that Event and its Mode from lists, as Slide Piece’s did in Chapter 7. Modes shows how to make the pair. In the Add Event window, the Draw Event is Draw, and it asks nothing more.

Draw The Lines has two Repeats. The first walks Arrows. For each row, Set Draw Color takes the row’s color, Square Screen Point finds both ends, and Draw Arrow draws it. The second walks Threats, the table Chapter 9 filled. Remember pinnedBy? This is who it was for. A row whose pinnedBy names a Square is a pinned Piece. Find King, from Chapter 3, finds its King. Draw Dashed Line runs from the pinning Piece to that King, across the pinned one.

The arrows come first. So where an arrow and a pin line cross, the dashes lie on top.

Draw Nothing has no Actions. While the Computer’s Piece slides, slideStarted switches the Draw Event to it, because the tables still describe the Board before the Move. The Promotion choice switches it to Draw Nothing too, while its panel lies over the Board.

On Position Changed is the one that does the work. Find Arrows fills Arrows for the Board as it is now, with the three arrow colors. Check Game State asks next whether the Promotion choice is open. If it is, Draw Nothing. If not, Draw The Lines again.

So the work happens once, on each change, and every frame after that the Draw Event reads two tables and draws what’s there. Nothing in it decides anything.

10.6.5 Game Rules

ObjectGame RulesAddDelete
Data Structure
Data StructureBoard State
Data Structure
From LogicSelect and Move
Data StructureSelect and Move Data
Data Structure
From LogicChoose Promotion
Data StructureChoose Promotion Data
Data Structure
From LogicMake Computer Move
Data StructureMake Computer Move Data
Data Structure
From LogicUndo
Data StructureUndo Data
Data Structure
From LogicGive A Hint
Data StructureGive A Hint Data
Data Structure
From LogicDraw Threat Lines
Data StructureDraw Threat Lines Data
Logic
Select and Move
Start Turn
Choose Promotion
Look For Game End
Flip Board
New Game
Make Computer Move
Undo
Give A Hint
Show Threats
Draw Threat Lines

Attach Draw Threat Lines to the Game Rules.

Run the game, press Threats, and play e2 to e4. Wait for the reply, and then move your Queen from d1 to h5, along the diagonal.

Your Queen lands, and a dashed amber line runs from it to Black’s King, across the Pawn on f7. Click your Queen. Red arrows reach out to the Black Pawns it could take. Teal arrows run the other ways, to the edge or to a Piece of your own. If a Black Piece could take your Queen, a red arrow comes in from it.

10.7 The Pressed Button

One last thing. Threats looks the same whether it’s on or off. Now it shows which.

10.7.1 Show Threats Button Pressed

Logic ComponentShow Threats Button Pressed

Draws the Threats button pressed while Threats mode is on (Board State's isShowingThreats), and plain while it is off: its Sprite is tinted, and its word colored, with the pressed colors or the plain ones. It looks after every change on the Board ("positionChanged"), which the Game Rules also send when the game begins (and again after New Game) and when Threats mode is turned on or off. The Game Rules send positionChanged, so they are its Other, whose Board State says whether Threats mode is on. pressedColor, pressedWordColor: the button's color and its word's color while pressed. plainColor, plainWordColor: the same while not pressed (the Tool Button's own colors).

Attach toAny Object
COMPONENTS
self.spriteSprite
self.textText / UI
SHARED VARIABLES
NameTypeKindStarting Value
pressedColorColorConstant#e3eadc
pressedWordColorColorConstant#244739
plainColorColorConstant#e2d5b4
plainWordColorColorConstant#503a2b
On Position Changed
WhenpositionChangedCustom Event
1Ifother.boardState.isShowingThreats == true
Then
2Change Sprite Tint Property
Value
@self.pressedColor
3Change Component Value
Component Value
self.text.color
Value
@self.pressedWordColor
4Otherwise
5Change Sprite Tint Property
Value
@self.plainColor
6Change Component Value
Component Value
self.text.color
Value
@self.plainWordColor

Show Threats Button Pressed looks at Board State’s isShowingThreats each time positionChanged arrives. Is Threats mode on? The button turns a pale green, with its word in dark green. Off, it’s the tan and brown every Tool Button wears. The first positionChanged comes as the game begins, so the button starts out right.

The Game Rules send positionChanged, so they are this Event’s Other. Its If reads other.boardState.isShowingThreats. There is no Object Value to set on this placement.

The button keeps no “pressed” of its own. It follows Board State, and a button can be pressed and faded at once. While the Computer thinks, Threats is pale green and faded.

Open Threats Button. Add Show Threats Button Pressed alongside Report Threats, so this variant has both jobs. Tool Button stays as it is.

Object variantThreats ButtonVariant of: Tool ButtonAddDelete
Logic
Report Threats
Show Threats Button Pressed

Run the game. Press Threats, and the button turns pale green. Press it again, and it’s tan.

Your Board marks each threat now, and shows you where it comes from.

Play it!

Watch for each of these.

  • Start a new game as White, and press Threats. The button turns pale green, with its word in dark green. Nothing on the Board is drawn yet.
  • Click Black’s Knight on b8. Its Square turns teal, and three teal arrows fan out, to a6, c6 and d7. The one to d7 points at a Pawn the Knight protects.
  • Click your Pawn on e2. Its Square turns gold, and its dots show. Two teal arrows point to d3 and f3, the Squares it captures on, not the ones it walks to.
  • Click the Pawn again. The gold and the dots go, the Square turns teal, and the arrows stay.
  • Play e2 to e4, wait for the reply, then play d1 to h5. Unless Black moved its f-Pawn or g-Pawn, a dashed amber line runs from your Queen to Black’s King, across the Pawn on f7.
  • Click your Queen. Red arrows run to the Black Pawns it could take. If a Black Knight stands on f6, a red arrow comes in from it to your Queen.
  • Play a Move, and watch the Computer’s Piece glide. No line or arrow is drawn while it slides. They’re back when it lands.
  • Press Threats again. The lines, the arrows and the teal all go, and the button is tan again.
How it works

Inspect Piece names a Piece, and Square Marks tints it teal. Find Arrows works out every arrow once, on each change, and writes them into a table. The Draw Event reads it every frame. It reads the Threats table too, and draws what’s there. Each color carries its own see-through amount, so the Board shows under every line.

Underneath is one idea. Work out the answer when something changes, and keep it, so the part that runs every frame only has to read it.

10.8 If It Didn’t Work

This chapter draws, and drawing has a surprise of its own. A line in the wrong place turns nothing red. The game draws exactly what you told it, even when that’s off the Board. So watch where things land.

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.

No lines or arrows at all, though the red marks show? Is Draw Threat Lines attached? Perhaps the Draw Event starts on Draw Nothing, when the card’s Draw The Lines is the one it should start with.

The lines show once, then never again after the Computer moves? Look at On Position Changed. Its Else needs its own Switch Mode, back to Draw The Lines.

Arrows land in the wrong spot, or off the Board? Look at Square Screen Point, and the Board’s Shared Variables. squareMeters, boardLeft and boardBottom must match Place On Board’s, and pixelsPerMeter is 32. Perhaps x is multiplied by pixelsPerMeter and y isn’t.

An arrow’s head looks bent, with both strokes leaning one way? Look at Draw Arrow’s last two Draw Lines, and the signs on sideX and sideY. One head stroke takes minus sideX and plus sideY, the other the other way round.

The lines are solid, not dashed? Look at Draw Dashed Line’s step 18. Its along should move on by dashSpacing, and dashSpacing must be more than dashLength.

The game freezes when Threats is on? A While that never ends looks like that, so look at Draw Dashed Line’s step 18. Is it inside the While, at the end of its line?

Dots appear for a Piece you didn’t select? Find Arrows’ last If may be missing, or its Clear Targets Unattached.

The teal never shows? Look at Square Marks’ step 8, and whether it comes before the selected Square’s If. Is Inspect Piece on the outer Else’s line in Select and Move?

The button never turns pale green? Look at Threats Button’s Logic. It needs Show Threats Button Pressed alongside Report Threats. Its If reads other.boardState, the Game Rules that sent the message.

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.

Challenge

Your Board draws two things now, and the Draw Event can draw anything you can describe as lines. What else would help a player read the Board? Perhaps a faint line along the last Move, from its first Square to its second. Or a ring of short strokes around the King in Check. Draw Arrow and Draw Dashed Line can draw any arrow and any dashed line you hand them. The rest is up to you.

Still stuck? Search the manual, or ask in the Myth Engine Discord (opens a new tab).

Your Board shows the lines between the Pieces now. In Chapter 11, the game gets new looks to choose from, and a memory that keeps your game when you close it.