agent.c (9578B)
1 #ifdef _XOPEN_SOURCE 2 #undef _XOPEN_SOURCE 3 #endif 4 5 #define _XOPEN_SOURCE 700 6 7 #include "hex.h" 8 9 #include <assert.h> 10 #include <errno.h> 11 #include <inttypes.h> 12 #include <stdbool.h> 13 #include <stdint.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 #include <arpa/inet.h> 19 #include <netdb.h> 20 #include <sys/socket.h> 21 #include <sys/types.h> 22 #include <unistd.h> 23 24 #define ARRLEN(arr) (sizeof (arr) / sizeof (arr)[0]) 25 26 int 27 net_init(char *restrict host, char *restrict port); 28 29 bool 30 net_recv_msg(int sock, struct hex_msg *out, enum hex_msg_type expected[], size_t len); 31 32 bool 33 net_send_msg(int sock, struct hex_msg *msg); 34 35 enum board_cell { 36 CELL_BLACK = HEX_PLAYER_BLACK, 37 CELL_WHITE = HEX_PLAYER_WHITE, 38 CELL_EMPTY, 39 }; 40 41 struct move { 42 uint32_t x, y; 43 }; 44 45 void 46 move_swap(struct move *restrict lhs, struct move *restrict rhs); 47 48 struct board { 49 uint32_t size; 50 51 enum board_cell *cells; 52 53 size_t moves_len; 54 struct move *moves; 55 }; 56 57 bool 58 board_init(struct board *self, uint32_t size); 59 60 bool 61 board_play(struct board *self, enum hex_player player, uint32_t x, uint32_t y); 62 63 void 64 board_swap(struct board *self); 65 66 bool 67 board_next(struct board *self, uint32_t *out_x, uint32_t *out_y); 68 69 enum game_state { 70 GAME_START, 71 GAME_RECV, 72 GAME_SEND, 73 GAME_END, 74 }; 75 76 int 77 main(int argc, char **argv) 78 { 79 srandom(getpid()); 80 81 if (argc < 3) { 82 fprintf(stderr, "Not enough args: %s <host> <port>\n", argv[0]); 83 exit(EXIT_FAILURE); 84 } 85 86 char *host = argv[1], *port = argv[2]; 87 88 int sockfd = net_init(host, port); 89 if (sockfd == -1) { 90 fprintf(stderr, "Failed to initialise network\n"); 91 exit(EXIT_FAILURE); 92 } 93 94 enum game_state game_state = GAME_START; 95 struct board board; 96 97 /* initialised to satisfy GCC's linters and sanitisers */ 98 enum hex_player player = HEX_PLAYER_BLACK; 99 enum hex_player opponent = HEX_PLAYER_WHITE; 100 enum hex_player winner = HEX_PLAYER_BLACK; 101 102 uint32_t game_secs, thread_limit, mem_limit_mib; // currently unused 103 (void) game_secs; (void) thread_limit; (void) mem_limit_mib; 104 105 bool game_over = false, first_round = true; 106 while (!game_over) { 107 switch (game_state) { 108 case GAME_START: { 109 enum hex_msg_type expected_msg_types[] = { 110 HEX_MSG_START, 111 }; 112 113 struct hex_msg msg; 114 if (!net_recv_msg(sockfd, &msg, expected_msg_types, ARRLEN(expected_msg_types))) { 115 fprintf(stderr, "Failed to receive message from hex server\n"); 116 exit(EXIT_FAILURE); 117 } 118 119 // unpack all parameters 120 player = msg.data.start.player; 121 opponent = hexopponent(player); 122 game_secs = msg.data.start.game_secs; 123 thread_limit = msg.data.start.thread_limit; 124 mem_limit_mib = msg.data.start.mem_limit_mib; 125 126 uint32_t board_size = msg.data.start.board_size; 127 128 if (!board_init(&board, board_size)) { 129 fprintf(stderr, "Failed to allocate game board of size %" PRIu32 "x%" PRIu32 "\n", 130 board_size, board_size); 131 exit(EXIT_FAILURE); 132 } 133 134 printf("[%s] Starting game: %" PRIu32 "x%" PRIu32 ", %" PRIu32 " secs\n", 135 hexplayerstr(player), board_size, board_size, game_secs); 136 137 switch (player) { 138 case HEX_PLAYER_BLACK: game_state = GAME_SEND; break; 139 case HEX_PLAYER_WHITE: game_state = GAME_RECV; break; 140 } 141 } break; 142 143 case GAME_RECV: { 144 enum hex_msg_type expected_msg_types[] = { 145 HEX_MSG_MOVE, 146 HEX_MSG_SWAP, 147 HEX_MSG_END, 148 }; 149 150 struct hex_msg msg; 151 if (!net_recv_msg(sockfd, &msg, expected_msg_types, ARRLEN(expected_msg_types))) { 152 fprintf(stderr, "Failed to receive message from hex server\n"); 153 exit(EXIT_FAILURE); 154 } 155 156 switch (msg.type) { 157 case HEX_MSG_MOVE: 158 board_play(&board, opponent, msg.data.move.board_x, msg.data.move.board_y); 159 160 if (first_round && random() % 2) { 161 board_swap(&board); 162 163 msg.type = HEX_MSG_SWAP; 164 if (!net_send_msg(sockfd, &msg)) { 165 fprintf(stderr, "Failed to send swap message to hex server\n"); 166 exit(EXIT_FAILURE); 167 } 168 169 game_state = GAME_RECV; 170 } else { 171 game_state = GAME_SEND; 172 } 173 break; 174 175 case HEX_MSG_SWAP: 176 board_swap(&board); 177 game_state = GAME_SEND; 178 break; 179 180 case HEX_MSG_END: 181 winner = msg.data.end.winner; 182 game_state = GAME_END; 183 break; 184 } 185 186 first_round = false; 187 } break; 188 189 case GAME_SEND: { 190 struct hex_msg msg = { 191 .type = HEX_MSG_MOVE, 192 }; 193 194 if (!board_next(&board, &msg.data.move.board_x, &msg.data.move.board_y)) { 195 fprintf(stderr, "Failed to generate next board move\n"); 196 exit(EXIT_FAILURE); 197 } 198 199 board_play(&board, player, msg.data.move.board_x, msg.data.move.board_y); 200 201 if (!net_send_msg(sockfd, &msg)) { 202 fprintf(stderr, "Failed to send message to hex server\n"); 203 exit(EXIT_FAILURE); 204 } 205 206 game_state = GAME_RECV; 207 first_round = false; 208 } break; 209 210 case GAME_END: { 211 printf("[%s] Player %s has won the game\n", 212 hexplayerstr(player), hexplayerstr(winner)); 213 game_over = true; 214 } break; 215 216 default: 217 fprintf(stderr, "Unknown game state: %d\n", game_state); 218 exit(EXIT_FAILURE); 219 break; 220 } 221 } 222 223 exit(EXIT_SUCCESS); 224 } 225 226 int 227 net_init(char *restrict host, char *restrict port) 228 { 229 assert(host); 230 assert(port); 231 232 struct addrinfo hints = { 233 .ai_family = AF_UNSPEC, 234 .ai_socktype = SOCK_STREAM, 235 }, *addrinfo, *ptr; 236 237 int res; 238 if ((res = getaddrinfo(host, port, &hints, &addrinfo))) { 239 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(res)); 240 return -1; 241 } 242 243 int sockfd; 244 for (ptr = addrinfo; ptr; ptr = ptr->ai_next) { 245 sockfd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); 246 if (sockfd == -1) continue; 247 if (connect(sockfd, ptr->ai_addr, ptr->ai_addrlen) != -1) break; 248 close(sockfd); 249 } 250 251 freeaddrinfo(addrinfo); 252 253 if (!ptr) { 254 fprintf(stderr, "Failed to connect to %s:%s\n", host, port); 255 return -1; 256 } 257 258 return sockfd; 259 } 260 261 static inline size_t 262 net_recv_all(int sock, uint8_t *buf, size_t len) 263 { 264 assert(buf); 265 266 size_t nbytes_received = 0; 267 268 do { 269 ssize_t res = recv(sock, buf + nbytes_received, len - nbytes_received, 0); 270 if (res <= 0) break; // error or socket shutdown 271 nbytes_received += res; 272 } while (nbytes_received < len); 273 274 return nbytes_received; 275 } 276 277 bool 278 net_recv_msg(int sock, struct hex_msg *out, enum hex_msg_type expected[], size_t len) 279 { 280 assert(out); 281 assert(expected); 282 283 uint8_t buf[HEX_MSG_SZ]; 284 if (!(net_recv_all(sock, buf, HEX_MSG_SZ) == HEX_MSG_SZ)) return false; 285 286 struct hex_msg msg; 287 if (!hex_msg_try_deserialise(buf, &msg)) return false; 288 289 for (size_t i = 0; i < len; i++) { 290 if (msg.type == expected[i]) { 291 *out = msg; 292 return true; 293 } 294 } 295 296 return false; 297 } 298 299 static inline size_t 300 net_send_all(int sock, uint8_t *buf, size_t len) 301 { 302 assert(buf); 303 304 size_t nbytes_sent = 0; 305 306 do { 307 ssize_t res = send(sock, buf + nbytes_sent, len - nbytes_sent, 0); 308 if (res <= 0) break; // error or socket shutdown 309 nbytes_sent += res; 310 } while (nbytes_sent < len); 311 312 return nbytes_sent; 313 } 314 315 bool 316 net_send_msg(int sock, struct hex_msg *msg) 317 { 318 assert(msg); 319 320 uint8_t buf[HEX_MSG_SZ]; 321 if (!hex_msg_try_serialise(msg, buf)) return false; 322 323 return net_send_all(sock, buf, HEX_MSG_SZ) == HEX_MSG_SZ; 324 } 325 326 void 327 move_swap(struct move *restrict lhs, struct move *restrict rhs) 328 { 329 assert(lhs); 330 assert(rhs); 331 332 struct move tmp = *lhs; 333 *lhs = *rhs; 334 *rhs = tmp; 335 } 336 337 static void 338 shuffle_moves(struct move *arr, size_t len) 339 { 340 for (size_t i = 0; i < len - 2; i++) { 341 size_t j = (i + random()) % len; 342 move_swap(&arr[i], &arr[j]); 343 } 344 } 345 346 bool 347 board_init(struct board *self, uint32_t size) 348 { 349 assert(self); 350 351 self->size = size; 352 353 if (!(self->cells = malloc(size * size * sizeof *self->cells))) 354 return false; 355 356 self->moves_len = size * size; 357 if (!(self->moves = malloc(size * size * sizeof *self->moves))) { 358 free(self->cells); 359 return false; 360 } 361 362 for (size_t j = 0; j < size; j++) { 363 for (size_t i = 0; i < size; i++) { 364 size_t idx = j * size + i; 365 366 self->cells[idx] = CELL_EMPTY; 367 368 self->moves[idx].x = i; 369 self->moves[idx].y = j; 370 } 371 } 372 373 shuffle_moves(self->moves, self->moves_len); 374 375 return true; 376 } 377 378 bool 379 board_play(struct board *self, enum hex_player player, uint32_t x, uint32_t y) 380 { 381 assert(self); 382 383 enum board_cell *cell = &self->cells[y * self->size + x]; 384 if (*cell != CELL_EMPTY) return false; 385 386 switch (player) { 387 case HEX_PLAYER_BLACK: 388 *cell = CELL_BLACK; 389 break; 390 391 case HEX_PLAYER_WHITE: 392 *cell = CELL_WHITE; 393 break; 394 395 default: 396 return false; 397 } 398 399 for (size_t i = 0; i < self->moves_len; i++) { 400 if (self->moves[i].x == x && self->moves[i].y == y) { 401 move_swap(&self->moves[i], &self->moves[--self->moves_len]); 402 break; 403 } 404 } 405 406 return true; 407 } 408 409 void 410 board_swap(struct board *self) 411 { 412 assert(self); 413 414 self->moves_len = 0; 415 416 for (size_t j = 0; j < self->size; j++) { 417 for (size_t i = 0; i < self->size; i++) { 418 enum board_cell *cell = &self->cells[j * self->size + i]; 419 420 switch (*cell) { 421 case CELL_BLACK: 422 *cell = CELL_WHITE; 423 break; 424 425 case CELL_WHITE: 426 *cell = CELL_BLACK; 427 break; 428 429 default: { 430 struct move *move = &self->moves[self->moves_len++]; 431 move->x = i; 432 move->y = j; 433 } break; 434 } 435 } 436 } 437 438 shuffle_moves(self->moves, self->moves_len); 439 } 440 441 bool 442 board_next(struct board *self, uint32_t *out_x, uint32_t *out_y) 443 { 444 assert(self); 445 assert(out_x); 446 assert(out_y); 447 448 if (self->moves_len == 0) return false; 449 450 struct move move = self->moves[--self->moves_len]; 451 *out_x = move.x; 452 *out_y = move.y; 453 454 return true; 455 } 456 457 extern inline bool 458 hex_msg_try_serialise(struct hex_msg const *msg, uint8_t out[static HEX_MSG_SZ]); 459 460 extern inline bool 461 hex_msg_try_deserialise(uint8_t buf[static HEX_MSG_SZ], struct hex_msg *out); 462 463 extern inline char const * 464 hexplayerstr(enum hex_player val); 465 466 extern inline enum hex_player 467 hexopponent(enum hex_player val);