Files
battleship/test/host/test_bot_game_integration.c
T

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;
}