#include #include #include #include "game_lifecycle.h" typedef struct { uint32_t value; } test_random_t; typedef struct { uint32_t delay_ms; uint8_t calls; } test_scheduler_t; static uint32_t next_random(void *context) { test_random_t *random = context; random->value = random->value * 1664525U + 1013904223U; return random->value; } static bool schedule_after(void *context, uint32_t delay_ms) { test_scheduler_t *scheduler = context; scheduler->delay_ms = delay_ms; ++scheduler->calls; return true; } static coordinate_t first_cell(const board_t *board, cell_t cell) { for (uint8_t index = 0; index < kBoardCellCount; ++index) { if (board->cells[index] == cell) return (coordinate_t){.x = (uint8_t)(index % kBoardWidth), .y = (uint8_t)(index / kBoardWidth)}; } assert(false); return (coordinate_t){0}; } static void test_bot_game_lifecycle(void) { test_random_t random = {.value = 101U}; test_scheduler_t scheduler = {0}; game_lifecycle_t lifecycle; game_lifecycle_init(&lifecycle, (random_source_t){.next_u32 = next_random, .context = &random}); game_lifecycle_set_bot_scheduler(&lifecycle, (scheduler_t){.schedule_after_ms = schedule_after, .context = &scheduler}); uint8_t player = 0; assert(game_lifecycle_join(&lifecycle, ROLE_PLAYER_1, "Alice", &player) == LIFECYCLE_RESULT_OK); const uint32_t game_id = lifecycle.game.state.game_id; assert(game_lifecycle_configure(&lifecycle, player, game_id, MODE_BOT) == LIFECYCLE_RESULT_OK); assert(game_lifecycle_start(&lifecycle, player, game_id) == LIFECYCLE_RESULT_OK); assert(lifecycle.game.state.phase == PHASE_IN_PROGRESS && lifecycle.game.state.mode == MODE_BOT); if (lifecycle.game.state.current_player == 0U) { assert(game_lifecycle_shot(&lifecycle, player, game_id, first_cell(&lifecycle.game.state.boards[1], CELL_WATER), NULL) == LIFECYCLE_RESULT_OK); } assert(lifecycle.game.state.current_player == 1U); assert(lifecycle.bot.turn_pending && scheduler.calls == 1U); assert(scheduler.delay_ms >= 500U && scheduler.delay_ms <= 900U); assert(game_lifecycle_bot_take_turn(&lifecycle)); assert(lifecycle.game.state.statistics[1].shots == 1U); assert(lifecycle.game.state.statistics[1].shots == lifecycle.game.state.statistics[1].hits + lifecycle.game.state.statistics[1].misses); for (uint16_t attempts = 0; attempts < kBoardCellCount && lifecycle.game.state.current_player == 1U && lifecycle.game.state.phase == PHASE_IN_PROGRESS; ++attempts) { assert(game_lifecycle_bot_take_turn(&lifecycle)); } assert(lifecycle.game.state.phase == PHASE_IN_PROGRESS && lifecycle.game.state.current_player == 0U); while (lifecycle.game.state.phase == PHASE_IN_PROGRESS) { assert(game_lifecycle_shot(&lifecycle, player, game_id, first_cell(&lifecycle.game.state.boards[1], CELL_SHIP), NULL) == LIFECYCLE_RESULT_OK); } assert(lifecycle.game.state.winner == 0U); assert(lifecycle.game.state.statistics[0].ships_sunk == kFleetShipCount); for (uint8_t side = 0; side < kPlayerCapacity; ++side) { const match_statistics_t *match = &lifecycle.game.state.statistics[side]; const cumulative_statistics_t *total = &lifecycle.cumulative[side]; assert(total->games == 1U && total->shots == match->shots && total->hits == match->hits && total->misses == match->misses && total->ships_sunk == match->ships_sunk); } assert(lifecycle.cumulative[0].wins == 1U && lifecycle.cumulative[1].losses == 1U); assert(game_lifecycle_rematch(&lifecycle, player, game_id) == LIFECYCLE_RESULT_OK); assert(lifecycle.game.state.phase == PHASE_IN_PROGRESS && lifecycle.game.state.game_id != game_id); assert(lifecycle.game.state.statistics[0].shots == 0U && lifecycle.game.state.statistics[1].shots == 0U); assert(lifecycle.cumulative[0].games == 1U && lifecycle.cumulative[0].wins == 1U); game_lifecycle_init(&lifecycle, (random_source_t){.next_u32 = next_random, .context = &random}); assert(lifecycle.game.state.phase == PHASE_LOBBY && lifecycle.game.state.game_id == 1U); assert(lifecycle.cumulative[0].games == 0U && lifecycle.cumulative[1].games == 0U); assert(!lifecycle.sessions.entries[0].occupied && !lifecycle.sessions.entries[1].occupied); } static void test_bot_can_receive_the_first_turn(void) { for (uint32_t seed = 1U; seed < 100U; ++seed) { test_random_t random = {.value = seed}; test_scheduler_t scheduler = {0}; game_lifecycle_t lifecycle; game_lifecycle_init(&lifecycle, (random_source_t){.next_u32 = next_random, .context = &random}); game_lifecycle_set_bot_scheduler(&lifecycle, (scheduler_t){.schedule_after_ms = schedule_after, .context = &scheduler}); uint8_t player = 0; assert(game_lifecycle_join(&lifecycle, ROLE_PLAYER_1, "Alice", &player) == LIFECYCLE_RESULT_OK); assert(game_lifecycle_configure(&lifecycle, player, lifecycle.game.state.game_id, MODE_BOT) == LIFECYCLE_RESULT_OK); assert(game_lifecycle_start(&lifecycle, player, lifecycle.game.state.game_id) == LIFECYCLE_RESULT_OK); if (lifecycle.game.state.current_player != 1U) continue; assert(lifecycle.bot.turn_pending && scheduler.calls == 1U); assert(scheduler.delay_ms >= 500U && scheduler.delay_ms <= 900U); return; } assert(false); } int main(void) { test_bot_game_lifecycle(); test_bot_can_receive_the_first_turn(); puts("bot game integration tests passed"); return 0; }