Ludo Game with first-class moves
In order to browse the state of the game over time, we make moves first-class objects.
Motivation: our test examples all consist of a setup example and the test case itself.
We would like to be able to see explicitly what the moves are and visualize them.
The driver for the change is the need for visualization.
Views are useful because they document what developers found interesting about the domain objects and how to query them.
We start by creating a Move object that encapsulates a token to move and the rolled number of of steps to move.
game := GtLudoGame new. game roll: 6. move := GtLudoMove new roll: 6; token: (game tokenNamed: 'a'); yourself. game
We have changed players so they know their game, and now we can navigate back and forth.
We want to reuse the BoardElement to highlight a token on the board.
token := move token. boardElement := token player game boardElement. (boardElement children select: [ :each | (each isKindOf: GtLudoSquareElement) and: [ each square token = token ] ]) do: #highlight. boardElement
From a token we can navigate to its player, and highlight the player's tokens.
GtLudoGame subclass: #GtLudoRecordingGame
as a subclass of
Object subclass: #GtLudoGame
instanceVariableNames: 'players squares startSquares goalSquares die announcer feedback winner needToRollDie lastDieRolled playerQueue routeCache'
, with first-class moves.
We add numerous views with highlights for the locations of tokens, squares and players.
A RecordingGame records its moves, and has a view showing the list of recorded moves.
To automatically update views, we should use
For example, see
^ (aView columnedList)
items: [ self players ];
column: 'Player' text: [ :player | player ];
column: 'Token' text: [ :player | player tokens first ];
column: 'State' text: [ :player | player tokens first state ];
column: 'Token' text: [ :player | player tokens second ];
column: 'State' text: [ :player | player tokens second state ];
updateWhen: GtLudoGameUpdated in: self announcer
, which is automatically updated when the game state is updated.
Recorded Game views
Moves should also record the from and to squares, and the before and after feedback.
This can be displayed in the game view.
We can subclass the GameExamples class to create the same examples for the recorded games.
game := GtLudoRecordingGame new. game roll: 6. game moveTokenNamed: 'a'. game roll: 6. game moveTokenNamed: 'A'.
"NB: For demo purposes we only implement autoPlay: in the GtLudoRecordingGame class, not in GtLudoGame."
self assert: numberOfMoves > 0.
(1 to: numberOfMoves) do: [ :n |
self isOver ifTrue: [ ^ self ].
ifTrue: [ self die roll ]
ifFalse: [ self moveTokenNamed: self tokensToMove atRandom name ] ]
game := GtLudoRecordingGame new.