hex

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

agent.cpp (8009B)


      1 #ifdef _XOPEN_SOURCE
      2 #undef _XOPEN_SOURCE
      3 #endif
      4 
      5 #define _XOPEN_SOURCE 700
      6 
      7 #include "hex.h"
      8 
      9 #include <cassert>
     10 #include <cerrno>
     11 #include <cstdint>
     12 #include <cstdio>
     13 #include <cstdlib>
     14 #include <cstring>
     15 
     16 #include <arpa/inet.h>
     17 #include <netdb.h>
     18 #include <sys/socket.h>
     19 #include <sys/types.h>
     20 #include <unistd.h>
     21 
     22 #include <algorithm>
     23 #include <array>
     24 #include <iostream>
     25 #include <memory>
     26 #include <random>
     27 #include <vector>
     28 
     29 #define ARRLEN(arr) (sizeof (arr) / sizeof (arr)[0])
     30 
     31 enum class Cell {
     32 	BLACK = HEX_PLAYER_BLACK,
     33 	WHITE = HEX_PLAYER_WHITE,
     34 	EMPTY,
     35 };
     36 
     37 struct Move {
     38 	uint32_t x, y;
     39 
     40 	bool operator==(const Move &rhs) {
     41 		return this->x == rhs.x && this->y == rhs.y;
     42 	}
     43 };
     44 
     45 void swap(Move &lhs, Move &rhs) {
     46 	std::swap(lhs.x, rhs.x);
     47 	std::swap(lhs.y, rhs.y);
     48 }
     49 
     50 class Board {
     51 	uint32_t size;
     52 	std::vector<Cell> cells;
     53 	std::vector<Move> moves;
     54 
     55 public:
     56 	template <class URBG>
     57 	Board(uint32_t size, URBG &&rng) : size(size) {
     58 		cells.reserve(size * size);
     59 		moves.reserve(size * size);
     60 
     61 		for (uint32_t j = 0; j < this->size; j++) {
     62 			for (uint32_t i = 0; i < this->size; i++) {
     63 				this->cells.push_back(Cell::EMPTY);
     64 
     65 				Move move{i, j};
     66 				this->moves.push_back(move);
     67 			}
     68 		}
     69 
     70 		std::shuffle(this->moves.begin(), this->moves.end(), rng);
     71 	}
     72 
     73 	bool play(enum hex_player player, uint32_t x, uint32_t y) {
     74 		Cell &cell = this->cells.at(y * this->size + x);
     75 		if (cell != Cell::EMPTY) return false;
     76 
     77 		switch (player) {
     78 		case HEX_PLAYER_BLACK:
     79 			cell = Cell::BLACK;
     80 			break;
     81 
     82 		case HEX_PLAYER_WHITE:
     83 			cell = Cell::WHITE;
     84 			break;
     85 		}
     86 
     87 		Move move{x, y};
     88 		auto it = std::find(this->moves.begin(), this->moves.end(), move);
     89 		if (it != std::end(this->moves)) {
     90 			::swap(*it, this->moves.back());
     91 			this->moves.pop_back();
     92 		}
     93 
     94 		return true;
     95 	}
     96 
     97 	template <class URBG>
     98 	void swap(URBG &&rng) {
     99 		this->moves.clear();
    100 
    101 		for (uint32_t j = 0; j < this->size; j++) {
    102 			for (uint32_t i = 0; i < this->size; i++) {
    103 				Cell &cell = this->cells.at(j * this->size + i);
    104 
    105 				switch (cell) {
    106 				case Cell::BLACK: cell = Cell::WHITE; break;
    107 				case Cell::WHITE: cell = Cell::BLACK; break;
    108 				case Cell::EMPTY:
    109 					Move move{i, j};
    110 					this->moves.push_back(move);
    111 					break;
    112 				}
    113 			}
    114 		}
    115 
    116 		std::shuffle(this->moves.begin(), this->moves.end(), rng);
    117 	}
    118 
    119 	bool next(Move &out) {
    120 		if (this->moves.empty()) return false;
    121 
    122 		out = this->moves.back();
    123 		this->moves.pop_back();
    124 
    125 		return true;
    126 	}
    127 };
    128 
    129 class Net {
    130 	int sockfd;
    131 public:
    132 	Net() : sockfd(-1) {}
    133 
    134 	bool init(char *host, char *port) {
    135 		struct addrinfo hints, *addrinfo, *ptr;
    136 
    137 		memset(&hints, 0, sizeof hints);
    138 		hints.ai_family = AF_UNSPEC;
    139 		hints.ai_socktype = SOCK_STREAM;
    140 
    141 		int res;
    142 		if ((res = getaddrinfo(host, port, &hints, &addrinfo))) {
    143 			std::cerr << "getaddrinfo: " << gai_strerror(res) << std::endl;
    144 			return false;
    145 		}
    146 
    147 		int sockfd;
    148 		for (ptr = addrinfo; ptr; ptr = ptr->ai_next) {
    149 			sockfd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
    150 			if (sockfd == -1) continue;
    151 			if (connect(sockfd, ptr->ai_addr, ptr->ai_addrlen) != -1) break;
    152 			close(sockfd);
    153 		}
    154 
    155 		freeaddrinfo(addrinfo);
    156 
    157 		if (!ptr) {
    158 			std::cerr << "Failed to connect to " << host << ":" << port << std::endl;
    159 			return false;
    160 		}
    161 
    162 		this->sockfd = sockfd;
    163 
    164 		return true;
    165 	}
    166 
    167 	bool recv_msg(struct hex_msg &out, const std::vector<enum hex_msg_type> &expected) {
    168 		uint8_t buf[HEX_MSG_SZ];
    169 
    170 		size_t nbytes_recv = 0, len = HEX_MSG_SZ;
    171 
    172 		do {
    173 			ssize_t curr = recv(this->sockfd, buf + nbytes_recv, len - nbytes_recv, 0);
    174 			if (curr <= 0) return false; // error or socket shutdown
    175 			nbytes_recv += curr;
    176 		} while (nbytes_recv < len);
    177 
    178 		struct hex_msg msg;
    179 		if (!hex_msg_try_deserialise(buf, &msg)) return false;
    180 
    181 		if (std::find(expected.begin(), expected.end(), msg.type) != std::end(expected)) {
    182 			out = msg;
    183 			return true;
    184 		}
    185 
    186 		return false;
    187 	}
    188 
    189 	bool send_msg(const struct hex_msg &msg) {
    190 		uint8_t buf[HEX_MSG_SZ];
    191 		if (!hex_msg_try_serialise(&msg, buf)) return false;
    192 
    193 		size_t nbytes_sent = 0, len = HEX_MSG_SZ;
    194 
    195 		do {
    196 			ssize_t curr = send(this->sockfd, buf + nbytes_sent, len - nbytes_sent, 0);
    197 			if (curr <= 0) return false; // error or socket shutdown
    198 			nbytes_sent += curr;
    199 		} while (nbytes_sent < len);
    200 
    201 		return true;
    202 	}
    203 };
    204 
    205 enum class State {
    206 	START,
    207 	RECV,
    208 	SEND,
    209 	END,
    210 };
    211 
    212 std::ostream &operator<<(std::ostream &os, const State &self) {
    213 	return os << static_cast<std::underlying_type<State>::type>(self);
    214 }
    215 
    216 int
    217 main(int argc, char *argv[])
    218 {
    219 	std::minstd_rand rand;
    220 	rand.seed(getpid());
    221 
    222 	if (argc < 3) {
    223 		std::cerr << "Not enough args: "  << argv[0] << " <host> <port>" << std::endl;
    224 		exit(EXIT_FAILURE);
    225 	}
    226 
    227 	char *host = argv[1], *port = argv[2];
    228 
    229 	Net net;
    230 	if (!net.init(host, port)) {
    231 		std::cerr << "Failed to initialise network" << std::endl;
    232 		exit(EXIT_FAILURE);
    233 	}
    234 
    235 	State state = State::START;
    236 	std::unique_ptr<Board> board;
    237 
    238 	/* initialised to satisfy GCC's linter and sanitiser */
    239 	enum hex_player player = HEX_PLAYER_BLACK;
    240 	enum hex_player opponent = HEX_PLAYER_WHITE;
    241 	enum hex_player winner = HEX_PLAYER_BLACK;
    242 
    243 	// game parameters (unused)
    244 	uint32_t game_secs, thread_limit, mem_limit_mib;
    245 	(void) game_secs; (void) thread_limit; (void) mem_limit_mib;
    246 
    247 	bool game_over = false, first_round = true;
    248 	while (!game_over) {
    249 		switch (state) {
    250 		case State::START: {
    251 			std::vector<enum hex_msg_type> expected_msg_types = {HEX_MSG_START};
    252 
    253 			struct hex_msg msg;
    254 			if (!net.recv_msg(msg, expected_msg_types)) {
    255 				std::cerr << "Failed to receive message from hex server" << std::endl;
    256 				exit(EXIT_FAILURE);
    257 			}
    258 
    259 			player = static_cast<enum hex_player>(msg.data.start.player);
    260 			opponent = hexopponent(player);
    261 			game_secs = msg.data.start.game_secs;
    262 			thread_limit = msg.data.start.thread_limit;
    263 			mem_limit_mib = msg.data.start.mem_limit_mib;
    264 
    265 			uint32_t board_size = msg.data.start.board_size;
    266 
    267 			board = std::make_unique<Board>(board_size, rand);
    268 
    269 			std::cout << "[" << hexplayerstr(player) << "] Starting game: "
    270 				  << board_size << "x" << board_size << ", "
    271 				  << game_secs << "secs" << std::endl;
    272 
    273 			switch (player) {
    274 			case HEX_PLAYER_BLACK: state = State::SEND; break;
    275 			case HEX_PLAYER_WHITE: state = State::RECV; break;
    276 			}
    277 		} break;
    278 
    279 		case State::RECV: {
    280 			std::vector<enum hex_msg_type> expected_msg_types = {HEX_MSG_MOVE, HEX_MSG_SWAP, HEX_MSG_END};
    281 
    282 			struct hex_msg msg;
    283 			if (!net.recv_msg(msg, expected_msg_types)) {
    284 				std::cerr << "Failed to receive message from hex server" << std::endl;
    285 				exit(EXIT_FAILURE);
    286 			}
    287 
    288 			switch (msg.type) {
    289 			case HEX_MSG_MOVE:
    290 				board->play(opponent, msg.data.move.board_x, msg.data.move.board_y);
    291 
    292 				if (first_round && rand() % 2) {
    293 					board->swap(rand);
    294 
    295 					msg.type = HEX_MSG_SWAP;
    296 					if (!net.send_msg(msg)) {
    297 						std::cerr << "Failed to send message to hex server" << std::endl;
    298 						exit(EXIT_FAILURE);
    299 					}
    300 
    301 					state = State::RECV;
    302 				} else {
    303 					state = State::SEND;
    304 				}
    305 				break;
    306 
    307 			case HEX_MSG_SWAP:
    308 				board->swap(rand);
    309 				state = State::SEND;
    310 				break;
    311 
    312 			case HEX_MSG_END:
    313 				winner = static_cast<enum hex_player>(msg.data.end.winner);
    314 				state = State::END;
    315 				break;
    316 			}
    317 
    318 			first_round = false;
    319 		} break;
    320 
    321 		case State::SEND: {
    322 			struct hex_msg msg;
    323 			msg.type = HEX_MSG_MOVE;
    324 
    325 			Move move;
    326 			if (!board->next(move)) {
    327 				std::cerr << "Failed to generate next board move" << std::endl;
    328 				exit(EXIT_FAILURE);
    329 			}
    330 
    331 			board->play(player, move.x, move.y);
    332 
    333 			msg.data.move.board_x = move.x;
    334 			msg.data.move.board_y = move.y;
    335 
    336 			if (!net.send_msg(msg)) {
    337 				std::cerr << "Failed to send message to hex server" << std::endl;
    338 				exit(EXIT_FAILURE);
    339 			}
    340 
    341 			state = State::RECV;
    342 			first_round = false;
    343 		} break;
    344 
    345 		case State::END: {
    346 			std::cout << "[" << hexplayerstr(player) << "] Player " << hexplayerstr(winner) << " has won the game" << std::endl;
    347 			game_over = true;
    348 		} break;
    349 
    350 		default:
    351 			std::cerr << "Unknown game state: " << state << std::endl;
    352 			exit(EXIT_FAILURE);
    353 			break;
    354 		}
    355 	}
    356 
    357 	exit(EXIT_SUCCESS);
    358 }