hex

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

commit a593bd19866624eb2888e175658c7ed1bfcd492a
parent 31bd4a0eb11f8f889a17a1ba0f005362a7e5b2b8
Author: MikoĊ‚aj Lenczewski <mblenczewski@gmail.com>
Date:   Sun, 28 Apr 2024 15:15:43 +0000

Port server to new protocol definitions, change server args

Diffstat:
Mserver/build.sh | 8++------
Dserver/include/hex.h | 248-------------------------------------------------------------------------------
Dserver/include/hex/proto.h | 175-------------------------------------------------------------------------------
Dserver/include/hex/types.h | 64----------------------------------------------------------------
Aserver/include/hex_server.h | 280+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mserver/src/board.c | 2+-
Dserver/src/hex.c | 203-------------------------------------------------------------------------------
Aserver/src/main.c | 160+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dserver/src/proto.c | 7-------
Mserver/src/server.c | 14+++++++-------
Mserver/src/utils.c | 16+++++++++++-----
Mtournament-host.py | 4++--
12 files changed, 463 insertions(+), 718 deletions(-)

diff --git a/server/build.sh b/server/build.sh @@ -5,17 +5,13 @@ CC="${CC:-cc}" WARNINGS="-Wall -Wextra -Wpedantic -Werror" CFLAGS="-std=c11 -Og -g" -CPPFLAGS="-UNDEBUG -Iinclude" +CPPFLAGS="-UNDEBUG -Iinclude -I../proto" LDFLAGS="" TARGET="hex-server" SOURCES=" - src/hex.c - src/server.c - src/proto.c - src/board.c - src/utils.c + src/main.c " set -ex diff --git a/server/include/hex.h b/server/include/hex.h @@ -1,248 +0,0 @@ -#ifndef HEX_H -#define HEX_H - -#ifdef _XOPEN_SOURCE -#undef _XOPEN_SOURCE -#endif /* _XOPEN_SOURCE */ - -#define _XOPEN_SOURCE 700 - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif /* _GNU_SOURCE */ - -#ifndef _DEFAULT_SOURCE -#define _DEFAULT_SOURCE -#endif /* _DEFAULT_SOURCE */ - -#ifdef __cplusplus - #include <cassert> - #include <cerrno> - #include <cinttypes> - #include <climits> - #include <cstdarg> - #include <cstdbool> - #include <cstdint> - #include <cstdio> - #include <cstdlib> - #include <cstring> -#else - #include <assert.h> - #include <errno.h> - #include <inttypes.h> - #include <limits.h> - #include <stdarg.h> - #include <stdbool.h> - #include <stdint.h> - #include <stdio.h> - #include <stdlib.h> - #include <string.h> -#endif /* __cplusplus */ - -#include <arpa/inet.h> -#include <fcntl.h> -#include <netdb.h> -#include <poll.h> -#include <signal.h> -#include <sys/resource.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <time.h> -#include <unistd.h> - -#include "hex/types.h" -#include "hex/proto.h" - -/* timeout for accept()-ing an agent connection before assuming a forfeit - */ -#define HEX_AGENT_ACCEPT_TIMEOUT_MS (1 * 1000) - -#define HEX_AGENT_LOGFILE_TEMPLATE "/tmp/hex-agent.XXXXXX" -#define HEX_AGENT_LOGFILE_MODE (0666) - -extern struct args { - char *agent_1; - uid_t agent_1_uid; - char *agent_2; - uid_t agent_2_uid; - u32 board_dimensions; - u32 game_secs; - u32 thread_limit; - u32 mem_limit_mib; - b32 verbose; -} args; - -enum hex_error { - HEX_ERROR_OK, - HEX_ERROR_GAME_OVER, - HEX_ERROR_TIMEOUT, - HEX_ERROR_BAD_MOVE, - HEX_ERROR_BAD_MSG, - HEX_ERROR_DISCONNECT, - HEX_ERROR_SERVER, -}; - -inline char const * -hexerrorstr(enum hex_error val) -{ - switch (val) { - case HEX_ERROR_OK: return "OK"; - case HEX_ERROR_GAME_OVER: return "GAME_OVER"; - case HEX_ERROR_TIMEOUT: return "TIMEOUT"; - case HEX_ERROR_BAD_MOVE: return "BAD_MOVE"; - case HEX_ERROR_BAD_MSG: return "BAD_MSG"; - case HEX_ERROR_DISCONNECT: return "DISCONNECT"; - case HEX_ERROR_SERVER: return "SERVER"; - default: return "UNKNOWN"; - } -} - -struct statistics { - char *agent_1; - b32 agent_1_won; - u32 agent_1_rounds; - f32 agent_1_secs; - enum hex_error agent_1_err; - char *agent_2; - b32 agent_2_won; - u32 agent_2_rounds; - f32 agent_2_secs; - enum hex_error agent_2_err; -}; - -struct agent_state { - /* which player are we, and what agent do we run */ - enum hex_player player; - char *agent; - uid_t agent_uid; - char logfile[PATH_MAX]; - - /* how much time this agent has left to execute before it times out */ - struct timespec timer; - - /* socket for communicating with agent */ - int sockfd; - struct sockaddr_storage sock_addr; - socklen_t sock_addrlen; -}; - -enum cell_state { - CELL_EMPTY, - CELL_BLACK, - CELL_WHITE, -}; - -struct board_segment { - s16 parent_relptr; /* pointer to root of rooted tree */ - u8 rank; /* disambiguation between identical segments */ - u8 cell; /* the owner of the current cell */ -}; - -static inline s16 -board_segment_abs2rel(struct board_segment *base, struct board_segment *absptr) { - return RELPTR_ABS2REL(s16, base, absptr); -} - -static inline struct board_segment * -board_segment_rel2abs(struct board_segment *base, s16 relptr) { - return RELPTR_REL2ABS(struct board_segment *, s16, base, relptr); -} - -extern struct board_segment * -board_segment_root(struct board_segment *self); - -extern void -board_segment_merge(struct board_segment *restrict self, struct board_segment *restrict elem); - -extern b32 -board_segment_joined(struct board_segment *self, struct board_segment *elem); - -struct board_state { - u32 size; - - /* track connections between board "segments" (groups of cells owned - * by one player), and the edges for each player - */ - struct board_segment black_source, black_sink, white_source, white_sink; - struct board_segment segments[]; -}; - -extern struct board_state * -board_alloc(size_t size); - -extern void -board_free(struct board_state *self); - -extern void -board_print(struct board_state *self); - -extern b32 -board_play(struct board_state *self, enum hex_player player, s32 x, s32 y); - -extern void -board_swap(struct board_state *self); - -extern b32 -board_completed(struct board_state *self, enum hex_player *winner); - -struct server_state { - struct agent_state black_agent, white_agent; - struct board_state *board; - - /* socket for accepting agent connections */ - int servfd; - struct sockaddr_storage serv_addr; - socklen_t serv_addrlen; - char serv_host[NI_MAXHOST], serv_port[NI_MAXSERV]; -}; - -extern bool -server_init(struct server_state *state); - -extern void -server_free(struct server_state *state); - -extern bool -server_spawn_agent(struct server_state *state, struct agent_state *agent_state); - -extern void -server_wait_all_agents(struct server_state *state); - -extern void -server_run(struct server_state *state, struct statistics *statistics); - -inline void -errlog(char *fmt, ...) -{ - va_list va; - - va_start(va, fmt); - vfprintf(stderr, fmt, va); - va_end(va); -} - -inline void -dbglog(char *fmt, ...) -{ - va_list va; - - va_start(va, fmt); - if (args.verbose) vfprintf(stderr, fmt, va); - va_end(va); -} - -inline void -difftimespec(struct timespec *restrict lhs, struct timespec *restrict rhs, struct timespec *restrict out) -{ - if (lhs->tv_sec <= rhs->tv_sec && lhs->tv_nsec < rhs->tv_nsec) { - out->tv_sec = 0; - out->tv_nsec = 0; - } else { - out->tv_sec = lhs->tv_sec - rhs->tv_sec - (lhs->tv_nsec < rhs->tv_nsec); - out->tv_nsec = lhs->tv_nsec - rhs->tv_nsec + (lhs->tv_nsec < rhs->tv_nsec) * NANOSECS; - } -} - -#endif /* HEX_H */ diff --git a/server/include/hex/proto.h b/server/include/hex/proto.h @@ -1,175 +0,0 @@ -#ifndef HEX_PROTO_H -#define HEX_PROTO_H - -#include "hex/types.h" - -#ifdef __cplusplus - #include <cassert> - #include <cstring> -#else - #include <assert.h> - #include <string.h> -#endif /* __cplusplus */ - -#include <arpa/inet.h> - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -enum hex_player { - HEX_PLAYER_BLACK = 0, - HEX_PLAYER_WHITE = 1, -}; - -inline enum hex_player -hexopponent(enum hex_player player) -{ - switch (player) { - case HEX_PLAYER_BLACK: return HEX_PLAYER_WHITE; - case HEX_PLAYER_WHITE: return HEX_PLAYER_BLACK; - default: assert(false); return HEX_PLAYER_BLACK; - } -} - -inline char const * -hexplayerstr(enum hex_player val) -{ - switch (val) { - case HEX_PLAYER_BLACK: return "black"; - case HEX_PLAYER_WHITE: return "white"; - default: return "(err)"; - } -} - -enum hex_msg_type { - HEX_MSG_START = 0, - HEX_MSG_MOVE = 1, - HEX_MSG_SWAP = 2, - HEX_MSG_END = 3, -}; - -struct hex_msg_start { - u32 player; - u32 board_size; - u32 game_secs; - u32 thread_limit; - u32 mem_limit_mib; -}; - -struct hex_msg_move { - u32 board_x; - u32 board_y; -}; - -struct hex_msg_end { - u32 winner; -}; - -union hex_msg_data { - struct hex_msg_start start; - struct hex_msg_move move; - struct hex_msg_end end; -}; - -struct hex_msg { - u32 type; - union hex_msg_data data; -}; - -#define HEX_MSG_SZ 32 - -inline b32 -#ifdef __cplusplus -hex_msg_try_serialise(struct hex_msg const *msg, u8 (&out)[HEX_MSG_SZ]) -#else -hex_msg_try_serialise(struct hex_msg const *msg, u8 out[static HEX_MSG_SZ]) -#endif -{ - assert(msg); - assert(out); - - u32 *bufp = (u32 *) out; - - *bufp++ = htonl(msg->type); - - switch (msg->type) { - case HEX_MSG_START: - *bufp++ = htonl(msg->data.start.player); - *bufp++ = htonl(msg->data.start.board_size); - *bufp++ = htonl(msg->data.start.game_secs); - *bufp++ = htonl(msg->data.start.thread_limit); - *bufp++ = htonl(msg->data.start.mem_limit_mib); - break; - - case HEX_MSG_MOVE: - *bufp++ = htonl(msg->data.move.board_x); - *bufp++ = htonl(msg->data.move.board_y); - break; - - case HEX_MSG_SWAP: - break; - - case HEX_MSG_END: - *bufp++ = htonl(msg->data.end.winner); - break; - } - - /* zero out remaining all message bytes */ - u8 *remaining = (u8 *) bufp; - assert(remaining < out + HEX_MSG_SZ); - memset(remaining, 0, (out + HEX_MSG_SZ) - remaining); - - return true; -} - -inline b32 -#ifdef __cplusplus -hex_msg_try_deserialise(u8 (&buf)[HEX_MSG_SZ], struct hex_msg *out) -#else -hex_msg_try_deserialise(u8 buf[static HEX_MSG_SZ], struct hex_msg *out) -#endif -{ - assert(buf); - assert(out); - - u32 *bufp = (u32 *) buf; - - struct hex_msg msg; - msg.type = ntohl(*bufp++); - - switch (msg.type) { - case HEX_MSG_START: - msg.data.start.player = ntohl(*bufp++); - msg.data.start.board_size = ntohl(*bufp++); - msg.data.start.game_secs = ntohl(*bufp++); - msg.data.start.thread_limit = ntohl(*bufp++); - msg.data.start.mem_limit_mib = ntohl(*bufp++); - break; - - case HEX_MSG_MOVE: - msg.data.move.board_x = ntohl(*bufp++); - msg.data.move.board_y = ntohl(*bufp++); - break; - - case HEX_MSG_SWAP: - break; - - case HEX_MSG_END: - msg.data.end.winner = ntohl(*bufp++); - break; - - default: - return false; - } - - *out = msg; - - return true; -} - -#ifdef __cplusplus -}; -#endif /* __cplusplus */ - -#endif /* HEX_PROTO_H */ diff --git a/server/include/hex/types.h b/server/include/hex/types.h @@ -1,64 +0,0 @@ -#ifndef HEX_TYPES_H -#define HEX_TYPES_H - -#ifdef __cplusplus - #include <cinttypes> - #include <cstdbool> - #include <cstddef> - #include <cstdint> -#else - #include <inttypes.h> - #include <stdbool.h> - #include <stddef.h> - #include <stdint.h> -#endif /* __cplusplus */ - -typedef int32_t b32; - -typedef unsigned char c8; - -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; - -typedef int8_t s8; -typedef int16_t s16; -typedef int32_t s32; -typedef int64_t s64; - -typedef float f32; -typedef double f64; - -#define ARRLEN(arr) (sizeof (arr) / sizeof (arr)[0]) - -#define MIN(a, b) ((a) > (b) ? (a) : (b)) -#define MAX(a, b) ((a) < (b) ? (b) : (a)) - -#define RELPTR_NULL (0) - -#define _RELPTR_MASK(ty_relptr) ((ty_relptr)1 << ((sizeof(ty_relptr) * 8) - 1)) -#define _RELPTR_ENC(ty_relptr, ptroff) \ - ((ty_relptr)((ptroff) ^ _RELPTR_MASK(ty_relptr))) -#define _RELPTR_DEC(ty_relptr, relptr) \ - ((ty_relptr)((relptr) ^ _RELPTR_MASK(ty_relptr))) - -#define RELPTR_ABS2REL(ty_relptr, base, absptr) \ - ((absptr) \ - ? _RELPTR_ENC(ty_relptr, (u8 *) absptr - (u8 *) base) \ - : RELPTR_NULL) - -#define RELPTR_REL2ABS(ty_absptr, ty_relptr, base, relptr) \ - ((relptr) \ - ? ((ty_absptr)((u8 *) base + _RELPTR_DEC(ty_relptr, relptr))) \ - : NULL) - -#define NANOSECS (1000000000ULL) - -#define TIMESPEC_TO_NANOS(sec, nsec) (((u64) (sec) * NANOSECS) + (nsec)) - -#define KiB (1024ULL) -#define MiB (1024ULL * KiB) -#define GiB (1024ULL * MiB) - -#endif /* HEX_TYPES_H */ diff --git a/server/include/hex_server.h b/server/include/hex_server.h @@ -0,0 +1,280 @@ +#ifndef HEX_SERVER_H +#define HEX_SERVER_H + +#define _XOPEN_SOURCE 700 +#define _GNU_SOURCE +#define _DEFAULT_SOURCE + +#include <assert.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <arpa/inet.h> +#include <fcntl.h> +#include <netdb.h> +#include <poll.h> +#include <signal.h> +#include <sys/resource.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> + +/* hex network protocol definitions */ +#include "hex.h" + +typedef int32_t b32; + +typedef unsigned char c8; + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +typedef float f32; +typedef double f64; + +#define ARRLEN(arr) (sizeof (arr) / sizeof (arr)[0]) + +#define MIN(a, b) ((a) > (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) + +#define RELPTR_NULL (0) + +#define _RELPTR_MASK(ty_relptr) ((ty_relptr)1 << ((sizeof(ty_relptr) * 8) - 1)) +#define _RELPTR_ENC(ty_relptr, ptroff) \ + ((ty_relptr)((ptroff) ^ _RELPTR_MASK(ty_relptr))) +#define _RELPTR_DEC(ty_relptr, relptr) \ + ((ty_relptr)((relptr) ^ _RELPTR_MASK(ty_relptr))) + +#define RELPTR_ABS2REL(ty_relptr, base, absptr) \ + ((absptr) \ + ? _RELPTR_ENC(ty_relptr, (u8 *) absptr - (u8 *) base) \ + : RELPTR_NULL) + +#define RELPTR_REL2ABS(ty_absptr, ty_relptr, base, relptr) \ + ((relptr) \ + ? ((ty_absptr)((u8 *) base + _RELPTR_DEC(ty_relptr, relptr))) \ + : NULL) + +#define NANOSECS (1000000000ULL) + +#define TIMESPEC_TO_NANOS(sec, nsec) (((u64) (sec) * NANOSECS) + (nsec)) + +#define KiB (1024) +#define MiB (1024 * KiB) +#define GiB (1024 * MiB) + +inline void +difftimespec(struct timespec *restrict lhs, struct timespec *restrict rhs, struct timespec *restrict out) +{ + if (lhs->tv_sec <= rhs->tv_sec && lhs->tv_nsec < rhs->tv_nsec) { + out->tv_sec = 0; + out->tv_nsec = 0; + } else { + out->tv_sec = lhs->tv_sec - rhs->tv_sec - (lhs->tv_nsec < rhs->tv_nsec); + out->tv_nsec = lhs->tv_nsec - rhs->tv_nsec + (lhs->tv_nsec < rhs->tv_nsec) * NANOSECS; + } +} + +/* hex server definitions */ + +/* timeout for accept()-ing an agent connection before assuming a forfeit + */ +#define HEX_AGENT_ACCEPT_TIMEOUT_MS (1 * 1000) + +#define HEX_AGENT_LOGFILE_TEMPLATE "/tmp/hex-agent.XXXXXX" +#define HEX_AGENT_LOGFILE_MODE (0666) + +struct opts { + char *agent_1; + uid_t agent_1_uid; + char *agent_2; + uid_t agent_2_uid; + u32 board_size; + u32 game_secs; + u32 agent_threads; + u32 agent_mem_mib; + b32 verbose; +}; + +extern struct opts opts; + +inline void +errlog(char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); +} + +inline void +dbglog(char *fmt, ...) +{ + va_list va; + + if (opts.verbose) + return; + + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); +} + +enum hex_error { + HEX_ERROR_OK, + HEX_ERROR_GAME_OVER, + HEX_ERROR_TIMEOUT, + HEX_ERROR_BAD_MOVE, + HEX_ERROR_BAD_MSG, + HEX_ERROR_DISCONNECT, + HEX_ERROR_SERVER, +}; + +inline char const * +hexerrorstr(enum hex_error val) +{ + switch (val) { + case HEX_ERROR_OK: return "OK"; + case HEX_ERROR_GAME_OVER: return "GAME_OVER"; + case HEX_ERROR_TIMEOUT: return "TIMEOUT"; + case HEX_ERROR_BAD_MOVE: return "BAD_MOVE"; + case HEX_ERROR_BAD_MSG: return "BAD_MSG"; + case HEX_ERROR_DISCONNECT: return "DISCONNECT"; + case HEX_ERROR_SERVER: return "SERVER"; + default: return "UNKNOWN"; + } +} + +struct statistics { + char *agent_1; + b32 agent_1_won; + u32 agent_1_rounds; + f32 agent_1_secs; + enum hex_error agent_1_err; + char *agent_2; + b32 agent_2_won; + u32 agent_2_rounds; + f32 agent_2_secs; + enum hex_error agent_2_err; +}; + +struct agent_state { + /* which player are we, and what agent do we run */ + enum hex_player player; + char *agent; + uid_t agent_uid; + char logfile[PATH_MAX]; + + /* how much time this agent has left to execute before it times out */ + struct timespec timer; + + /* socket for communicating with agent */ + int sockfd; + struct sockaddr_storage sock_addr; + socklen_t sock_addrlen; +}; + +enum cell_state { + CELL_EMPTY, + CELL_BLACK, + CELL_WHITE, +}; + +struct board_segment { + s16 parent_relptr; /* pointer to root of rooted tree */ + u8 rank; /* disambiguation between identical segments */ + u8 cell; /* the owner of the current cell */ +}; + +static inline s16 +board_segment_abs2rel(struct board_segment *base, struct board_segment *absptr) { + return RELPTR_ABS2REL(s16, base, absptr); +} + +static inline struct board_segment * +board_segment_rel2abs(struct board_segment *base, s16 relptr) { + return RELPTR_REL2ABS(struct board_segment *, s16, base, relptr); +} + +extern struct board_segment * +board_segment_root(struct board_segment *self); + +extern void +board_segment_merge(struct board_segment *restrict self, struct board_segment *restrict elem); + +extern b32 +board_segment_joined(struct board_segment *self, struct board_segment *elem); + +struct board_state { + u32 size; + + /* track connections between board "segments" (groups of cells owned + * by one player), and the edges for each player + */ + struct board_segment black_source, black_sink, white_source, white_sink; + struct board_segment segments[]; +}; + +extern struct board_state * +board_alloc(size_t size); + +extern void +board_free(struct board_state *self); + +extern void +board_print(struct board_state *self); + +extern b32 +board_play(struct board_state *self, enum hex_player player, s32 x, s32 y); + +extern void +board_swap(struct board_state *self); + +extern b32 +board_completed(struct board_state *self, enum hex_player *winner); + +struct server_state { + struct agent_state black_agent, white_agent; + struct board_state *board; + + /* socket for accepting agent connections */ + int servfd; + struct sockaddr_storage serv_addr; + socklen_t serv_addrlen; + char serv_host[NI_MAXHOST], serv_port[NI_MAXSERV]; +}; + +extern bool +server_init(struct server_state *state); + +extern void +server_free(struct server_state *state); + +extern bool +server_spawn_agent(struct server_state *state, struct agent_state *agent_state); + +extern void +server_wait_all_agents(struct server_state *state); + +extern void +server_run(struct server_state *state, struct statistics *statistics); + +#endif /* HEX_SERVER_H */ diff --git a/server/src/board.c b/server/src/board.c @@ -1,4 +1,4 @@ -#include "hex.h" +#include "hex_server.h" extern inline s16 board_segment_abs2rel(struct board_segment *base, struct board_segment *absptr); diff --git a/server/src/hex.c b/server/src/hex.c @@ -1,203 +0,0 @@ -#include "hex.h" - -struct args args = { - .agent_1 = NULL, - .agent_1_uid = 0, - .agent_2 = NULL, - .agent_2_uid = 0, - .board_dimensions = 11, - .game_secs = 300, - .thread_limit = 4, - .mem_limit_mib = 1024, - .verbose = false, -}; - -static void -parse_args(s32 argc, char **argv); - -static void -usage(char **argv) -{ - fprintf(stderr, "Usage: %s -a <agent-1> -ua <uid> -b <agent-2> -ub <uid> [-d 11] [-s 300] [-t 4] [-m 1024] [-v] [-h]\n", argv[0]); - fprintf(stderr, "\t-a: The command to execute for the first agent (black)\n"); - fprintf(stderr, "\t-ua: The user id to set for the first agent (black)\n"); - fprintf(stderr, "\t-b: The command to execute for the second agent (white)\n"); - fprintf(stderr, "\t-ub: The user id to set for the second agent (white)\n"); - fprintf(stderr, "\t-d: The dimensions for the game board (default: 11)\n"); - fprintf(stderr, "\t-s: The per-agent game timer, in seconds (default: 300 seconds)\n"); - fprintf(stderr, "\t-t: The per-agent thread hard-limit (default: 4 threads)\n"); - fprintf(stderr, "\t-m: The per-agent memory hard-limit, in MiB (default: 1024 MiB)\n"); - fprintf(stderr, "\t-v: Enables verbose logging on the server\n"); - fprintf(stderr, "\t-h: Prints this help information\n"); -} - -s32 -main(s32 argc, char **argv) -{ - parse_args(argc, argv); - - if (!args.agent_1 || !args.agent_2) { - errlog("Must provide execution targets for both agent-1 and agent-2\n"); - usage(argv); - exit(EXIT_FAILURE); - } - - if (!args.agent_1_uid || !args.agent_2_uid) { - errlog("Must provide (non-root) user ids for both agent-1 and agent-2\n"); - usage(argv); - exit(EXIT_FAILURE); - } - - struct board_state *board = board_alloc(args.board_dimensions); - if (!board) { - errlog("Failed to allocate board of size %" PRIu32 "\n", args.board_dimensions); - exit(EXIT_FAILURE); - } - - struct server_state state = { - .black_agent = { - .player = HEX_PLAYER_BLACK, - .agent = args.agent_1, - .agent_uid = args.agent_1_uid, - .logfile = HEX_AGENT_LOGFILE_TEMPLATE, - .timer = { .tv_sec = args.game_secs, .tv_nsec = 0, }, - .sock_addrlen = sizeof(struct sockaddr_storage), - }, - .white_agent = { - .player = HEX_PLAYER_WHITE, - .agent = args.agent_2, - .agent_uid = args.agent_2_uid, - .logfile = HEX_AGENT_LOGFILE_TEMPLATE, - .timer = { .tv_sec = args.game_secs, .tv_nsec = 0, }, - .sock_addrlen = sizeof(struct sockaddr_storage), - }, - .board = board, - }; - - if (!server_init(&state)) { - errlog("Failed to initialise server state\n"); - exit(EXIT_FAILURE); - } - - if (!server_spawn_agent(&state, &state.black_agent)) { - errlog("Failed to spawn black user agent: %s\n", state.black_agent.agent); - exit(EXIT_FAILURE); - } - - if (!server_spawn_agent(&state, &state.white_agent)) { - errlog("Failed to spawn white user agent: %s\n", state.white_agent.agent); - exit(EXIT_FAILURE); - } - - struct statistics stats; - server_run(&state, &stats); - - server_wait_all_agents(&state); - - server_free(&state); - - board_free(board); - - fprintf(stdout, "agent_1,agent_1_won,agent_1_rounds,agent_1_secs,agent_1_err,agent_1_logfile,agent_2,agent_2_won,agent_2_rounds,agent_2_secs,agent_2_err,agent_2_logfile,\n"); - fprintf(stdout, - "%s,%i,%u,%f,%s,%s,%s,%i,%u,%f,%s,%s,\n", - stats.agent_1, stats.agent_1_won, stats.agent_1_rounds, stats.agent_1_secs, hexerrorstr(stats.agent_1_err), state.black_agent.logfile, - stats.agent_2, stats.agent_2_won, stats.agent_2_rounds, stats.agent_2_secs, hexerrorstr(stats.agent_2_err), state.white_agent.logfile); - - return 0; -} - -static u32 -try_parse_u32(char *src, s32 base, u32 *out) -{ - char *endptr = NULL; - u32 result = strtoul(src, &endptr, base); - if (*endptr || errno) - return false; - - *out = result; - - return true; -} - -static void -parse_args(s32 argc, char **argv) -{ - for (s32 i = 1; i < argc; i++) { - char *arg = argv[i]; - - if (arg[0] != '-') continue; - - switch (arg[1]) { - case 'a': - args.agent_1 = argv[++i]; - break; - - case 'b': - args.agent_2 = argv[++i]; - break; - - case 'u': - if (arg[2] == 'a' && !try_parse_u32(argv[++i], 10, &args.agent_1_uid)) { - errlog("-ua takes a positive, unsigned integer argument, was given: '%s'\n", - argv[i]); - exit(EXIT_FAILURE); - } else if (arg[2] == 'b' && !try_parse_u32(argv[++i], 10, &args.agent_2_uid)) { - errlog("-ub takes a positive, unsigned integer argument, was given: '%s'\n", - argv[i]); - exit(EXIT_FAILURE); - } else if (arg[2] != 'a' && arg[2] != 'b') { - goto unknown_arg; - } - break; - - case 'd': { - if (!try_parse_u32(argv[++i], 10, &args.board_dimensions)) { - errlog("-d takes a positive, unsigned integer argument, was given: '%s'\n", - argv[i]); - exit(EXIT_FAILURE); - } - } break; - - case 's': { - if (!try_parse_u32(argv[++i], 10, &args.game_secs)) { - errlog("-s takes a positive, unsigned integer argument, was given: '%s'\n", - argv[i]); - exit(EXIT_FAILURE); - } - } break; - - case 't': { - if (!try_parse_u32(argv[++i], 10, &args.thread_limit)) { - errlog("-t takes a positive, unsigned integer argument, was given: '%s'\n", - argv[i]); - exit(EXIT_FAILURE); - } - } break; - - case 'm': { - if (!try_parse_u32(argv[++i], 10, &args.mem_limit_mib)) { - errlog("-m takes a positive, unsigned integer argument, was given: '%s'\n", - argv[i]); - exit(EXIT_FAILURE); - } - } break; - - case 'v': - args.verbose = true; - break; - - case 'h': - usage(argv); - exit(EXIT_SUCCESS); - break; - - default: { -unknown_arg: - errlog("[server] Unknown argument: %s\n", &arg[1]); - usage(argv); - exit(EXIT_FAILURE); - } break; - } - } -} diff --git a/server/src/main.c b/server/src/main.c @@ -0,0 +1,160 @@ +#include "hex_server.h" + +struct opts opts = { + .board_size = 11, + .game_secs = 300, + .agent_threads = 4, + .agent_mem_mib= 1024, +}; + +static char const *optstr = "hva:A:b:B:d:s:t:m:"; + +static void +usage(int argc, char **argv) +{ + (void) argc; + + fprintf(stderr, "Usage: %s [-hv] -a <agent1> -A <agent1-uid> -b <agent2> -B <agent2-uid>\n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "\t-h :\n\t\tdisplays this help information\n"); + fprintf(stderr, "\t-v :\n\t\tenabled verbose logging\n"); + fprintf(stderr, "\t-a :\n\t\tthe command for the first agent (required)\n"); + fprintf(stderr, "\t-A :\n\t\tthe uid for the first agent (required)\n"); + fprintf(stderr, "\t-b :\n\t\tthe command for the second agent (required)\n"); + fprintf(stderr, "\t-B :\n\t\tthe uid for the second agent (required)\n"); + fprintf(stderr, "\t-d :\n\t\tthe dimensions for the game board (default: 11)\n"); + fprintf(stderr, "\t-s :\n\t\tthe game timer, in seconds (default: 300 secs)\n"); + fprintf(stderr, "\t-t :\n\t\tthe per-agent hard thread limit (default: 4 threads)\n"); + fprintf(stderr, "\t-m :\n\t\tthe per-agent hard memory limit, in MiB (default: 1024 MiB)\n"); + fprintf(stderr, "\n"); +} + +static bool +parse_args(int argc, char **argv) +{ + int opt; + while ((opt = getopt(argc, argv, optstr)) != -1) { + switch (opt) { + case 'v': + opts.verbose = true; + break; + + case 'a': + opts.agent_1 = optarg; + break; + + case 'A': + if (!(opts.agent_1_uid = strtoul(optarg, NULL, 0))) + return false; + break; + + case 'b': + opts.agent_2 = optarg; + break; + + case 'B': + if (!(opts.agent_2_uid = strtoul(optarg, NULL, 0))) + return false; + break; + + case 'd': + if (!(opts.board_size = strtoull(optarg, NULL, 0))) + return false; + break; + + case 's': + if (!(opts.game_secs = strtoull(optarg, NULL, 0))) + return false; + break; + + case 't': + if (!(opts.agent_threads = strtoull(optarg, NULL, 0))) + return false; + break; + + case 'm': + if (!(opts.agent_mem_mib = strtoull(optarg, NULL, 0))) + return false; + break; + + default: + return false; + } + } + + if (!opts.agent_1 || !opts.agent_1_uid || !opts.agent_2 || !opts.agent_2_uid) + return false; + + return true; +} + +int +main(int argc, char **argv) +{ + if (!parse_args(argc, argv)) { + usage(argc, argv); + exit(EXIT_FAILURE); + } + + struct board_state *board = board_alloc(opts.board_size); + if (!board) { + errlog("Failed to allocate board of size %" PRIu32 "\n", opts.board_size); + exit(EXIT_FAILURE); + } + + struct server_state state = { + .black_agent = { + .player = HEX_PLAYER_BLACK, + .agent = opts.agent_1, + .agent_uid = opts.agent_1_uid, + .logfile = HEX_AGENT_LOGFILE_TEMPLATE, + .timer = { .tv_sec = opts.game_secs, .tv_nsec = 0, }, + .sock_addrlen = sizeof(struct sockaddr_storage), + }, + .white_agent = { + .player = HEX_PLAYER_WHITE, + .agent = opts.agent_2, + .agent_uid = opts.agent_2_uid, + .logfile = HEX_AGENT_LOGFILE_TEMPLATE, + .timer = { .tv_sec = opts.game_secs, .tv_nsec = 0, }, + .sock_addrlen = sizeof(struct sockaddr_storage), + }, + .board = board, + }; + + if (!server_init(&state)) { + errlog("Failed to initialise server state\n"); + exit(EXIT_FAILURE); + } + + if (!server_spawn_agent(&state, &state.black_agent)) { + errlog("Failed to spawn black user agent: %s\n", state.black_agent.agent); + exit(EXIT_FAILURE); + } + + if (!server_spawn_agent(&state, &state.white_agent)) { + errlog("Failed to spawn white user agent: %s\n", state.white_agent.agent); + exit(EXIT_FAILURE); + } + + struct statistics stats; + server_run(&state, &stats); + + server_wait_all_agents(&state); + + server_free(&state); + + board_free(board); + + fprintf(stdout, "agent_1,agent_1_won,agent_1_rounds,agent_1_secs,agent_1_err,agent_1_logfile,agent_2,agent_2_won,agent_2_rounds,agent_2_secs,agent_2_err,agent_2_logfile,\n"); + fprintf(stdout, + "%s,%i,%u,%f,%s,%s,%s,%i,%u,%f,%s,%s,\n", + stats.agent_1, stats.agent_1_won, stats.agent_1_rounds, stats.agent_1_secs, hexerrorstr(stats.agent_1_err), state.black_agent.logfile, + stats.agent_2, stats.agent_2_won, stats.agent_2_rounds, stats.agent_2_secs, hexerrorstr(stats.agent_2_err), state.white_agent.logfile); + + exit(EXIT_SUCCESS); +} + +#include "server.c" +#include "board.c" +#include "utils.c" diff --git a/server/src/proto.c b/server/src/proto.c @@ -1,7 +0,0 @@ -#include "hex.h" - -extern inline b32 -hex_msg_try_serialise(struct hex_msg const *msg, u8 out[static HEX_MSG_SZ]); - -extern inline b32 -hex_msg_try_deserialise(u8 buf[static HEX_MSG_SZ], struct hex_msg *out); diff --git a/server/src/server.c b/server/src/server.c @@ -1,4 +1,4 @@ -#include "hex.h" +#include "hex_server.h" bool server_init(struct server_state *state) @@ -101,10 +101,10 @@ server_spawn_agent(struct server_state *state, struct agent_state *agent_state) struct rlimit limit; - limit.rlim_cur = limit.rlim_max = args.thread_limit; + limit.rlim_cur = limit.rlim_max = opts.agent_threads; prlimit(pid, RLIMIT_NPROC, &limit, NULL); - limit.rlim_cur = limit.rlim_max = args.mem_limit_mib * 1024 * 1024; + limit.rlim_cur = limit.rlim_max = opts.agent_mem_mib * 1024 * 1024; prlimit(pid, RLIMIT_DATA, &limit, NULL); char *args[] = { @@ -224,10 +224,10 @@ server_run(struct server_state *state, struct statistics *statistics) */ struct hex_msg msg; msg.type = HEX_MSG_START; - msg.data.start.board_size = args.board_dimensions; - msg.data.start.game_secs = args.game_secs; - msg.data.start.thread_limit = args.thread_limit; - msg.data.start.mem_limit_mib = args.mem_limit_mib; + msg.data.start.board_size = opts.board_size; + msg.data.start.game_secs = opts.game_secs; + msg.data.start.thread_limit = opts.agent_threads; + msg.data.start.mem_limit_mib = opts.agent_mem_mib; msg.data.start.player = HEX_PLAYER_BLACK; if ((err = send_msg(&state->black_agent, &msg, true))) goto forfeit_black; diff --git a/server/src/utils.c b/server/src/utils.c @@ -1,7 +1,7 @@ -#include "hex.h" +#include "hex_server.h" -extern inline char const * -hexerrorstr(enum hex_error val); +extern inline void +difftimespec(struct timespec *restrict lhs, struct timespec *restrict rhs, struct timespec *restrict out); extern inline void errlog(char *fmt, ...); @@ -9,8 +9,14 @@ errlog(char *fmt, ...); extern inline void dbglog(char *fmt, ...); -extern inline void -difftimespec(struct timespec *restrict lhs, struct timespec *restrict rhs, struct timespec *restrict out); +extern inline char const * +hexerrorstr(enum hex_error val); extern inline char const * hexplayerstr(enum hex_player val); + +extern inline bool +hex_msg_try_serialise(struct hex_msg const *msg, uint8_t out[static HEX_MSG_SZ]); + +extern inline bool +hex_msg_try_deserialise(uint8_t buf[static HEX_MSG_SZ], struct hex_msg *out); diff --git a/tournament-host.py b/tournament-host.py @@ -80,8 +80,8 @@ async def game(sem, args, agent_pair, uid_pool): proc = await asyncio.create_subprocess_exec( HEX_SERVER_PROGRAM, - '-a', agent1, '-ua', agent1_uid, - '-b', agent2, '-ub', agent2_uid, + '-a', agent1, '-A', agent1_uid, + '-b', agent2, '-B', agent2_uid, '-d', str(args.dimension), '-s', str(args.seconds), '-t', str(args.threads),