hex

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

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"