From cf1df87129eaea16854be394453724680c132cb0 Mon Sep 17 00:00:00 2001 From: Xavier Lepretre Date: Mon, 22 Aug 2022 18:35:48 +0400 Subject: [PATCH] Add player info handling and tests. --- x/checkers/keeper/end_block_server_game.go | 1 + .../keeper/end_block_server_game_test.go | 103 ++++++++++++++++++ .../keeper/msg_server_create_game_test.go | 81 ++++++++++++++ x/checkers/keeper/msg_server_play_move.go | 1 + .../keeper/msg_server_play_move_test.go | 56 ++++++++++ .../msg_server_play_move_winner_test.go | 61 +++++++++++ x/checkers/keeper/player_info_handler.go | 81 ++++++++++++++ x/checkers/types/errors.go | 2 + 8 files changed, 386 insertions(+) create mode 100644 x/checkers/keeper/player_info_handler.go diff --git a/x/checkers/keeper/end_block_server_game.go b/x/checkers/keeper/end_block_server_game.go index bfba02c..c2d98b3 100644 --- a/x/checkers/keeper/end_block_server_game.go +++ b/x/checkers/keeper/end_block_server_game.go @@ -54,6 +54,7 @@ func (k Keeper) ForfeitExpiredGames(goCtx context.Context) { panic(fmt.Sprintf(types.ErrCannotFindWinnerByColor.Error(), storedGame.Turn)) } k.MustPayWinnings(ctx, &storedGame) + k.MustRegisterPlayerForfeit(ctx, &storedGame) storedGame.Board = "" k.SetStoredGame(ctx, storedGame) } diff --git a/x/checkers/keeper/end_block_server_game_test.go b/x/checkers/keeper/end_block_server_game_test.go index 8cd9774..0ce762d 100644 --- a/x/checkers/keeper/end_block_server_game_test.go +++ b/x/checkers/keeper/end_block_server_game_test.go @@ -779,3 +779,106 @@ func TestForfeit2OldestPlayedTwiceIn1CallCalledBank(t *testing.T) { keeper.SetStoredGame(ctx, game2) keeper.ForfeitExpiredGames(context) } + +func TestForfeitGameAddPlayerInfo(t *testing.T) { + msgServer, keeper, context := setupMsgServerWithOneGameForPlayMove(t) + ctx := sdk.UnwrapSDKContext(context) + + msgServer.PlayMove(context, &types.MsgPlayMove{ + Creator: bob, + GameIndex: "1", + FromX: 1, + FromY: 2, + ToX: 2, + ToY: 3, + }) + msgServer.PlayMove(context, &types.MsgPlayMove{ + Creator: carol, + GameIndex: "1", + FromX: 0, + FromY: 5, + ToX: 1, + ToY: 4, + }) + game1, found := keeper.GetStoredGame(ctx, "1") + require.True(t, found) + oldDeadline := types.FormatDeadline(ctx.BlockTime().Add(time.Duration(-1))) + game1.Deadline = oldDeadline + keeper.SetStoredGame(ctx, game1) + keeper.ForfeitExpiredGames(context) + + bobInfo, found := keeper.GetPlayerInfo(ctx, bob) + require.True(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: bob, + WonCount: 0, + LostCount: 0, + ForfeitedCount: 1, + }, bobInfo) + carolInfo, found := keeper.GetPlayerInfo(ctx, carol) + require.True(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: carol, + WonCount: 1, + LostCount: 0, + ForfeitedCount: 0, + }, carolInfo) +} + +func TestForfeiGameUpdatePlayerInfo(t *testing.T) { + msgServer, keeper, context := setupMsgServerWithOneGameForPlayMove(t) + ctx := sdk.UnwrapSDKContext(context) + + keeper.SetPlayerInfo(ctx, types.PlayerInfo{ + Index: bob, + WonCount: 1, + LostCount: 2, + ForfeitedCount: 3, + }) + keeper.SetPlayerInfo(ctx, types.PlayerInfo{ + Index: carol, + WonCount: 4, + LostCount: 5, + ForfeitedCount: 6, + }) + + msgServer.PlayMove(context, &types.MsgPlayMove{ + Creator: bob, + GameIndex: "1", + FromX: 1, + FromY: 2, + ToX: 2, + ToY: 3, + }) + msgServer.PlayMove(context, &types.MsgPlayMove{ + Creator: carol, + GameIndex: "1", + FromX: 0, + FromY: 5, + ToX: 1, + ToY: 4, + }) + game1, found := keeper.GetStoredGame(ctx, "1") + require.True(t, found) + oldDeadline := types.FormatDeadline(ctx.BlockTime().Add(time.Duration(-1))) + game1.Deadline = oldDeadline + keeper.SetStoredGame(ctx, game1) + keeper.ForfeitExpiredGames(context) + + bobInfo, found := keeper.GetPlayerInfo(ctx, bob) + require.True(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: bob, + WonCount: 1, + LostCount: 2, + ForfeitedCount: 4, + }, bobInfo) + carolInfo, found := keeper.GetPlayerInfo(ctx, carol) + require.True(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: carol, + WonCount: 5, + LostCount: 5, + ForfeitedCount: 6, + }, carolInfo) +} diff --git a/x/checkers/keeper/msg_server_create_game_test.go b/x/checkers/keeper/msg_server_create_game_test.go index 40109cd..44791b1 100644 --- a/x/checkers/keeper/msg_server_create_game_test.go +++ b/x/checkers/keeper/msg_server_create_game_test.go @@ -423,3 +423,84 @@ func TestSavedCreatedDeadlineIsParseable(t *testing.T) { _, err := game.GetDeadlineAsTime() require.Nil(t, err) } + +func TestCreatePlayerInfoNotAdded(t *testing.T) { + msgServer, keeper, context := setupMsgServerCreateGame(t) + ctx := sdk.UnwrapSDKContext(context) + msgServer.CreateGame(context, &types.MsgCreateGame{ + Creator: alice, + Black: bob, + Red: carol, + Wager: 45, + Denom: "stake", + }) + aliceInfo, found := keeper.GetPlayerInfo(ctx, alice) + require.False(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: "", + WonCount: 0, + LostCount: 0, + ForfeitedCount: 0, + }, aliceInfo) + bobInfo, found := keeper.GetPlayerInfo(ctx, bob) + require.False(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: "", + WonCount: 0, + LostCount: 0, + ForfeitedCount: 0, + }, bobInfo) + carolInfo, found := keeper.GetPlayerInfo(ctx, carol) + require.False(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: "", + WonCount: 0, + LostCount: 0, + ForfeitedCount: 0, + }, carolInfo) +} + +func TestCreatePlayerInfoNotUpdated(t *testing.T) { + msgServer, keeper, context := setupMsgServerCreateGame(t) + ctx := sdk.UnwrapSDKContext(context) + keeper.SetPlayerInfo(ctx, types.PlayerInfo{ + Index: alice, + }) + keeper.SetPlayerInfo(ctx, types.PlayerInfo{ + Index: bob, + }) + keeper.SetPlayerInfo(ctx, types.PlayerInfo{ + Index: carol, + }) + msgServer.CreateGame(context, &types.MsgCreateGame{ + Creator: alice, + Black: bob, + Red: carol, + Wager: 45, + Denom: "stake", + }) + aliceInfo, found := keeper.GetPlayerInfo(ctx, alice) + require.True(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: alice, + WonCount: 0, + LostCount: 0, + ForfeitedCount: 0, + }, aliceInfo) + bobInfo, found := keeper.GetPlayerInfo(ctx, bob) + require.True(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: bob, + WonCount: 0, + LostCount: 0, + ForfeitedCount: 0, + }, bobInfo) + carolInfo, found := keeper.GetPlayerInfo(ctx, carol) + require.True(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: carol, + WonCount: 0, + LostCount: 0, + ForfeitedCount: 0, + }, carolInfo) +} diff --git a/x/checkers/keeper/msg_server_play_move.go b/x/checkers/keeper/msg_server_play_move.go index 9c92d82..5cbdbdf 100644 --- a/x/checkers/keeper/msg_server_play_move.go +++ b/x/checkers/keeper/msg_server_play_move.go @@ -79,6 +79,7 @@ func (k msgServer) PlayMove(goCtx context.Context, msg *types.MsgPlayMove) (*typ storedGame.Board = "" k.Keeper.RemoveFromFifo(ctx, &storedGame, &systemInfo) k.Keeper.MustPayWinnings(ctx, &storedGame) + k.Keeper.MustRegisterPlayerWin(ctx, &storedGame) } storedGame.Deadline = types.FormatDeadline(types.GetNextDeadline(ctx)) diff --git a/x/checkers/keeper/msg_server_play_move_test.go b/x/checkers/keeper/msg_server_play_move_test.go index 78d75c6..9f6258c 100644 --- a/x/checkers/keeper/msg_server_play_move_test.go +++ b/x/checkers/keeper/msg_server_play_move_test.go @@ -512,3 +512,59 @@ func TestSavedPlayedDeadlineIsParseable(t *testing.T) { _, err := game.GetDeadlineAsTime() require.Nil(t, err) } + +func TestPlayerInfoNoAdditionOnNoWinner(t *testing.T) { + msgServer, keeper, context := setupMsgServerWithOneGameForPlayMove(t) + ctx := sdk.UnwrapSDKContext(context) + msgServer.PlayMove(context, &types.MsgPlayMove{ + Creator: bob, + GameIndex: "1", + FromX: 1, + FromY: 2, + ToX: 2, + ToY: 3, + }) + bobInfo, found := keeper.GetPlayerInfo(ctx, bob) + require.False(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: "", + WonCount: 0, + LostCount: 0, + ForfeitedCount: 0, + }, bobInfo) +} + +func TestPlayerInfoNoUpdatedOnNoWinner(t *testing.T) { + msgServer, keeper, context := setupMsgServerWithOneGameForPlayMove(t) + ctx := sdk.UnwrapSDKContext(context) + keeper.SetPlayerInfo(ctx, types.PlayerInfo{ + Index: bob, + }) + keeper.SetPlayerInfo(ctx, types.PlayerInfo{ + Index: carol, + }) + msgServer.PlayMove(context, &types.MsgPlayMove{ + Creator: bob, + GameIndex: "1", + FromX: 1, + FromY: 2, + ToX: 2, + ToY: 3, + }) + bobInfo, found := keeper.GetPlayerInfo(ctx, bob) + require.True(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: bob, + WonCount: 0, + LostCount: 0, + ForfeitedCount: 0, + }, bobInfo) + carolInfo, found := keeper.GetPlayerInfo(ctx, carol) + require.True(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: carol, + WonCount: 0, + LostCount: 0, + ForfeitedCount: 0, + }, carolInfo) +} diff --git a/x/checkers/keeper/msg_server_play_move_winner_test.go b/x/checkers/keeper/msg_server_play_move_winner_test.go index bfe3bc6..49666cc 100644 --- a/x/checkers/keeper/msg_server_play_move_winner_test.go +++ b/x/checkers/keeper/msg_server_play_move_winner_test.go @@ -62,3 +62,64 @@ func TestPlayMoveUpToWinnerCalledBank(t *testing.T) { testutil.PlayAllMoves(t, msgServer, context, "1", bob, carol, testutil.Game1Moves) } + +func TestCompleteGameAddPlayerInfo(t *testing.T) { + msgServer, keeper, context := setupMsgServerWithOneGameForPlayMove(t) + ctx := sdk.UnwrapSDKContext(context) + + testutil.PlayAllMoves(t, msgServer, context, "1", bob, carol, testutil.Game1Moves) + + bobInfo, found := keeper.GetPlayerInfo(ctx, bob) + require.True(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: bob, + WonCount: 1, + LostCount: 0, + ForfeitedCount: 0, + }, bobInfo) + carolInfo, found := keeper.GetPlayerInfo(ctx, carol) + require.True(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: carol, + WonCount: 0, + LostCount: 1, + ForfeitedCount: 0, + }, carolInfo) +} + +func TestCompleteGameUpdatePlayerInfo(t *testing.T) { + msgServer, keeper, context := setupMsgServerWithOneGameForPlayMove(t) + ctx := sdk.UnwrapSDKContext(context) + + keeper.SetPlayerInfo(ctx, types.PlayerInfo{ + Index: bob, + WonCount: 1, + LostCount: 2, + ForfeitedCount: 3, + }) + keeper.SetPlayerInfo(ctx, types.PlayerInfo{ + Index: carol, + WonCount: 4, + LostCount: 5, + ForfeitedCount: 6, + }) + + testutil.PlayAllMoves(t, msgServer, context, "1", bob, carol, testutil.Game1Moves) + + bobInfo, found := keeper.GetPlayerInfo(ctx, bob) + require.True(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: bob, + WonCount: 2, + LostCount: 2, + ForfeitedCount: 3, + }, bobInfo) + carolInfo, found := keeper.GetPlayerInfo(ctx, carol) + require.True(t, found) + require.EqualValues(t, types.PlayerInfo{ + Index: carol, + WonCount: 4, + LostCount: 6, + ForfeitedCount: 6, + }, carolInfo) +} diff --git a/x/checkers/keeper/player_info_handler.go b/x/checkers/keeper/player_info_handler.go new file mode 100644 index 0000000..cf3bcdb --- /dev/null +++ b/x/checkers/keeper/player_info_handler.go @@ -0,0 +1,81 @@ +package keeper + +import ( + "fmt" + + rules "github.com/b9lab/checkers/x/checkers/rules" + "github.com/b9lab/checkers/x/checkers/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func mustAddDeltaGameResultToPlayer( + k *Keeper, + ctx sdk.Context, + player sdk.AccAddress, + wonDelta uint64, + lostDelta uint64, + forfeitedDelta uint64, +) (playerInfo types.PlayerInfo) { + playerInfo, found := k.GetPlayerInfo(ctx, player.String()) + if !found { + playerInfo = types.PlayerInfo{ + Index: player.String(), + WonCount: 0, + LostCount: 0, + ForfeitedCount: 0, + } + } + playerInfo.WonCount += wonDelta + playerInfo.LostCount += lostDelta + playerInfo.ForfeitedCount += forfeitedDelta + k.SetPlayerInfo(ctx, playerInfo) + return playerInfo +} + +func (k *Keeper) MustAddWonGameResultToPlayer(ctx sdk.Context, player sdk.AccAddress) types.PlayerInfo { + return mustAddDeltaGameResultToPlayer(k, ctx, player, 1, 0, 0) +} + +func (k *Keeper) MustAddLostGameResultToPlayer(ctx sdk.Context, player sdk.AccAddress) types.PlayerInfo { + return mustAddDeltaGameResultToPlayer(k, ctx, player, 0, 1, 0) +} + +func (k *Keeper) MustAddForfeitedGameResultToPlayer(ctx sdk.Context, player sdk.AccAddress) types.PlayerInfo { + return mustAddDeltaGameResultToPlayer(k, ctx, player, 0, 0, 1) +} + +func getWinnerAndLoserAddresses(storedGame *types.StoredGame) (winnerAddress sdk.AccAddress, loserAddress sdk.AccAddress) { + if storedGame.Winner == rules.PieceStrings[rules.NO_PLAYER] { + panic(types.ErrThereIsNoWinner.Error()) + } + redAddress, err := storedGame.GetRedAddress() + if err != nil { + panic(err.Error()) + } + blackAddress, err := storedGame.GetBlackAddress() + if err != nil { + panic(err.Error()) + } + if storedGame.Winner == rules.PieceStrings[rules.RED_PLAYER] { + winnerAddress = redAddress + loserAddress = blackAddress + } else if storedGame.Winner == rules.PieceStrings[rules.BLACK_PLAYER] { + winnerAddress = blackAddress + loserAddress = redAddress + } else { + panic(fmt.Sprintf(types.ErrWinnerNotParseable.Error(), storedGame.Winner)) + } + return winnerAddress, loserAddress +} + +func (k *Keeper) MustRegisterPlayerWin(ctx sdk.Context, storedGame *types.StoredGame) (winnerInfo types.PlayerInfo, loserInfo types.PlayerInfo) { + winnerAddress, loserAddress := getWinnerAndLoserAddresses(storedGame) + return k.MustAddWonGameResultToPlayer(ctx, winnerAddress), + k.MustAddLostGameResultToPlayer(ctx, loserAddress) +} + +func (k *Keeper) MustRegisterPlayerForfeit(ctx sdk.Context, storedGame *types.StoredGame) (winnerInfo types.PlayerInfo, forfeiterInfo types.PlayerInfo) { + winnerAddress, loserAddress := getWinnerAndLoserAddresses(storedGame) + return k.MustAddWonGameResultToPlayer(ctx, winnerAddress), + k.MustAddForfeitedGameResultToPlayer(ctx, loserAddress) +} diff --git a/x/checkers/types/errors.go b/x/checkers/types/errors.go index 0cb2024..73fd7a1 100644 --- a/x/checkers/types/errors.go +++ b/x/checkers/types/errors.go @@ -27,4 +27,6 @@ var ( ErrCannotRefundWager = sdkerrors.Register(ModuleName, 1116, "cannot refund wager to: %s") ErrCannotPayWinnings = sdkerrors.Register(ModuleName, 1117, "cannot pay winnings to winner: %s") ErrNotInRefundState = sdkerrors.Register(ModuleName, 1118, "game is not in a state to refund, move count: %d") + ErrWinnerNotParseable = sdkerrors.Register(ModuleName, 1119, "winner is not parseable: %s") + ErrThereIsNoWinner = sdkerrors.Register(ModuleName, 1120, "there is no winner") )