We introduce some simple custom views for the basic board model in Implementing a Ludo Game as a moldable development exercise.
Rather than browsing the
raw
view of a Ludo board, we would like to have custom views that show us key information about (for starters) the Players and the Squares.
To get some inspiration, we query and browse existing gtView methods that build a column list views, and we sort them by size.
(#gtView gtPragmas & #columnedList gtSenders) contents sort: [ :a :b | a size < b size ]
For example, we find the Keys
view of the OrderedDictionary
Collection subclass: #OrderedDictionary
instanceVariableNames: 'dictionary orderedKeys'
classVariableNames: ''
package: 'Collections-Sequenceable-Ordered'
class.
We follow this example and implement 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
and GtLudoGame>>#gtSquaresFor:
gtSquaresFor: aView
<gtView>
^ aView columnedList
title: 'Squares';
priority: 30;
items: [ self squares ];
column: 'Index'
text: [ :eachItem :eachIndex |
eachIndex asRopedText foreground: Color gray ]
width: 45;
column: 'Token' text: [ :square | square token ];
column: 'State' text: [ :square |
square notEmpty
ifTrue: [ square token state ]
ifFalse: [ '' ] ];
updateWhen: GtLudoGameUpdated in: self announcer
.
Initially we just list the name of each Player, and we later expand this to show each player's tokens and their states.
Similarly for the Squares, we show the index of each square, the name of any token, and the token's state.
We would like the views to be updated automatically whenever the game state changes. To do this, we need an
announcer
that will be used to announce updates. We create an announcer
slot in GtLudoGame
Object subclass: #GtLudoGame
instanceVariableNames: 'players squares startSquares goalSquares die announcer feedback winner needToRollDie lastDieRolled playerQueue routeCache'
classVariableNames: ''
package: 'GToolkit-Demo-Ludo-Model'
and initialize it in GtLudoGame>>#initialize
initialize
super initialize.
announcer := Announcer new.
feedback := ''.
players := OrderedCollection new.
($A to: $D) do: [ :player | players add: (GtLudoPlayer new name: player) ].
self initializeSquares.
players do: #initializeTokens.
self initializeStartSquares.
self initializeGoalSquares.
die := GtLudoDie new.
routeCache := Dictionary new.
"Initialization code from the Game class"
self die announcer when: GtLudoDieRolled send: #onRolled to: self.
lastDieRolled := self die topFace.
self squares , self startSquares do: [ :square |
square announcer
when: GtLudoSquareSignalTokenMove
do: [ :announcement | self moveToken: announcement token ] ].
playerQueue := self players copy.
playerQueue do: [ :each | each game: self ].
self requireDieRoll.
self setWinner.
self feedback: self gameState
.
We define GtLudoGameUpdated
Announcement subclass: #GtLudoGameUpdated
instanceVariableNames: ''
classVariableNames: ''
package: 'GToolkit-Demo-Ludo-Announcements'
as a subclass of Announcement
Object subclass: #Announcement
instanceVariableNames: ''
classVariableNames: ''
package: 'Announcements-Core-Base'
, and we broadcast it using the announcer whenever we need it by running GtLudoGame>>#notifyGameUpdated
notifyGameUpdated
self announcer announce: GtLudoGameUpdated new
.
We subscribe to this event in the last line of 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
.
In this way, the view will be automatically updated whenever the game state changes.