hex

hex.git
git clone git://git.lenczewski.org/hex.git
Log | Files | Refs

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