# The Reject Game Elements

synopsis

Before proceeding, make sure you have all you need:

If anyone can create a game for any two other players, it is important to allow a player to reject a game. A player should not be allowed to reject a game once they have made their first move.

To reject a game, a player needs to provide the ID of the game that the player wants to reject. Call the field idValue. This should be sufficient as the signer of the message is implicitly the player.

# Working with Starport

Name the message object RejectGame. Invoke Starport with:

Copy $ starport scaffold message rejectGame idValue --module checkers

It creates all the boilerplate for you and leaves a single place for the code you want to include:

Copy func (k msgServer) RejectGame(goCtx context.Context, msg *types.MsgRejectGame) (*types.MsgRejectGameResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) // TODO: Handling the message _ = ctx return &types.MsgRejectGameResponse{}, nil } x checkers keeper msg_server_reject_game.go View source

# Additional information

A new rule of the game should be that a player cannot reject a game once they begin to play. As of now, when loading a StoredGame from storage, you have no way of knowing whether a player already played or not. Add a new field to the StoredGame called MoveCount. In proto/checkers/stored_game.proto:

Copy storedGame := types.StoredGame{ ... uint64 moveCount = 7; } proto checkers stored_game.proto View source

Run Protobuf to recompile the relevant Go files:

Copy $ starport generate proto-go

MoveCount should start at 0 and increment by 1 on each move. So adjust it first in the handler when creating the game:

Copy storedGame := types.StoredGame{ ... MoveCount: 0, } x checkers keeper msg_server_create_game.go View source

Just before saving to the storage, in the handler when playing a move:

Copy ... storedGame.MoveCount++ storedGame.Game = game.String() ... x checkers keeper msg_server_play_move.go View source

With MoveCount counting properly, you are now ready to handle a rejection request.

# The reject handling

To follow the Cosmos SDK conventions declare the following new errors:

Copy ErrRedAlreadyPlayed = sdkerrors.Register(ModuleName, 1108, "red player has already played") ErrBlackAlreadyPlayed = sdkerrors.Register(ModuleName, 1109, "black player has already played") x checkers types errors.go View source

You will add an event for rejection. Begin by preparing the new keys:

Copy const ( RejectGameEventKey = "GameRejected" RejectGameEventCreator = "Creator" RejectGameEventIdValue = "IdValue" ) x checkers types keys.go View source

In the message handler the reject steps are:

  1. Fetch the relevant information:

    Copy storedGame, found := k.Keeper.GetStoredGame(ctx, msg.IdValue) if !found { return nil, sdkerrors.Wrapf(types.ErrGameNotFound, "game not found %s", msg.IdValue) } x checkers keeper msg_server_reject_game.go View source
  2. Is the player expected? Did the player already play? Check with:

    Copy if strings.Compare(storedGame.Red, msg.Creator) == 0 { if 1 < storedGame.MoveCount { // Notice the use of the new field return nil, types.ErrRedAlreadyPlayed } } else if strings.Compare(storedGame.Black, msg.Creator) == 0 { if 0 < storedGame.MoveCount { // Notice the use of the new field return nil, types.ErrBlackAlreadyPlayed } } else { return nil, types.ErrCreatorNotPlayer } x checkers keeper msg_server_reject_game.go View source

    Remember that the black player plays first.

  3. Get rid of the game, as it is not interesting enough to keep:

    Copy k.Keeper.RemoveStoredGame(ctx, msg.IdValue) x checkers keeper msg_server_reject_game.go View source

    Finally using the Keeper.RemoveStoredGame (opens new window) function created long ago by the starport scaffold map storedGame... command.

  4. Emit the relevant event:

    Copy ctx.EventManager().EmitEvent( sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyModule, "checkers"), sdk.NewAttribute(sdk.AttributeKeyAction, types.RejectGameEventKey), sdk.NewAttribute(types.RejectGameEventCreator, msg.Creator), sdk.NewAttribute(types.RejectGameEventIdValue, msg.IdValue), ), ) x checkers keeper msg_server_reject_game.go View source
  5. Leave the returned object as it is as you have nothing new to tell the caller.

You can confirm that your project at least compiles with (opens new window):

Copy $ starport chain build

# Next up

The next four sections cover forfeits and how games end. In the next section you create a doubly-linked FIFO.

Later you add a deadline and a game winner fields, before being able to finally enforce the forfeit.

If you want to enable token wagers in your games instead, skip ahead to wagers.