# Message and Handler - Add a Way to Make a Move
Make sure you have all you need before proceeding:
- You understand the concepts of transactions, messages, and Protobuf.
- Go is installed.
- You have the checkers blockchain codebase with
MsgCreateGameand its handling. If not, follow the previous steps or check out the relevant version (opens new window).
In this section, you will:
- Extend message handling - play the game.
- Handle moves and update the game state.
- Validate input.
- Extend unit tests.
Your blockchain can now create games, but can you play them? Not yet...so what do you need to make this possible?
# Some initial thoughts
Before diving into the exercise, take some time to think about the following questions:
- What goes into the message?
- How do you sanitize the inputs?
- How do you unequivocally identify games?
- How do you report back errors?
- How do you use your files that implement the checkers rules?
- How do you make sure that nothing is lost?
# Code needs
When it comes to the code you need, ask yourself:
- What Ignite CLI commands will create your message?
- How do you adjust what Ignite CLI created for you?
- How would you unit-test these new elements?
- How would you use Ignite CLI to locally run a one-node blockchain and interact with it via the CLI to see what you get?
As before, do not bother yet with niceties like gas metering or event emission.
To play a game a player only needs to specify:
- The ID of the game the player wants to join. Call the field
idValue. - The initial positions of the pawn. Call the fields
fromXandfromYand make themuint. - The final position of the pawn after a player's move. Call the fields
toXandtoYto beuinttoo.
The player does not need to be explicitly added as a field in the message because the player is implicitly the signer of the message. Name the object PlayMove.
Unlike when creating the game, you want to return:
- The game ID again. Call this field
idValue. - The captured piece, if any. Call the fields
capturedXandcapturedY. - The winner in the field
winner.
# With Ignite CLI
Ignite CLI only creates a response object with a single field. You can update the object after Ignite CLI has run:
Ignite CLI once more creates all the necessary Protobuf files and the boilerplate for you. All you have to do is:
Add the missing fields to the response in
proto/checkers/tx.proto:Use
int64here so that you can enter-1when no pawns have been captured.Fill in the needed part in
x/checkers/keeper/msg_server_play_move.go:Where the
TODOis replaced as per the following.
# The move handling
The rules represent the ready-made file containing the rules of the game you imported earlier. Declare them in x/checkers/types/errors.go, given your code has to handle new error situations:
Take the following steps to replace the TODO:
Fetch the stored game information using the
Keeper.GetStoredGame(opens new window) function created by Ignite CLI:Is the player legitimate? Check with:
This uses the certainty that the
MsgPlayMove.Creatorhas been verified by its signature (opens new window).Instantiate the board in order to implement the rules:
Fortunately you previously created this helper (opens new window).
Is it the player's turn? Check using the rules file's own
TurnIs(opens new window) function:Properly conduct the move, using the rules'
Move(opens new window) function:Prepare the updated board to be stored and store the information:
This updates the fields that were modified using the
Keeper.SetStoredGame(opens new window) function, as when you created and saved the game.Return relevant information regarding the move's result:
The
CapturedandWinnerinformation would be lost if you do not do this. More accurately, one would have to replay the transaction to discover the values. Better to be a good citizen and make this information easily accessible.
This completes the move process, facilitated by good preparation and the use of Ignite CLI.
# Unit tests
Adding unit tests for this play message is very similar to what you did for the previous message: create a new msg_server_play_move_test.go file and add to it. Start with a function that sets up the keeper as you prefer. In this case, already having a game saved can reduce several lines of code in each test:
Now test the result of a move:
Also test whether the game was saved correctly (opens new window). Check what happens when players try to play out of turn (opens new window), or make a wrong move (opens new window). Check after two (opens new window) or three turns with a capture (opens new window).
# Interact via the CLI
With one game in storage and the game waiting for Bob's move, can Alice make a move? Look at the play-move message and which parameters it accepts:
This returns:
So Alice tries:
This includes:
If you did not get this raw_log, your transaction may have been sent asynchronously. You can always query a transaction by using the txhash with the following command:
And you are back on track:
Can Bob, who plays black, make a move? Can he make a wrong move? For instance, a move from 0-1 to 1-0, which is occupied by one of his pieces.
The computer says no:
So far all seems to be working.
Time for Bob to make a correct move:
This returns:
Confirm the move went through with your one-line formatter from the previous section:
This shows:
Bob's piece moved down and right.
# Next up
Before you add a third message to let a player reject a game, add events to the existing message handlers for relevant information. This is the object of the next section.
If you want to skip ahead and see how you can assist a player in not submitting a transaction that would result in a failed move, you can create a query to test a move.