Fixing Ludo autoplay and storeStrings
TL;DR
This last page is about cleaning up a number of open issues. We add some further tests, refactor, and repair the broken bits.
Issue
Currently the recording Ludo game records moves, but misses the roll+s. To replay a game via the storeString, the empty rolls without any physical moves are needed.
Instead of recording only moves, we must also record the rolls that do not lead to any token moves.
We could introduce a separate Roll class, but the existing Move already has either 1 or 2 token moves. We could simply extend it to also have zero token moves, but ensure that such empty moves are also recorded.
Steps
• check where the Move is created
• check that the token moves are initiaized to an empty collection
• make sure that an empty move (roll with no moves) is recorded
• test it
• design a test case (count the number of moves; ask how many tokens are moved)
• maybe subclass the existing tests
• check that the Moves view works as expected also for empty moves
• fix the storeOn: method
• write a test to verify that the storeString produces an equivalent game
• write a test that exercises autoplay after a short, rigged game (some player close to end)
Handling empty moves
This test case shows only 4 moves, but there are actually 3 empty moves (rolls without moving) after the first two moves.
GtLudoRecordingGameExamples new playerAentersAndLandsOnA
The current Move stores a token, but it should also store a player in case no token is moved.
The token could be initialized to a Blank Token (to avoid nil problems)
Renaming privateMove:
We rename privateMove:
to moveToken:
Instead we define moveToken: and moveTokenNamed: in the parent, and just redefine moveToken: in the subclass.
We remove moveTokenNamed:
from the subclass and run the tests.
(GtExplicitExampleGroup withAll: GtLudoGame package gtExamplesAllContained) runAll
Where to create the Move?
Currently the Move object is only created in the moveToken: method, but we also want one when we roll but don't move.
Whenever a player rolls the die (on their turn), a new Move object should be created. If there are possible tokens to be moved, then we wait for the move to be made before recording the move. Otherwise we immediately record the empty move.
The magic happens in the onRolled
method, which is also invoked by callback when the roll:
message is explciitly sent.
One problem is that we introduce a fake roll:
if a player rolls the die when they shouldn't. This would make the recording logic unnecessarily complex, so instead we introduce a rollback:
method to the die that does
not
signal an update, and use it to rollback invalid die rolls.
We also factor out a helper method updateOnRoll
that does the update on a roll, which we can then extend in the subclass.
We run the tests.
(GtExplicitExampleGroup withAll: GtLudoGame package gtExamplesAllContained) runAll
We explore the moves of the previous example and now we see the empty moves.
GtLudoRecordingGameExamples new playerAentersAndLandsOnA
However the Move view is broken for empty moves because a blank token has no game.
We add a player to the move and get the game from there.
Expand views to show the Player of a move.
We commit this version.
Adding some tests
We override playerAentersAndLandsOnA and add some more assertions.
GtLudoRecordingGameExamples>>#playerAentersAndLandsOnA
Autoplay
We verify that we can now autoplay a recorded game.
game := GtLudoRecordingGameExamples new playerAentersAndLandsOnA. game autoPlay: 1000. game
Storestring
We patch up the GtLudoRecordingGame>>#storeOn:
method and check if it works.
script := game storeString
We evaluate the script:
Smalltalk compiler evaluate: script
Now we can add a test for this: GtLudoRecordingGameExamples>>#evaluatedStoreStringYieldsSameStorestring
Back in time execution
We would like to reconstruct a game just up to a certain point. Let's take the palyed game from earlier.
game := GtLudoRecordingGameExamples new playerAentersAndLandsOnA. game autoPlay: 1000. game
Now let's just replay the game up to a certain point.
movesToPlay := game moves copyFrom: 1 to: 20. newGame := GtLudoRecordingGame new. movesToPlay do: [ :move | newGame roll: move roll. move canMove ifTrue: [ newGame moveTokenNamed: move token name ] ]. newGame
Added GtLudoMove>>#gtReplayedMoveFor:
.
How to get the highlighted move in the replayed state?
It should just be:
newGame moves last
Check the views.
game := GtLudoRecordingGameExamples new playerAentersAndLandsOnA. game autoPlay: 1000. game
Look at the moves, and check the Move, Replay and Highlight views of a given move.