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 }