penguins  1.0.0
autonomous.c
Go to the documentation of this file.
1 #include "autonomous.h"
2 #include "arguments.h"
3 #include "board.h"
4 #include "bot.h"
5 #include "game.h"
6 #include "interactive.h"
7 #include "movement.h"
8 #include "placement.h"
9 #include "utils.h"
10 #include <assert.h>
11 #include <ctype.h>
12 #include <stdbool.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 
17 const char* MY_AUTONOMOUS_PLAYER_NAME = "102D";
18 
19 // These are specified by the board file format
20 #define MAX_PLAYERS 9
21 #define MIN_PLAYER_ID 1
22 #define MAX_PLAYER_ID 9
23 
24 int run_autonomous_mode(const Arguments* args) {
25  const char* my_player_name = args->set_name != NULL ? args->set_name : MY_AUTONOMOUS_PLAYER_NAME;
26  if (args->action == ACTION_ARG_PRINT_NAME) {
27  printf("%s\n", my_player_name);
28  return EXIT_OK;
29  }
30 
31  Rng rng = init_stdlib_rng();
32  Game* game = game_new();
33  FILE *input_file, *output_file;
34  bool move_ok = true;
35 
36  game_begin_setup(game);
37  if (args->action == ACTION_ARG_GENERATE) {
38  game_set_players_count(game, 0);
40  setup_board(game, args->board_gen_width, args->board_gen_height);
41  switch (args->board_gen_type) {
42  case GENERATE_ARG_ISLAND: generate_board_island(game, &rng); break;
43  case GENERATE_ARG_RANDOM: generate_board_random(game, &rng); break;
44  case GENERATE_ARG_NONE: break;
45  }
46  } else if (args->input_board_file != NULL) {
47  if ((input_file = fopen(args->input_board_file, "r")) == NULL) {
48  perror("Failed to open the input board file");
49  return EXIT_INTERNAL_ERROR;
50  }
51  int penguins_arg = args->action == ACTION_ARG_PLACEMENT ? args->penguins : 0;
52  if (!load_game_state(game, input_file, penguins_arg, my_player_name)) {
53  fprintf(stderr, "Failed to parse the input file\n");
54  return EXIT_INPUT_FILE_ERROR;
55  }
56  fclose(input_file);
57  }
58  game_end_setup(game);
59 
60  if (args->action == ACTION_ARG_VIEW) {
61 #ifdef INTERACTIVE_MODE
62  print_game_state(game);
63 #else
64  printf("The app must be compiled with the interactive mode for viewing the board files!\n");
65 #endif
66  } else if (args->action == ACTION_ARG_PLACEMENT || args->action == ACTION_ARG_MOVEMENT) {
67  move_ok = false;
68  int my_player_index = -1;
69  for (int i = 0; i < game->players_count; i++) {
70  if (strcmp(game_get_player(game, i)->name, my_player_name) == 0) {
71  my_player_index = i;
72  break;
73  }
74  }
75  assert(my_player_index >= 0);
76  BotState* bot = bot_state_new(&args->bot, game, &rng);
77 
78  if (args->action == ACTION_ARG_PLACEMENT) {
79  placement_begin(game);
80  game->current_player_index = my_player_index - 1;
81  if (placement_switch_player(game) == my_player_index) {
82  Coords target;
83  move_ok = bot_compute_placement(bot, &target);
84  if (move_ok) {
85  place_penguin(game, target);
86  }
87  }
88  placement_end(game);
89  } else if (args->action == ACTION_ARG_MOVEMENT) {
90  movement_begin(game);
91  game->current_player_index = my_player_index - 1;
92  if (movement_switch_player(game) == my_player_index) {
93  Coords penguin, target;
94  move_ok = bot_compute_move(bot, &penguin, &target);
95  if (move_ok) {
96  move_penguin(game, penguin, target);
97  }
98  }
99  movement_end(game);
100  }
101 
102  bot_state_free(bot);
103  }
104 
105  if (args->output_board_file != NULL) {
106  if ((output_file = fopen(args->output_board_file, "w")) == NULL) {
107  perror("Failed to open the output board file");
108  return EXIT_INTERNAL_ERROR;
109  }
110  if (!save_game_state(game, output_file)) {
111  return EXIT_INTERNAL_ERROR;
112  }
113  fflush(output_file);
114  fclose(output_file);
115  }
116 
117  game_free(game);
118 
119  return move_ok ? EXIT_OK : EXIT_NO_POSSIBLE_MOVES;
120 }
121 
122 static size_t read_line(FILE* file, char** buf, size_t* line_len) {
123  *line_len = 0;
124  size_t buf_size = 64;
125  *buf = realloc(*buf, buf_size);
126  int c;
127  while ((c = fgetc(file)) != EOF) {
128  if (*line_len + 1 >= buf_size) {
129  buf_size *= 2;
130  *buf = realloc(*buf, buf_size);
131  }
132  (*buf)[*line_len] = (char)c;
133  *line_len += 1;
134  if (c == '\n') {
135  break;
136  }
137  }
138  (*buf)[*line_len] = '\0';
139  return *line_len;
140 }
141 
142 bool load_game_state(Game* game, FILE* file, int penguins_arg, const char* my_player_name) {
143  char* line_buf = NULL;
144  size_t line_len = 0;
145 
146  read_line(file, &line_buf, &line_len);
147  int board_width, board_height;
148  if (sscanf(line_buf, "%d %d", &board_height, &board_width) != 2) {
149  fprintf(stderr, "Failed to parse the board size line: '%s'\n", line_buf);
150  return false;
151  }
152  if (!(board_width > 0 && board_height > 0)) {
153  fprintf(stderr, "Invalid board size: %d %d\n", board_width, board_height);
154  return false;
155  }
156  setup_board(game, board_width, board_height);
157 
158  short player_ids[MAX_PLAYERS];
159  char* player_names[MAX_PLAYERS];
160  int player_scores[MAX_PLAYERS];
161 
162  int player_penguins_by_id[MAX_PLAYER_ID - MIN_PLAYER_ID + 1];
163  bool taken_player_ids[MAX_PLAYER_ID - MIN_PLAYER_ID + 1];
164  for (int i = MIN_PLAYER_ID; i <= MAX_PLAYER_ID; i++) {
165  player_penguins_by_id[i - MIN_PLAYER_ID] = 0;
166  taken_player_ids[i - MIN_PLAYER_ID] = false;
167  }
168 
169  for (int y = 0; y < board_height; y++) {
170  read_line(file, &line_buf, &line_len);
171  char* str = line_buf;
172  // Skip the leading whitespace
173  while (isspace(*str)) str++;
174  for (int x = 0; x < board_width; x++) {
175  Coords coords = { x, y };
176 
177  if (str[0] == '\0' || str[1] == '\0') {
178  // Reached the end of the string prematurely
179  set_tile(game, coords, WATER_TILE);
180  continue;
181  }
182  char c1 = str[0], c2 = str[1];
183  str += 2;
184 
185  if ('0' <= c1 && c1 <= '9' && c2 == '0') {
186  short fish = c1 - '0';
187  set_tile(game, coords, FISH_TILE(fish));
188  } else if ('1' <= c2 && c2 <= '9' && c1 == '0') {
189  short player_id = c2 - '0';
190  set_tile(game, coords, PENGUIN_TILE(player_id));
191  player_penguins_by_id[player_id - MIN_PLAYER_ID] += 1;
192  } else {
193  fprintf(stderr, "Invalid tile at x=%d y=%d: '%c%c'\n", x, y, c1, c2);
194  return false;
195  }
196 
197  // Skip the whitespace separators
198  while (isspace(*str)) str++;
199  }
200  }
201 
202  int players_count = 0;
203  for (int linenr = 0; linenr < MAX_PLAYERS; linenr++) {
204  if (!read_line(file, &line_buf, &line_len)) {
205  break;
206  }
207  char name[256];
208  int id;
209  int points;
210  if (sscanf(line_buf, "%255s %d %d", name, &id, &points) != 3) {
211  fprintf(stderr, "Failed to parse the player on line %d: '%s'\n", linenr, line_buf);
212  return false;
213  }
214  if (!(MIN_PLAYER_ID <= id && id <= MAX_PLAYER_ID)) {
215  fprintf(stderr, "Player ID on line %d falls out of the acceptable range: %d\n", linenr, id);
216  return false;
217  }
218  if (taken_player_ids[id]) {
219  fprintf(stderr, "Player ID on line %d is a duplicate: %d\n", linenr, id);
220  return false;
221  }
222  if (*name == '\0') {
223  fprintf(stderr, "Player name on line %d is empty\n", linenr);
224  return false;
225  }
226  taken_player_ids[id] = true;
227  int i = players_count;
228  player_ids[i] = (short)id;
229  player_names[i] = strdup(name);
230  player_scores[i] = points;
231  players_count += 1;
232  }
233 
234  bool my_player_found = false;
235  for (int i = 0; i < players_count; i++) {
236  if (strcmp(player_names[i], my_player_name) == 0) {
237  my_player_found = true;
238  break;
239  }
240  }
241  if (!my_player_found) {
242  short free_id = -1;
243  for (short id = MIN_PLAYER_ID; id <= MAX_PLAYER_ID; id++) {
244  if (!taken_player_ids[id]) {
245  free_id = id;
246  break;
247  }
248  }
249  if (free_id <= 0 || players_count >= MAX_PLAYERS) {
250  fprintf(stderr, "No IDs left in the input file to assign to our own player\n");
251  return false;
252  }
253  int i = players_count;
254  player_ids[i] = free_id;
255  player_names[i] = strdup(my_player_name);
256  player_scores[i] = 0;
257  players_count += 1;
258  }
259 
260  int penguins_per_player = my_max(penguins_arg, 1);
261  for (int i = 0; i < players_count; i++) {
262  penguins_per_player =
263  my_max(penguins_per_player, player_penguins_by_id[player_ids[i] - MIN_PLAYER_ID]);
264  }
265  game_set_penguins_per_player(game, penguins_per_player);
266 
267  game_set_players_count(game, players_count);
268  for (int i = 0; i < players_count; i++) {
269  game_set_player_name(game, i, player_names[i]);
270  Player* player = game_get_player(game, i);
271  player->id = player_ids[i];
272  player->points = player_scores[i];
273  for (int y = 0; y < game->board_height; y++) {
274  for (int x = 0; x < game->board_width; x++) {
275  Coords coords = { x, y };
276  short tile = get_tile(game, coords);
277  if (get_tile_player_id(tile) == player->id) {
278  game_add_player_penguin(game, i, coords);
279  }
280  }
281  }
282  assert(player->penguins_count == player_penguins_by_id[player->id - MIN_PLAYER_ID]);
283  }
284 
285  free(line_buf);
286  for (int i = 0; i < players_count; i++) {
287  free(player_names[i]);
288  }
289  return true;
290 }
291 
292 bool save_game_state(const Game* game, FILE* file) {
293  fprintf(file, "%d %d\n", game->board_height, game->board_width);
294 
295  for (int y = 0; y < game->board_height; y++) {
296  for (int x = 0; x < game->board_width; x++) {
297  Coords coords = { x, y };
298  short tile = get_tile(game, coords);
299  if (x != 0) {
300  fprintf(file, " ");
301  }
302  if (0 <= tile && tile <= 9) {
303  short fish = get_tile_fish(tile);
304  fprintf(file, "%c0", '0' + fish);
305  } else if (-9 <= tile && tile <= -1) {
306  short player_id = get_tile_player_id(tile);
307  fprintf(file, "0%c", '0' + player_id);
308  }
309  }
310  fprintf(file, "\n");
311  }
312 
313  for (int i = 0; i < game->players_count; i++) {
314  Player* player = game_get_player(game, i);
315  fprintf(file, "%s %d %d\n", player->name, player->id, player->points);
316  }
317 
318  return true;
319 }
The command-line argument parser.
@ ACTION_ARG_PRINT_NAME
Definition: arguments.h:17
@ ACTION_ARG_GENERATE
Definition: arguments.h:20
@ ACTION_ARG_MOVEMENT
Definition: arguments.h:19
@ ACTION_ARG_PLACEMENT
Definition: arguments.h:18
@ ACTION_ARG_VIEW
Definition: arguments.h:21
@ GENERATE_ARG_ISLAND
Definition: arguments.h:26
@ GENERATE_ARG_NONE
Definition: arguments.h:25
@ GENERATE_ARG_RANDOM
Definition: arguments.h:27
#define MAX_PLAYERS
Definition: autonomous.c:20
const char * MY_AUTONOMOUS_PLAYER_NAME
Definition: autonomous.c:17
bool load_game_state(Game *game, FILE *file, int penguins_arg, const char *my_player_name)
Definition: autonomous.c:142
static size_t read_line(FILE *file, char **buf, size_t *line_len)
Definition: autonomous.c:122
int run_autonomous_mode(const Arguments *args)
Definition: autonomous.c:24
bool save_game_state(const Game *game, FILE *file)
Definition: autonomous.c:292
#define MAX_PLAYER_ID
Definition: autonomous.c:22
#define MIN_PLAYER_ID
Definition: autonomous.c:21
The autonomous-mode machine interface.
@ EXIT_INTERNAL_ERROR
Definition: autonomous.h:20
@ EXIT_INPUT_FILE_ERROR
Definition: autonomous.h:19
@ EXIT_OK
Definition: autonomous.h:17
@ EXIT_NO_POSSIBLE_MOVES
Definition: autonomous.h:18
void generate_board_random(Game *game, Rng *rng)
Generates the board by setting every tile purely randomly. The resulting board will look sort of like...
Definition: board.c:26
void generate_board_island(Game *game, Rng *rng)
Generates the board which looks sort of like a big icy island.
Definition: board.c:37
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
#define get_tile_player_id(tile)
Definition: board.h:28
#define get_tile_fish(tile)
Definition: board.h:27
The bot algorithm.
The core of the unified game logic library, contains the Game struct.
void print_game_state(const Game *game)
Definition: interactive.c:122
The interactive-mode text user interface.
Movement phase functions.
Placement phase functions.
const char * set_name
Definition: arguments.h:35
ActionArg action
Definition: arguments.h:31
int board_gen_height
Definition: arguments.h:38
const char * input_board_file
Definition: arguments.h:33
const char * output_board_file
Definition: arguments.h:34
GenerateArg board_gen_type
Definition: arguments.h:39
BotParameters bot
Definition: arguments.h:36
int penguins
Definition: arguments.h:32
int board_gen_width
Definition: arguments.h:37
Contains temporary data created during the evaluation of bot's moves.
Definition: bot.h:101
bool bot_compute_placement(BotState *self, Coords *out_target)
Computes the best placement for the current player given the current game state.
Definition: bot.c:143
void bot_state_free(BotState *self)
Recursively destroys a BotState and its substates (similarly to game_free).
Definition: bot.c:75
BotState * bot_state_new(const BotParameters *params, Game *game, Rng *rng)
Constructs a BotState (similarly game_new).
Definition: bot.c:42
bool bot_compute_move(BotState *self, Coords *out_penguin, Coords *out_target)
Computes the best move for the current player given the current game state.
Definition: bot.c:295
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
int movement_switch_player(Game *game)
Performs the player switching logic for the movement phase.
Definition: movement.c:31
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_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
int board_width
Use setup_board for setting this.
Definition: game.h:264
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
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
void placement_begin(Game *game)
Enters the GAME_PHASE_PLACEMENT phase, can only be called in GAME_PHASE_SETUP_DONE.
Definition: placement.c:11
int board_height
Use setup_board for setting this.
Definition: game.h:266
int placement_switch_player(Game *game)
Performs the player switching logic for the placement phase.
Definition: placement.c:32
void movement_begin(Game *game)
Enters the GAME_PHASE_MOVEMENT phase, can only be called in GAME_PHASE_SETUP_DONE.
Definition: movement.c:11
int current_player_index
A negative value means that there is no current player selected. Use game_set_current_player for sett...
Definition: game.h:256
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
int players_count
Use game_set_players_count for setting this.
Definition: game.h:251
void place_penguin(Game *game, Coords target)
Creates a GameLogPlacement entry. The requested placement must be valid.
Definition: placement.c:93
void movement_end(Game *game)
Exits the GAME_PHASE_MOVEMENT phase and switches to GAME_PHASE_SETUP_DONE.
Definition: movement.c:20
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
void placement_end(Game *game)
Exits the GAME_PHASE_PLACEMENT phase and switches to GAME_PHASE_SETUP_DONE.
Definition: placement.c:20
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
char * name
Use game_set_player_name to set this.
Definition: game.h:72
int points
The score of the player, i.e. the number of collected fish.
Definition: game.h:74
int penguins_count
The length of the penguins array.
Definition: game.h:76
A wrapper around random number generators.
Definition: utils.h:129
Rng init_stdlib_rng(void)
Returns an RNG implementation based on the rand function from <stdlib.h> (and seeds it with srand).
Definition: utils.c:123
#define my_max(x, y)
Compares two numbers and returns the larger one.
Definition: utils.h:99