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:
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),