Ludo Game with first-class moves

TL;DR

In order to browse the state of the game over time, we make moves first-class objects.

Motivation

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.

Move objects

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.

token player
  

We introduce GtLudoRecordingGame GtLudoGame subclass: #GtLudoRecordingGame instanceVariableNames: 'moves' classVariableNames: '' package: 'GToolkit-Demo-Ludo-Model' as a subclass of GtLudoGame Object subclass: #GtLudoGame instanceVariableNames: 'players squares startSquares goalSquares die announcer feedback winner needToRollDie lastDieRolled playerQueue routeCache' classVariableNames: '' package: 'GToolkit-Demo-Ludo-Model' , with first-class moves.

GtLudoRecordingGame new
  

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 GtPhlowView>>#updateWhen:in:

For example, see GtLudoGame>>#gtPlayersFor: gtPlayersFor: aView <gtView> ^ (aView columnedList) title: 'Players'; priority: 20; 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'.
  

Autoplay

We introduce GtLudoRecordingGame>>#autoPlay: autoPlay: numberOfMoves "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 ]. self playerToRoll ifTrue: [ self die roll ] ifFalse: [ self moveTokenNamed: self tokensToMove atRandom name ] ] and storeOn:

game := GtLudoRecordingGame new.