Adding simple list views for Ludo Players and Squares

TL;DR

We introduce some simple custom views for the basic board model in Implementing a Ludo Game as a moldable development exercise.

Adding some simple views

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.

Using Announcements to update the views

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.