hexes.c (6739B)
1 #include "hexes.h" 2 3 struct opts opts = { 4 .log_level = LOG_INFO, 5 .agent_type = AGENT_RANDOM, 6 .host = NULL, 7 .port = NULL, 8 }; 9 10 static bool 11 argparse(int argc, char **argv, struct opts *opts); 12 13 enum game_state { 14 GAME_START, 15 GAME_RECV, 16 GAME_SEND, 17 GAME_END, 18 }; 19 20 struct game { 21 struct network network; 22 struct threadpool threadpool; 23 struct board board; 24 struct agent agent; 25 26 size_t round, thread_limit, mem_limit_mib; 27 struct timespec timer; 28 enum hex_player player, opponent; 29 30 enum game_state state; 31 bool game_over; 32 }; 33 34 static struct game game = { 35 .state = GAME_START, 36 }; 37 38 static void 39 start_handler(struct game *game); 40 41 static void 42 recv_handler(struct game *game); 43 44 static void 45 send_handler(struct game *game); 46 47 static void 48 end_handler(struct game *game); 49 50 int 51 main(int argc, char **argv) 52 { 53 srandom(getpid()); 54 55 if (!argparse(argc, argv, &opts)) exit(EXIT_FAILURE); 56 57 dbglog(LOG_DEBUG, "Opts: log_level: %" PRIu32 ", agent_type: %" PRIu32 ", host: %s, port: %s\n", 58 opts.log_level, opts.agent_type, opts.host, opts.port); 59 60 if (!network_init(&game.network, opts.host, opts.port)) { 61 dbglog(LOG_ERROR, "Failed to initialise network (connecting to %s:%s)\n", opts.host, opts.port); 62 exit(EXIT_FAILURE); 63 } 64 65 while (!game.game_over) { 66 dbglog(LOG_INFO, "==============================\n"); 67 68 switch (game.state) { 69 case GAME_START: start_handler(&game); break; 70 case GAME_RECV: recv_handler(&game); break; 71 case GAME_SEND: send_handler(&game); break; 72 case GAME_END: end_handler(&game); break; 73 } 74 75 game.round++; 76 } 77 78 agent_free(&game.agent); 79 board_free(&game.board); 80 threadpool_free(&game.threadpool); 81 82 network_free(&game.network); 83 84 exit(EXIT_SUCCESS); 85 } 86 87 static bool 88 argparse(int argc, char **argv, struct opts *opts) 89 { 90 assert(opts); 91 92 char const *optstr = "va:"; 93 94 int opt; 95 while ((opt = getopt(argc, argv, optstr)) != -1) { 96 switch (opt) { 97 case 'v': 98 opts->log_level = LOG_DEBUG; 99 break; 100 101 case 'a': 102 if (strcmp(optarg, "random") == 0) { 103 opts->agent_type = AGENT_RANDOM; 104 } else if (strcmp(optarg, "mcts") == 0) { 105 opts->agent_type = AGENT_MCTS; 106 } else { 107 fprintf(stderr, "Unknown agent: %s.\n", optarg); 108 goto error; 109 } 110 break; 111 112 default: goto error; /* ? */ 113 } 114 } 115 116 if (optind + 2 > argc) goto error; 117 118 opts->host = argv[optind++]; 119 opts->port = argv[optind]; 120 121 return true; 122 123 error: 124 fprintf(stderr, "Usage: %s [-v] [-a random|mcts] <host> <port>\n", argv[0]); 125 126 return false; 127 } 128 129 static void 130 start_handler(struct game *game) 131 { 132 assert(game); 133 134 enum hex_msg_type expected[] = { HEX_MSG_START, }; 135 136 struct hex_msg msg; 137 if (!network_recv(&game->network, &msg, expected, ARRLEN(expected))) { 138 dbglog(LOG_ERROR, "Failed to receive message from server\n"); 139 goto error; 140 } 141 142 game->player = msg.data.start.player; 143 game->opponent = hexopponent(game->player); 144 game->timer.tv_sec = msg.data.start.game_secs; 145 game->thread_limit = msg.data.start.thread_limit; 146 game->mem_limit_mib = msg.data.start.mem_limit_mib; 147 148 dbglog(LOG_INFO, "Received game parameters: player: %s, board size: %" PRIu32 ", game secs: %" PRIu32 ", thread limit: %" PRIu32 ", mem limit (MiB): %" PRIu32 "\n", 149 hexplayerstr(game->player), msg.data.start.board_size, game->timer.tv_sec, game->thread_limit, game->mem_limit_mib); 150 151 if (!threadpool_init(&game->threadpool, msg.data.start.thread_limit - 1)) { 152 dbglog(LOG_ERROR, "Failed to initialise threadpool\n"); 153 goto error; 154 } 155 156 if (!board_init(&game->board, msg.data.start.board_size)) { 157 dbglog(LOG_ERROR, "Failed to initialise board\n"); 158 goto error; 159 } 160 161 if (!agent_init(&game->agent, (enum agent_type) opts.agent_type, &game->board, 162 &game->threadpool, game->mem_limit_mib, game->player)) { 163 dbglog(LOG_ERROR, "Failed to initialise agent\n"); 164 goto error; 165 } 166 167 switch (game->player) { 168 case HEX_PLAYER_BLACK: game->state = GAME_SEND; break; 169 case HEX_PLAYER_WHITE: game->state = GAME_RECV; break; 170 } 171 172 return; 173 174 error: 175 game->state = GAME_END; 176 } 177 178 static void 179 recv_handler(struct game *game) 180 { 181 assert(game); 182 183 enum hex_msg_type expected[] = { HEX_MSG_MOVE, HEX_MSG_SWAP, HEX_MSG_END, }; 184 185 struct hex_msg msg; 186 if (!network_recv(&game->network, &msg, expected, ARRLEN(expected))) { 187 dbglog(LOG_ERROR, "Failed to receive message from server\n"); 188 goto error; 189 } 190 191 switch (msg.type) { 192 case HEX_MSG_MOVE: { 193 dbglog(LOG_INFO, "Received move {x=%" PRIu32 ", y=%" PRIu32 "} from opponent\n", 194 msg.data.move.board_x, msg.data.move.board_y); 195 196 if (!board_play(&game->board, game->opponent, msg.data.move.board_x, 197 msg.data.move.board_y)) { 198 dbglog(LOG_ERROR, "Failed to play received move on board\n"); 199 goto error; 200 } 201 202 agent_play(&game->agent, game->opponent, msg.data.move.board_x, msg.data.move.board_y); 203 204 if (game->round == 1 && /* TODO: calculate when to attempt to swap board */ false) { 205 game->state = GAME_RECV; 206 } else { 207 game->state = GAME_SEND; 208 } 209 } break; 210 211 case HEX_MSG_SWAP: { 212 dbglog(LOG_INFO, "Received swap msg from opponent\n"); 213 214 board_swap(&game->board); 215 agent_swap(&game->agent); 216 217 game->state = GAME_SEND; 218 } break; 219 220 case HEX_MSG_END: { 221 dbglog(LOG_INFO, "Player %s has won the game\n", hexplayerstr(msg.data.end.winner)); 222 223 game->state = GAME_END; 224 } break; 225 } 226 227 return; 228 229 error: 230 game->state = GAME_END; 231 } 232 233 static void 234 send_handler(struct game *game) 235 { 236 assert(game); 237 238 struct hex_msg msg = { 239 .type = HEX_MSG_MOVE, 240 }; 241 242 size_t total_rounds = ((game->board.size * game->board.size) / 2) + 1; 243 244 struct timespec timeout = { 245 .tv_sec = game->timer.tv_sec / (total_rounds - game->round), 246 }, start, end, diff, new_timer; 247 248 clock_gettime(CLOCK_MONOTONIC, &start); 249 if (!agent_next(&game->agent, timeout, &msg.data.move.board_x, &msg.data.move.board_y)) { 250 dbglog(LOG_ERROR, "Failed to generate next move\n"); 251 goto error; 252 } 253 clock_gettime(CLOCK_MONOTONIC, &end); 254 255 difftimespec(&end, &start, &diff); 256 difftimespec(&game->timer, &diff, &new_timer); 257 game->timer = new_timer; 258 259 dbglog(LOG_INFO, "Generated move: {x=%" PRIu32 ", y=%" PRIu32 "}\n", msg.data.move.board_x, msg.data.move.board_y); 260 261 if (!board_play(&game->board, game->player, msg.data.move.board_x, msg.data.move.board_y)) { 262 dbglog(LOG_ERROR, "Failed to play generated move on board\n"); 263 goto error; 264 } 265 266 agent_play(&game->agent, game->player, msg.data.move.board_x, msg.data.move.board_y); 267 268 if (!network_send(&game->network, &msg)) { 269 dbglog(LOG_ERROR, "Failed to send message to server\n"); 270 goto error; 271 } 272 273 game->state = GAME_RECV; 274 275 return; 276 277 error: 278 game->state = GAME_END; 279 } 280 281 static void 282 end_handler(struct game *game) 283 { 284 assert(game); 285 286 dbglog(LOG_INFO, "Game over. Goodbye, World!\n"); 287 288 game->game_over = true; 289 } 290 291 #include "network.c" 292 #include "board.c" 293 #include "threadpool.c" 294 #include "utils.c" 295 #include "agent.c"