penguins  1.0.0
tests.c
Go to the documentation of this file.
1 // NOTE: Refer to <https://github.com/nemequ/munit/blob/master/example.c> for
2 // information on using our testing library.
3 
4 #include "board.h"
5 #include "game.h"
6 #include "movement.h"
7 #include "placement.h"
8 #include "utils.h"
9 #include <munit.h>
10 #include <stdio.h>
11 
12 static MunitResult test_game_clone(const MunitParameter* params, void* data) {
13  UNUSED(params), UNUSED(data);
14  Game* game = game_new();
15  game_begin_setup(game);
16  setup_board(game, 20, 20);
17  game_set_players_count(game, 3);
18  game_set_player_name(game, 0, "A");
19  game_set_player_name(game, 1, "B");
20  game_set_player_name(game, 2, NULL);
22  Game* clone = game_clone(game);
23  game_free(game);
24  game_free(clone);
25  return MUNIT_OK;
26 }
27 
28 static void setup_test_game(
29  Game* game,
30  int players_count,
31  int penguins_per_player,
32  int board_width,
33  int board_height,
34  // The encoding for this is:
35  // ~ - a water tile
36  // 0-9 - a fish tile
37  // A-Z - a penguin tile
38  const char* board_data
39 ) {
40  game_begin_setup(game);
41  game_set_players_count(game, players_count);
42  game_set_penguins_per_player(game, penguins_per_player);
43  for (int i = 0; i < players_count; i++) {
44  char name[] = { 'A' + i, '\0' };
45  game_set_player_name(game, i, name);
46  }
47  setup_board(game, board_width, board_height);
48  for (int y = 0; y < board_height; y++) {
49  for (int x = 0; x < board_width; x++) {
50  Coords coords = { x, y };
51  char tile = *board_data;
52  if ('1' <= tile && tile <= '9') {
53  short fish = tile - '0';
54  set_tile(game, coords, fish);
55  } else if ('A' <= tile && tile <= 'Z') {
56  int player_idx = tile - 'A';
57  Player* player = game_get_player(game, player_idx);
58  set_tile(game, coords, PENGUIN_TILE(player->id));
59  game_add_player_penguin(game, player_idx, coords);
60  } else {
61  set_tile(game, coords, WATER_TILE);
62  }
63  if (tile != '\0') {
64  board_data++;
65  }
66  }
67  }
68  game_end_setup(game);
69 }
70 
71 static MunitResult test_placeable_spot_exists(const MunitParameter* params, void* data) {
72  UNUSED(params), UNUSED(data);
73  Game* game = game_new();
74  const char* board = "~12"
75  "A21";
76  setup_test_game(game, /*players*/ 1, /*penguins*/ 1, /*width*/ 3, /*height*/ 2, board);
77  munit_assert_true(any_valid_placement_exists(game));
78  game_free(game);
79  return MUNIT_OK;
80 }
81 
82 static MunitResult test_placeable_spot_does_not_exist(const MunitParameter* params, void* data) {
83  UNUSED(params), UNUSED(data);
84  Game* game = game_new();
85  const char* board = "~~2"
86  "AB3";
87  setup_test_game(game, /*players*/ 2, /*penguins*/ 1, /*width*/ 3, /*height*/ 2, board);
88  munit_assert_false(any_valid_placement_exists(game));
89  game_free(game);
90  return MUNIT_OK;
91 }
92 
93 static MunitResult
94 test_valid_movement_exists_for_player(const MunitParameter* params, void* data) {
95  UNUSED(params), UNUSED(data);
96  Game* game = game_new();
97  const char* board = "B~A"
98  "~13";
99  setup_test_game(game, /*players*/ 2, /*penguins*/ 1, /*width*/ 3, /*height*/ 2, board);
100  munit_assert_true(any_valid_player_move_exists(game, 0));
101  game_free(game);
102  return MUNIT_OK;
103 }
104 
105 static MunitResult
106 test_no_valid_movement_exists_for_player(const MunitParameter* params, void* data) {
107  UNUSED(params), UNUSED(data);
108  Game* game = game_new();
109  const char* board = "B~A"
110  "~13";
111  setup_test_game(game, /*players*/ 2, /*penguins*/ 1, /*width*/ 3, /*height*/ 2, board);
112  munit_assert_false(any_valid_player_move_exists(game, 1));
113  game_free(game);
114  return MUNIT_OK;
115 }
116 
117 static MunitResult test_detect_valid_movement(const MunitParameter* params, void* data) {
118  UNUSED(params), UNUSED(data);
119  Game* game = game_new();
120  const char* board = "A13";
121  setup_test_game(game, /*players*/ 1, /*penguins*/ 1, /*width*/ 3, /*height*/ 1, board);
122  game_set_current_player(game, 0);
123  Coords fail = { -1, -1 };
124  MovementError result = validate_movement(game, (Coords){ 0, 0 }, (Coords){ 2, 0 }, &fail);
125  munit_assert_int(result, ==, MOVEMENT_VALID);
126  game_free(game);
127  return MUNIT_OK;
128 }
129 
130 static MunitResult
131 test_movement_over_empty_space_invalid(const MunitParameter* params, void* data) {
132  UNUSED(params), UNUSED(data);
133  Game* game = game_new();
134  const char* board = "1A~3";
135  setup_test_game(game, /*players*/ 1, /*penguins*/ 1, /*width*/ 4, /*height*/ 1, board);
136  game_set_current_player(game, 0);
137  Coords fail = { -1, -1 };
138  MovementError result = validate_movement(game, (Coords){ 1, 0 }, (Coords){ 3, 0 }, &fail);
139  munit_assert_int(result, ==, MOVEMENT_OVER_EMPTY_TILE);
140  game_free(game);
141  return MUNIT_OK;
142 }
143 
144 static MunitResult test_movement_over_penguin_invalid(const MunitParameter* params, void* data) {
145  UNUSED(params), UNUSED(data);
146  Game* game = game_new();
147  const char* board = "1AB3";
148  setup_test_game(game, /*players*/ 2, /*penguins*/ 1, /*width*/ 4, /*height*/ 1, board);
149  game_set_current_player(game, 0);
150  Coords fail = { -1, -1 };
151  MovementError result = validate_movement(game, (Coords){ 1, 0 }, (Coords){ 3, 0 }, &fail);
152  munit_assert_int(result, ==, MOVEMENT_OVER_PENGUIN);
153  game_free(game);
154  return MUNIT_OK;
155 }
156 
157 static MunitResult
158 test_move_penguin_and_calculate_points(const MunitParameter* params, void* data) {
159  UNUSED(params), UNUSED(data);
160  Game* game = game_new();
161  const char* board = "A13";
162  setup_test_game(game, /*players*/ 1, /*penguins*/ 1, /*width*/ 3, /*height*/ 1, board);
163  movement_begin(game);
164  game_set_current_player(game, 0);
165  move_penguin(game, (Coords){ 0, 0 }, (Coords){ 2, 0 });
166  munit_assert_int(get_tile(game, (Coords){ 0, 0 }), ==, WATER_TILE);
167  munit_assert_int(get_tile(game, (Coords){ 1, 0 }), ==, FISH_TILE(1));
168  munit_assert_int(get_tile(game, (Coords){ 2, 0 }), ==, PENGUIN_TILE(1));
169  munit_assert_int(game_get_player(game, 0)->points, ==, 3);
170  game_free(game);
171  return MUNIT_OK;
172 }
173 
174 static MunitTest board_suite_tests[] = {
175  {
176  .name = "/cloning the Game produces a deep copy",
177  .test = test_game_clone,
178  },
179  {
180  .name = "/detects that a placeable spot exists somewhere on the board",
182  .setup = NULL,
183  .tear_down = NULL,
184  .options = MUNIT_TEST_OPTION_NONE,
185  .parameters = NULL,
186  },
187  {
188  .name = "/detects that there are no valid placement slots on the board",
190  .setup = NULL,
191  .tear_down = NULL,
192  .options = MUNIT_TEST_OPTION_NONE,
193  .parameters = NULL,
194  },
195  {
196  .name = "/detects that there are valid movement options for a player",
198  .setup = NULL,
199  .tear_down = NULL,
200  .options = MUNIT_TEST_OPTION_NONE,
201  .parameters = NULL,
202  },
203  {
204  .name = "/detects that there are no valid movement options for a player",
206  .setup = NULL,
207  .tear_down = NULL,
208  .options = MUNIT_TEST_OPTION_NONE,
209  .parameters = NULL,
210  },
211  {
212  .name = "/detects that a move is going to be valid",
214  .setup = NULL,
215  .tear_down = NULL,
216  .options = MUNIT_TEST_OPTION_NONE,
217  .parameters = NULL,
218  },
219  {
220  .name = "/detects that a move over an empty space is going to be invalid",
222  .setup = NULL,
223  .tear_down = NULL,
224  .options = MUNIT_TEST_OPTION_NONE,
225  .parameters = NULL,
226  },
227  {
228  .name = "/detects that a move over a penguin is going to be invalid",
230  .setup = NULL,
231  .tear_down = NULL,
232  .options = MUNIT_TEST_OPTION_NONE,
233  .parameters = NULL,
234  },
235  {
236  .name = "/updates the board and returns number of fish captured during a movement",
238  .setup = NULL,
239  .tear_down = NULL,
240  .options = MUNIT_TEST_OPTION_NONE,
241  .parameters = NULL,
242  },
243  // Marker of the end of the array, don't touch.
244  { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
245 };
246 
247 static const MunitSuite board_suite = {
248  .prefix = "/board",
249  .tests = board_suite_tests,
250  .suites = NULL,
251  .iterations = 1,
252  .options = MUNIT_SUITE_OPTION_NONE,
253 };
254 
255 int main(int argc, char* argv[]) {
256  return munit_suite_main(&board_suite, NULL, argc, argv);
257 }
Functions for working with the game board (and the specifics of its encoding)
#define FISH_TILE(fish)
Definition: board.h:22
#define PENGUIN_TILE(player_id)
Definition: board.h:23
#define WATER_TILE
Definition: board.h:21
The core of the unified game logic library, contains the Game struct.
Movement phase functions.
MovementError
Definition: movement.h:16
@ MOVEMENT_OVER_EMPTY_TILE
Definition: movement.h:25
@ MOVEMENT_VALID
Definition: movement.h:17
@ MOVEMENT_OVER_PENGUIN
Definition: movement.h:26
Placement phase functions.
A pair of 2D coordinates, used for addressing the Game::board_grid.
Definition: utils.h:15
The central struct of the application, holds the game data and settings.
Definition: game.h:237
void game_set_penguins_per_player(Game *self, int value)
Sets Game::penguins_per_player (the value mustn't be negative) and allocates Player::penguins lists o...
Definition: game.c:248
ALWAYS_INLINE void set_tile(Game *game, Coords coords, short value)
Sets the value of the tile at coords (and also sets the attribute TILE_DIRTY). Fails if coords are ou...
Definition: board.h:117
void move_penguin(Game *game, Coords start, Coords target)
Creates a GameLogMovement entry. The requested move must be valid.
Definition: movement.c:118
ALWAYS_INLINE short get_tile(const Game *game, Coords coords)
Returns the value of the tile at coords. Fails if coords are outside the bounds.
Definition: board.h:108
void game_add_player_penguin(Game *self, int idx, Coords coords)
Definition: game.c:297
void game_set_current_player(Game *self, int idx)
Sets Game::current_player_index and creates a GameLogPlayerChange log entry.
Definition: game.c:195
void game_begin_setup(Game *self)
Switches to the GAME_PHASE_SETUP phase, can only be called in GAME_PHASE_NONE. Should be called right...
Definition: game.c:209
bool any_valid_player_move_exists(const Game *game, int player_idx)
Definition: movement.c:47
void setup_board(Game *game, int width, int height)
Sets Game::board_width and Game::board_height and allocates Game::board_grid and Game::tile_attribute...
Definition: board.c:12
bool any_valid_placement_exists(const Game *game)
Definition: placement.c:51
void game_end_setup(Game *self)
Verifies that all fields have been initialized and configured and switches the phase from GAME_PHASE_...
Definition: game.c:224
Game * game_clone(const Game *other)
Creates a (deep) copy of another Game.
Definition: game.c:36
void movement_begin(Game *game)
Enters the GAME_PHASE_MOVEMENT phase, can only be called in GAME_PHASE_SETUP_DONE.
Definition: movement.c:11
void game_set_player_name(Game *self, int idx, const char *name)
Sets the Player::name of a player at the given index. Only available in the GAME_PHASE_SETUP phase.
Definition: game.c:287
MovementError validate_movement(const Game *game, Coords start, Coords target, Coords *fail)
Definition: movement.c:76
Player * game_get_player(const Game *self, int idx)
Returns a pointer to the player at the given index. Fails if the index isn't within the bounds of the...
Definition: game.h:394
Game * game_new(void)
Constructs a Game. Allocates memory for storing the struct itself, setting all fields to default valu...
Definition: game.c:15
void game_free(Game *self)
Destroys a Game, freeing the memory allocated for the struct itself and all associated internal lists...
Definition: game.c:68
void game_set_players_count(Game *self, int count)
Sets Game::players_count (the value mustn't be negative) and allocates the Game::players list....
Definition: game.c:264
Holds the data of the players of the Game.
Definition: game.h:68
short id
The unique ID, usually (but not necessarily) is just index + 1.
Definition: game.h:70
int main(int argc, char *argv[])
Definition: tests.c:255
static MunitResult test_movement_over_empty_space_invalid(const MunitParameter *params, void *data)
Definition: tests.c:131
static void setup_test_game(Game *game, int players_count, int penguins_per_player, int board_width, int board_height, const char *board_data)
Definition: tests.c:28
static MunitResult test_move_penguin_and_calculate_points(const MunitParameter *params, void *data)
Definition: tests.c:158
static MunitResult test_placeable_spot_does_not_exist(const MunitParameter *params, void *data)
Definition: tests.c:82
static MunitResult test_placeable_spot_exists(const MunitParameter *params, void *data)
Definition: tests.c:71
static MunitResult test_detect_valid_movement(const MunitParameter *params, void *data)
Definition: tests.c:117
static MunitResult test_no_valid_movement_exists_for_player(const MunitParameter *params, void *data)
Definition: tests.c:106
static MunitResult test_valid_movement_exists_for_player(const MunitParameter *params, void *data)
Definition: tests.c:94
static MunitResult test_game_clone(const MunitParameter *params, void *data)
Definition: tests.c:12
static MunitTest board_suite_tests[]
Definition: tests.c:174
static const MunitSuite board_suite
Definition: tests.c:247
static MunitResult test_movement_over_penguin_invalid(const MunitParameter *params, void *data)
Definition: tests.c:144
#define UNUSED(var)
Helper for silencing warnings about unused variables.
Definition: utils.h:72