More game states + helper methods

This commit is contained in:
Derek Jamison 2023-02-12 13:05:32 -05:00
parent ca5515c8b9
commit 58ccc2ef0b

View File

@ -59,9 +59,15 @@ typedef enum {
StatePaper = MovePaper, // P StatePaper = MovePaper, // P
StateRock = MoveRock, // R StateRock = MoveRock, // R
StateScissors = MoveScissors, // S StateScissors = MoveScissors, // S
StateLost = 'L', StateLostRock = 'L',
StateTie = 'T', StateLostPaper = 'l',
StateWon = 'W', StateLostScissors = '-',
StateTieRock = 'T',
StateTiePaper = 't',
StateTieScissors = 'x',
StateWonRock = 'W',
StateWonPaper = 'w',
StateWonScissors = '+',
StateErrorRemoteTimeout = '7', // Joined but didn't make any moves. StateErrorRemoteTimeout = '7', // Joined but didn't make any moves.
StateErrorRemoteFast = '8', // Remote user sent moves after than local user. StateErrorRemoteFast = '8', // Remote user sent moves after than local user.
StateErrorLocalFast = '9', // Local user sent moves after than remote user. StateErrorLocalFast = '9', // Local user sent moves after than remote user.
@ -262,9 +268,6 @@ static void rps_render_callback(Canvas* canvas, void* ctx) {
GameState remotePlayer = data->remotePlayer; GameState remotePlayer = data->remotePlayer;
canvas_set_font(canvas, FontSecondary); canvas_set_font(canvas, FontSecondary);
// Temporary - Show game state.
furi_string_printf(data->buffer, "State: %c - %c", localPlayer, remotePlayer);
canvas_draw_str_aligned(canvas, 5, 8, AlignLeft, AlignTop, furi_string_get_cstr(data->buffer));
// Temporary - Just show game text. // Temporary - Just show game text.
switch (localPlayer) { switch (localPlayer) {
@ -282,7 +285,10 @@ static void rps_render_callback(Canvas* canvas, void* ctx) {
break; break;
case StateCount2: case StateCount2:
canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignTop, "Pick U:Rock R:Paper D:Scissors"); canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignTop, "^ Rock");
canvas_draw_str_aligned(canvas, 50, 30, AlignLeft, AlignTop, "> Paper");
canvas_draw_str_aligned(canvas, 5, 50, AlignLeft, AlignTop, "v Scissors");
break; break;
case StateRock: case StateRock:
@ -297,16 +303,43 @@ static void rps_render_callback(Canvas* canvas, void* ctx) {
canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignTop, "Scissors"); canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignTop, "Scissors");
break; break;
case StateWon: case StateWonRock:
canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignTop, "You won!!!"); canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignTop, "You won!!!");
canvas_draw_str_aligned(canvas, 5, 30, AlignLeft, AlignTop, "Rock v. Scissors");
break;
case StateWonPaper:
canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignTop, "You won!!!");
canvas_draw_str_aligned(canvas, 5, 30, AlignLeft, AlignTop, "Paper v. Rock");
break;
case StateWonScissors:
canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignTop, "You won!!!");
canvas_draw_str_aligned(canvas, 5, 30, AlignLeft, AlignTop, "Scissors v. Paper");
break; break;
case StateTie: case StateTieRock:
canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignTop, "You tied!"); canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignTop, "You tied!");
canvas_draw_str_aligned(canvas, 5, 30, AlignLeft, AlignTop, "Rock v. Rock");
break;
case StateTiePaper:
canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignTop, "You tied!");
canvas_draw_str_aligned(canvas, 5, 30, AlignLeft, AlignTop, "Paper v. Paper");
break;
case StateTieScissors:
canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignTop, "You tied!");
canvas_draw_str_aligned(canvas, 5, 30, AlignLeft, AlignTop, "Scissors v. Scissors");
break; break;
case StateLost: case StateLostRock:
canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignTop, "You lost."); canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignTop, "You lost.");
canvas_draw_str_aligned(canvas, 5, 30, AlignLeft, AlignTop, "Rock v. Paper");
break;
case StateLostPaper:
canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignTop, "You lost.");
canvas_draw_str_aligned(canvas, 5, 30, AlignLeft, AlignTop, "Paper v. Scissors");
break;
case StateLostScissors:
canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignTop, "You lost.");
canvas_draw_str_aligned(canvas, 5, 30, AlignLeft, AlignTop, "Scissors v. Rock");
break; break;
case StateError: case StateError:
@ -411,9 +444,59 @@ static uint32_t duration(uint32_t tick) {
return current - tick; return current - tick;
} }
// Checks if game state is winner.
// @param state GameState to check.
// @returns true if game state is a winner.
static bool isWin(GameState state) {
return (StateWonPaper == state) ||
(StateWonRock == state) ||
(StateWonScissors == state);
}
// Checks if game state is lost.
// @param state GameState to check.
// @returns true if game state is a loss.
static bool isLoss(GameState state) {
return (StateLostPaper == state) ||
(StateLostRock == state) ||
(StateLostScissors == state);
}
// Checks if game state is tie.
// @param state GameState to check.
// @returns true if game state is a tie.
static bool isTie(GameState state) {
return (StateTiePaper == state) ||
(StateTieRock == state) ||
(StateTieScissors == state);
}
// Checks if game state is result (win/loss/tie).
// @param state GameState to check.
// @returns true if game state is a win, loss or tie.
static bool isResult(GameState state) {
return isWin(state) || isLoss(state) || isTie(state);
}
// Checks if game state is final move (rock/paper/scissors).
// @param state GameState to check.
// @returns true if game state is a rock, paper, scissors.
static bool isFinalMove(GameState state) {
return (StateRock == state) ||
(StatePaper == state) ||
(StateScissors == state);
}
static bool isError(GameState state) {
return (StateError == state) ||
(StateErrorLocalFast == state) ||
(StateErrorRemoteFast == state) ||
(StateErrorRemoteTimeout == state);
}
// Temporary timings, since I don't have second Flipper & send commands via laptop. // Temporary timings, since I don't have second Flipper & send commands via laptop.
#define DURATION_NO_MOVE_DETECTED_ERROR 60000 #define DURATION_NO_MOVE_DETECTED_ERROR 60000
#define DURATION_SHOW_ERROR 15000 #define DURATION_SHOW_ERROR 3000
#define DURATION_SHOW_MOVES 5000 #define DURATION_SHOW_MOVES 5000
#define DURATION_WIN_LOSS_TIE 10000 #define DURATION_WIN_LOSS_TIE 10000
@ -436,7 +519,7 @@ static void rps_state_machine_update(GameContext* game_context) {
} }
// TEMP - After Error, we reset back to Looking for player. // TEMP - After Error, we reset back to Looking for player.
if ((StateErrorRemoteTimeout == d->localPlayer) && if (isError(d->localPlayer) &&
(duration(d->localMoveTick) > DURATION_SHOW_ERROR)) { (duration(d->localMoveTick) > DURATION_SHOW_ERROR)) {
d->remotePlayer = StateLookingForPlayer; d->remotePlayer = StateLookingForPlayer;
d->remoteMoveTick = furi_get_tick(); d->remoteMoveTick = furi_get_tick();
@ -447,7 +530,7 @@ static void rps_state_machine_update(GameContext* game_context) {
} }
// TEMP - After Win, Loss, Tie - we reset back to Ready. // TEMP - After Win, Loss, Tie - we reset back to Ready.
if (((StateWon == d->localPlayer) || (StateLost == d->localPlayer) || (StateTie == d->localPlayer)) && if (isResult(d->localPlayer) &&
(duration(d->localMoveTick) > DURATION_WIN_LOSS_TIE)) { (duration(d->localMoveTick) > DURATION_WIN_LOSS_TIE)) {
d->remotePlayer = StateReady; d->remotePlayer = StateReady;
d->remoteMoveTick = furi_get_tick(); d->remoteMoveTick = furi_get_tick();
@ -459,37 +542,47 @@ static void rps_state_machine_update(GameContext* game_context) {
} }
// Check for winner. // Check for winner.
if ((duration(d->localMoveTick) > DURATION_SHOW_MOVES) && if (isFinalMove(d->localPlayer) && isFinalMove(d->remotePlayer) &&
(duration(d->remoteMoveTick) > DURATION_SHOW_MOVES)) { (duration(d->localMoveTick) > DURATION_SHOW_MOVES)) {
if ((d->localPlayer == d->remotePlayer) && d->localMoveTick = furi_get_tick();
((d->localPlayer == StateRock) || d->remoteMoveTick = furi_get_tick();
(d->localPlayer == StatePaper) || if ((d->localPlayer == StateRock) && (d->remotePlayer == StateScissors)) {
(d->localPlayer == StateScissors))) { d->localPlayer = StateWonRock;
d->localPlayer = StateTie; d->remotePlayer = StateLostScissors;
d->localMoveTick = furi_get_tick(); FURI_LOG_I(TAG, "Local won w/Rock.");
d->remotePlayer = StateTie; } else if ((d->localPlayer == StateScissors) && (d->remotePlayer == StatePaper)) {
d->remoteMoveTick = furi_get_tick(); d->localPlayer = StateWonScissors;
d->remotePlayer = StateLostPaper;
FURI_LOG_I(TAG, "Local won w/Scissors.");
} else if ((d->localPlayer == StatePaper) && (d->remotePlayer == StateRock)) {
d->localPlayer = StateWonPaper;
d->remotePlayer = StateLostRock;
FURI_LOG_I(TAG, "Local won w/Paper.");
} else if ((d->localPlayer == StateRock) && (d->remotePlayer == StatePaper)) {
d->localPlayer = StateLostRock;
d->remotePlayer = StateWonPaper;
FURI_LOG_I(TAG, "Remote won w/Paper.");
} else if ((d->localPlayer == StateScissors) && (d->remotePlayer == StateRock)) {
d->localPlayer = StateLostScissors;
d->remotePlayer = StateWonRock;
FURI_LOG_I(TAG, "Remote won w/Rock.");
} else if ((d->localPlayer == StatePaper) && (d->remotePlayer == StateScissors)) {
d->localPlayer = StateLostPaper;
d->remotePlayer = StateWonScissors;
FURI_LOG_I(TAG, "Remote won w/Scissors.");
} else {
FURI_LOG_I(TAG, "Tie game."); FURI_LOG_I(TAG, "Tie game.");
return; if (d->localPlayer == StateRock) {
} else if (((d->localPlayer == StateRock) && (d->remotePlayer == StateScissors)) || d->localPlayer = StateTieRock;
((d->localPlayer == StateScissors) && (d->remotePlayer == StatePaper)) || d->remotePlayer = StateTieRock;
((d->localPlayer == StatePaper) && (d->remotePlayer == StateRock))) { } else if (d->localPlayer == StatePaper) {
d->localPlayer = StateWon; d->localPlayer = StateTiePaper;
d->localMoveTick = furi_get_tick(); d->remotePlayer = StateTiePaper;
d->remotePlayer = StateLost; } else {
d->remoteMoveTick = furi_get_tick(); d->localPlayer = StateTieScissors;
FURI_LOG_I(TAG, "Local won."); d->remotePlayer = StateTieScissors;
return; }
} else if (((d->remotePlayer == StateRock) && (d->localPlayer == StateScissors)) || }
((d->remotePlayer == StateScissors) && (d->localPlayer == StatePaper)) ||
((d->remotePlayer == StatePaper) && (d->localPlayer == StateRock))) {
d->remotePlayer = StateWon;
d->remoteMoveTick = furi_get_tick();
d->localPlayer = StateLost;
d->localMoveTick = furi_get_tick();
FURI_LOG_I(TAG, "Remote won.");
return;
}
} }
} }
@ -522,48 +615,45 @@ static bool rps_state_machine_local_moved(GameContext* game_context, Move move)
} else if (MoveCount == move && StateCount1 == game_context->data->localPlayer) { } else if (MoveCount == move && StateCount1 == game_context->data->localPlayer) {
if ((StateCount1 == game_context->data->remotePlayer) || if ((StateCount1 == game_context->data->remotePlayer) ||
(StateCount2 == game_context->data->remotePlayer)) { (StateCount2 == game_context->data->remotePlayer)) {
localMove = MoveCount2; localMove = MoveCount2;
localState = StateCount2; localState = StateCount2;
} else { } else {
localState = StateErrorLocalFast; localState = StateErrorLocalFast;
FURI_LOG_I(TAG, "Local count sync error. remote is %c.", game_context->data->remotePlayer); FURI_LOG_I(TAG, "Local count sync error. remote is %c.", game_context->data->remotePlayer);
} }
} else if (MoveRock == move && StateCount2 == game_context->data->localPlayer) { } else if (StateCount2 == game_context->data->localPlayer) {
if ((StateCount2 == game_context->data->remotePlayer) || if (MoveRock == move) {
(StateRock == game_context->data->remotePlayer) || if ((StateCount2 == game_context->data->remotePlayer) ||
(StatePaper == game_context->data->remotePlayer) || isFinalMove(game_context->data->remotePlayer)) {
(StateScissors == game_context->data->remotePlayer)) {
localMove = MoveRock; localMove = MoveRock;
localState = StateRock; localState = StateRock;
} else { } else {
localState = StateErrorLocalFast; localState = StateErrorLocalFast;
FURI_LOG_I(TAG, "Local rock sync error. remote is %c.", game_context->data->remotePlayer); FURI_LOG_I(TAG, "Local rock sync error. remote is %c.", game_context->data->remotePlayer);
} }
} else if (MovePaper == move && StateCount2 == game_context->data->localPlayer) { } else if (MovePaper == move) {
if ((StateCount2 == game_context->data->remotePlayer) || if ((StateCount2 == game_context->data->remotePlayer) ||
(StateRock == game_context->data->remotePlayer) || isFinalMove(game_context->data->remotePlayer)) {
(StatePaper == game_context->data->remotePlayer) ||
(StateScissors == game_context->data->remotePlayer)) {
localMove = MovePaper; localMove = MovePaper;
localState = StatePaper; localState = StatePaper;
} else { } else {
localState = StateErrorLocalFast; localState = StateErrorLocalFast;
FURI_LOG_I(TAG, "Local paper sync error. remote is %c.", game_context->data->remotePlayer); FURI_LOG_I(TAG, "Local paper sync error. remote is %c.", game_context->data->remotePlayer);
} }
} else if (MoveScissors == move && StateCount2 == game_context->data->localPlayer) { } else if (MoveScissors == move) {
if ((StateCount2 == game_context->data->remotePlayer) || if ((StateCount2 == game_context->data->remotePlayer) ||
(StateRock == game_context->data->remotePlayer) || isFinalMove(game_context->data->remotePlayer)) {
(StatePaper == game_context->data->remotePlayer) ||
(StateScissors == game_context->data->remotePlayer)) {
localMove = MoveScissors; localMove = MoveScissors;
localState = StateScissors; localState = StateScissors;
} else { } else {
localState = StateErrorLocalFast; localState = StateErrorLocalFast;
FURI_LOG_I(TAG, "Local scissors sync error. remote is %c.", game_context->data->remotePlayer); FURI_LOG_I(TAG, "Local scissors sync error. remote is %c.", game_context->data->remotePlayer);
} }
} else {
FURI_LOG_E(TAG, "Invalid Local move '%c' error. lState=%c. rState=%c.", move, game_context->data->localPlayer, game_context->data->remotePlayer);
}
} else { } else {
FURI_LOG_E(TAG, "Local move '%c' error. lState=%c. rState=%c.", move, game_context->data->localPlayer, game_context->data->remotePlayer); FURI_LOG_E(TAG, "Invalid Local move '%c' error. lState=%c. rState=%c.", move, game_context->data->localPlayer, game_context->data->remotePlayer);
localState = StateError;
} }
if (MoveUnknown != localMove) { if (MoveUnknown != localMove) {
@ -590,41 +680,35 @@ static bool rps_state_machine_remote_moved(GameContext* game_context, Move move)
} else if (MoveCount2 == move && StateCount1 == game_context->data->remotePlayer) { } else if (MoveCount2 == move && StateCount1 == game_context->data->remotePlayer) {
if ((StateCount1 == game_context->data->localPlayer) || if ((StateCount1 == game_context->data->localPlayer) ||
(StateCount2 == game_context->data->localPlayer)) { (StateCount2 == game_context->data->localPlayer)) {
remoteState = StateCount2; remoteState = StateCount2;
} else { } else {
remoteState = StateErrorRemoteFast; remoteState = StateErrorRemoteFast;
FURI_LOG_I(TAG, "Remote count sync error. local is %c.", game_context->data->localPlayer); FURI_LOG_I(TAG, "Remote count sync error. local is %c.", game_context->data->localPlayer);
} }
} else if (MoveRock == move && StateCount2 == game_context->data->remotePlayer) { } else if (MoveRock == move && StateCount2 == game_context->data->remotePlayer) {
if ((StateCount2 == game_context->data->localPlayer) || if ((StateCount2 == game_context->data->localPlayer) ||
(StateRock == game_context->data->localPlayer) || isFinalMove(game_context->data->localPlayer)) {
(StatePaper == game_context->data->localPlayer) || remoteState = StateRock;
(StateScissors == game_context->data->localPlayer)) { } else {
remoteState = StateRock; remoteState = StateErrorRemoteFast;
} else { FURI_LOG_I(TAG, "Remote rock sync error. local is %c.", game_context->data->localPlayer);
remoteState = StateErrorRemoteFast; }
FURI_LOG_I(TAG, "Remote rock sync error. local is %c.", game_context->data->localPlayer);
}
} else if (MovePaper == move && StateCount2 == game_context->data->remotePlayer) { } else if (MovePaper == move && StateCount2 == game_context->data->remotePlayer) {
if ((StateCount2 == game_context->data->localPlayer) || if ((StateCount2 == game_context->data->localPlayer) ||
(StateRock == game_context->data->localPlayer) || isFinalMove(game_context->data->localPlayer)) {
(StatePaper == game_context->data->localPlayer) || remoteState = StatePaper;
(StateScissors == game_context->data->localPlayer)) { } else {
remoteState = StatePaper; remoteState = StateErrorRemoteFast;
} else { FURI_LOG_I(TAG, "Remote paper sync error. local is %c.", game_context->data->localPlayer);
remoteState = StateErrorRemoteFast; }
FURI_LOG_I(TAG, "Remote paper sync error. local is %c.", game_context->data->localPlayer);
}
} else if (MoveScissors == move && StateCount2 == game_context->data->remotePlayer) { } else if (MoveScissors == move && StateCount2 == game_context->data->remotePlayer) {
if ((StateCount2 == game_context->data->localPlayer) || if ((StateCount2 == game_context->data->localPlayer) ||
(StateRock == game_context->data->localPlayer) || isFinalMove(game_context->data->localPlayer)) {
(StatePaper == game_context->data->localPlayer) || remoteState = StateScissors;
(StateScissors == game_context->data->localPlayer)) { } else {
remoteState = StateScissors; remoteState = StateErrorRemoteFast;
} else { FURI_LOG_I(TAG, "Remote scissors sync error. local is %c.", game_context->data->localPlayer);
remoteState = StateErrorRemoteFast; }
FURI_LOG_I(TAG, "Remote scissors sync error. local is %c.", game_context->data->localPlayer);
}
} else { } else {
FURI_LOG_E(TAG, "Remote move '%c' error. lState=%c. rState=%c.", move, game_context->data->localPlayer, game_context->data->remotePlayer); FURI_LOG_E(TAG, "Remote move '%c' error. lState=%c. rState=%c.", move, game_context->data->localPlayer, game_context->data->remotePlayer);
remoteState = StateError; remoteState = StateError;