#include #include #include #include "game_lifecycle.h" typedef struct { uint32_t value; } test_random_t; static uint32_t next_random(void *context) { test_random_t *random = context; random->value = random->value * 1664525U + 1013904223U; return random->value; } static game_lifecycle_t new_lifecycle(test_random_t *random) { game_lifecycle_t lifecycle; game_lifecycle_init(&lifecycle, (random_source_t){.next_u32 = next_random, .context = random}); return lifecycle; } static void join_players(game_lifecycle_t *lifecycle, uint8_t *player_1, uint8_t *player_2) { assert(game_lifecycle_join(lifecycle, ROLE_PLAYER_1, "Alice", player_1) == LIFECYCLE_RESULT_OK); assert(game_lifecycle_join(lifecycle, ROLE_PLAYER_2, "Bob", player_2) == LIFECYCLE_RESULT_OK); assert(*player_1 == 0U && *player_2 == 1U); } static void finish_for_player_1(game_lifecycle_t *lifecycle, uint8_t player_1) { game_state_t *state = &lifecycle->game.state; state->current_player = 0U; memset(&state->boards[1], 0, sizeof(state->boards[1])); state->boards[1].ships_alive = 1U; state->boards[1].ships[0] = (ship_t){.x = 0, .y = 0, .length = 1, .horizontal = true}; state->boards[1].cells[0] = CELL_SHIP; shot_result_t result = {0}; assert(game_lifecycle_shot(lifecycle, player_1, state->game_id, (coordinate_t){0, 0}, &result) == LIFECYCLE_RESULT_OK); assert(result.finished && state->phase == PHASE_FINISHED && state->winner == 0U); } static void test_capacity_sanitize_and_resume(void) { test_random_t random = {.value = 1}; game_lifecycle_t lifecycle = new_lifecycle(&random); uint8_t player_1 = 0; uint8_t player_2 = 0; assert(game_lifecycle_join(&lifecycle, ROLE_PLAYER_2, "Bob", &player_2) == LIFECYCLE_RESULT_NO_PLAYER_SLOT); join_players(&lifecycle, &player_1, &player_2); assert(game_lifecycle_join(&lifecycle, ROLE_PLAYER_1, "Other", &player_1) == LIFECYCLE_RESULT_NO_PLAYER_SLOT); assert(game_lifecycle_join(&lifecycle, ROLE_SPECTATOR, "", &player_1) == LIFECYCLE_RESULT_INVALID_NAME); for (uint8_t index = 0; index < kSpectatorCapacity; ++index) { assert(game_lifecycle_join(&lifecycle, ROLE_SPECTATOR, "Watch", &player_1) == LIFECYCLE_RESULT_OK); } assert(game_lifecycle_join(&lifecycle, ROLE_SPECTATOR, "Extra", &player_1) == LIFECYCLE_RESULT_NO_SPECTATOR_SLOT); const uint8_t token[kSessionTokenBytes] = {0}; uint8_t saved_token[kSessionTokenBytes] = {0}; memcpy(saved_token, lifecycle.sessions.entries[player_2].token, sizeof(saved_token)); assert(memcmp(token, saved_token, sizeof(token)) != 0); assert(game_lifecycle_disconnect(&lifecycle, player_2) == LIFECYCLE_RESULT_OK); assert(!lifecycle.sessions.entries[player_2].connected); uint8_t resumed = kSessionCapacity; assert(game_lifecycle_resume(&lifecycle, saved_token, &resumed) == LIFECYCLE_RESULT_OK); assert(resumed == player_2 && lifecycle.sessions.entries[resumed].role == ROLE_PLAYER_2); } static void test_human_lifecycle_and_guards(void) { test_random_t random = {.value = 9}; game_lifecycle_t lifecycle = new_lifecycle(&random); uint8_t player_1 = 0; uint8_t player_2 = 0; join_players(&lifecycle, &player_1, &player_2); const uint32_t game_id = lifecycle.game.state.game_id; game_lifecycle_t before = lifecycle; assert(game_lifecycle_configure(&lifecycle, player_2, game_id, MODE_BOT) == LIFECYCLE_RESULT_FORBIDDEN_ROLE); assert(memcmp(&before, &lifecycle, sizeof(lifecycle)) == 0); assert(game_lifecycle_start(&lifecycle, player_1, game_id + 1U) == LIFECYCLE_RESULT_STALE_GAME); assert(game_lifecycle_start(&lifecycle, player_1, game_id) == LIFECYCLE_RESULT_OK); assert(lifecycle.game.state.phase == PHASE_IN_PROGRESS); finish_for_player_1(&lifecycle, player_1); assert(lifecycle.cumulative[0].games == 1U && lifecycle.cumulative[0].wins == 1U); assert(lifecycle.cumulative[1].games == 1U && lifecycle.cumulative[1].losses == 1U); assert(game_lifecycle_rematch(&lifecycle, player_1, game_id) == LIFECYCLE_RESULT_OK); assert(lifecycle.game.state.phase == PHASE_REMATCH_WAIT); assert(game_lifecycle_rematch(&lifecycle, player_2, game_id) == LIFECYCLE_RESULT_OK); assert(lifecycle.game.state.phase == PHASE_IN_PROGRESS && lifecycle.game.state.game_id != game_id); assert(lifecycle.cumulative[0].wins == 1U); assert(game_lifecycle_disconnect(&lifecycle, player_2) == LIFECYCLE_RESULT_OK); assert(game_lifecycle_abort(&lifecycle, player_1, lifecycle.game.state.game_id) == LIFECYCLE_RESULT_OK); assert(lifecycle.game.state.phase == PHASE_LOBBY); } static void test_bot_lifecycle_and_stale_actions(void) { test_random_t random = {.value = 17}; game_lifecycle_t lifecycle = new_lifecycle(&random); uint8_t player_1 = 0; assert(game_lifecycle_join(&lifecycle, ROLE_PLAYER_1, "Alice", &player_1) == LIFECYCLE_RESULT_OK); const uint32_t game_id = lifecycle.game.state.game_id; assert(game_lifecycle_configure(&lifecycle, player_1, game_id, MODE_BOT) == LIFECYCLE_RESULT_OK); assert(lifecycle.bot_reserved); assert(game_lifecycle_join(&lifecycle, ROLE_PLAYER_2, "Bob", &player_1) == LIFECYCLE_RESULT_NO_PLAYER_SLOT); assert(game_lifecycle_start(&lifecycle, player_1, game_id) == LIFECYCLE_RESULT_OK); finish_for_player_1(&lifecycle, player_1); assert(game_lifecycle_rematch(&lifecycle, player_1, game_id) == LIFECYCLE_RESULT_OK); assert(lifecycle.game.state.phase == PHASE_IN_PROGRESS && lifecycle.game.state.game_id != game_id); const game_lifecycle_t before = lifecycle; assert(game_lifecycle_shot(&lifecycle, player_1, game_id, (coordinate_t){0, 0}, NULL) == LIFECYCLE_RESULT_STALE_GAME); assert(memcmp(&before, &lifecycle, sizeof(lifecycle)) == 0); } int main(void) { test_capacity_sanitize_and_resume(); test_human_lifecycle_and_guards(); test_bot_lifecycle_and_stale_actions(); puts("game lifecycle tests passed"); return 0; }