108 lines
5.3 KiB
C
108 lines
5.3 KiB
C
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#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;
|
|
}
|