summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PROJECT.md86
-rw-r--r--STYLE_GUIDE.md123
-rw-r--r--src/engine/thread.c11
-rw-r--r--src/uci.c22
-rw-r--r--src/uci/response.c659
-rw-r--r--src/uci/state.c156
-rw-r--r--tests/generated.c94
7 files changed, 1072 insertions, 79 deletions
diff --git a/PROJECT.md b/PROJECT.md
index 5a3826a..e7b292d 100644
--- a/PROJECT.md
+++ b/PROJECT.md
@@ -23,7 +23,9 @@ gacrux/
│ ├── engine/ # Engine internals headers
│ │ ├── magic.h # Magic-bitboard attack tables
│ │ ├── moves.h # move_t, moves_t, move generation API
-│ │ ├── threads.h # Multi-threaded search thread structs & API
+│ │ ├── search.h # Alpha-beta search, qsearch, PV reconstruction
+│ │ ├── evaluate.h # Static evaluation (material + PST)
+│ │ ├── threads.h # Multi-threaded DTS search thread structs & API
│ │ └── ttable.h # Transposition table (Zobrist hashing, buckets)
│ ├── ipc/ # Inter-process / thread IPC headers
│ │ ├── state.h # ipc_state_t: shared atomic event state
@@ -57,8 +59,10 @@ gacrux/
│ │ └── moves.c # ipc_moves_t add/init/deinit
│ └── engine/
│ ├── moves.c # Legal move generation (pseudolegal + legality filter)
-│ ├── threads.c # pthread creation/cancel/cleanup for DTS search
-│ ├── thread.c # child_thread() & root_thread() entry points
+│ ├── search.c # Alpha-beta search, qsearch, PV reconstruction, tests
+│ ├── evaluate.c # Static evaluation (material + PST), tests
+│ ├── threads.c # pthread creation/cleanup, split-point pool, DTS helpers
+│ ├── thread.c # child_thread() & root_thread() entry points, tests
│ ├── ttable.c # Zobrist init, transposition table insert/find
│ └── moves/
│ ├── vec.c # Dynamic move list (moves_t) management
@@ -82,9 +86,13 @@ gacrux/
│ ├── extractor.py # Character-level C tokenizer (handles macros, depth tracking)
│ └── analysis.js # Output data consumed by an HTML visualization
├── resources/
+│ ├── bitboards.html # Bitboard visualization / reference
+│ ├── search.html # DTS paper by Hyatt et al. (Cray Blitz)
│ └── uci/
│ ├── uci_min.txt # Minimal UCI spec reference
-│ └── UCI-Protocol-Specification.txt # Full UCI protocol spec
+│ ├── UCI-Protocol-Specification.txt # Full UCI protocol spec
+│ ├── draft-1.pdf # UCI protocol draft
+│ └── draft-2.pdf # UCI protocol draft
├── output/ # Build artifacts (.o files)
└── opt/ # External / vendored projects (gitignored)
├── en-croissant/ # React + Tauri chess GUI
@@ -133,7 +141,9 @@ The engine uses a **multi-threaded, event-driven** architecture:
by `src/engine/threads.c`. Thread 0 is the `root_thread`; threads 1..N are
`child_thread`s. These implement a Distributed Tree Search (DTS) pattern where
the root thread owns the search tree and children collaborate on split points
- (`split_point_t`).
+ (`split_point_t`). The root does iterative deepening with alpha-beta search,
+ publishing info messages per depth. Children scan the split-point pool for
+ published work, claim moves, search them, and share results back.
- **IPC layer** (`src/ipc/`): Shared `ipc_state_t` containing an atomic `event`
field that acts as a synchronization primitive. Events flow between UCI and
@@ -146,6 +156,12 @@ The engine uses a **multi-threaded, event-driven** architecture:
pattern) to push search info (depth, score, principal variation) to the UCI
thread without locks.
+- **Cooperative stop**: Engine threads check an atomic `stop` flag at each
+ iteration (root) or worker loop cycle (children). `threads_stop_all()` sets
+ the flag on all threads; `threads_join()` waits for them to exit; `threads_cleanup()`
+ reinitializes state. The UCI thread (which blocks on stdin) is the only thread
+ that uses `pthread_cancel`.
+
### Position Representation
Uses **64-bit bitboards** (`bitboard_t`, one per piece type × color, 12 total)
@@ -173,6 +189,22 @@ stored in a `position_t` struct. Squares are indexed 0–63 (0 = a1, 7 = h1,
Insertion replaces the shallowest entry when a bucket is full (replacement
scheme).
+### Search
+
+- **Alpha-beta search** (`src/engine/search.c`): Negamax with alpha-beta pruning,
+ TT probing for move ordering and cutoffs, aspiration windows at depth > 4.
+- **Quiescence search** (`src/engine/search.c`): Stand-pat with delta pruning
+ (margin of 200 cp), searches captures and promotions only at depth 0.
+- **PV reconstruction** (`src/engine/search.c:reconstruct_pv`): Follows TT
+ best-move entries to rebuild the principal variation after each iteration.
+
+### Evaluation
+
+- **Material + PST** (`src/engine/evaluate.c`): Simple material counting
+ (pawn=100, knight/bishop=320, rook=500, queen=900) plus piece-square table
+ bonuses for pawn advancement and knight centralization. Returns a score from
+ the side-to-move's perspective.
+
### UCI Protocol
- Implements the standard UCI state machine (`include/uci/internals/response.h`):
@@ -185,8 +217,24 @@ stored in a `position_t` struct. Squares are indexed 0–63 (0 = a1, 7 = h1,
### Testing
- **Embedded tests**: Source files use `#ifdef TEST_MOD` to conditionally compile
- `test_*` functions directly into the source (e.g. `test_fen_*`,
- `test_ttable_*`, `test_perft_*`, `test_uci_cmd_t`).
+ `test_*` functions directly into the source. Test families:
+ - `test_fen_*` (3) — FEN parsing
+ - `test_perft_*` (3) — move generation correctness against known node counts
+ - `test_white_king_corners`, `test_black_king_corners` — king/castling moves
+ - `test_uci_cmd_t`, `test_invalid_cmd_uci_cmd_t` — UCI command parsing
+ - `test_ttable_*` (7) — transposition table insert/find/replacement
+ - `test_search_*` (4) — alpha-beta search, mate detection, depth limit, stop
+ - `test_qsearch_finds_capture` — quiescence search captures
+ - `test_pv_reconstruction` — TT-based PV rebuild
+ - `test_eval_*` (2) — evaluation balance and material scaling
+ - `test_root_thread_*` (4) — root iterative deepening, depth/node limits, mate
+ - `test_child_*` (3) — DTS worker claim/share/stop
+ - `test_root_split_*`, `test_root_with_workers_*` (2) — root split with workers
+ - `test_threads_*` (2) — thread args initialization
+ - `test_split_*` (4) — split-point pool, claim, merge, share
+ - `test_help_scan_*` — DTS HELP protocol
+ - `test_thrashing_counter_*` — thrashing guard
+ - `test_cooperative_stop_*` — cooperative shutdown
- **Test discovery** (`tests/find_tests.c`): Scours `src/` for `bool test_*()`
signatures, generates `tests/generated.c` with a function-pointer table and
name list.
@@ -219,14 +267,22 @@ just test
## Current Status
-The engine is a **work in progress**. The threaded search infrastructure (thread
-creation, IPC event signaling, engine message passing, transposition table,
-Zobrist hashing) and all move generation are implemented and tested. However, the
-search threads themselves (`child_thread`, `root_thread`) currently perform
-placeholder work (a busy-wait loop) and emit a hardcoded best move — the actual
-mini-max/alpha-beta search and evaluation function are not yet implemented. The
-UCI protocol layer is fully functional for position setup, options, and
-`isready`/`stop`/`quit`/`go` commands.
+The engine has a **working threaded search** with iterative deepening, alpha-beta
+pruning, quiescence search, and a Distributed Tree Search (DTS) multi-threading
+model. The root thread drives iterative deepening and publishes split points;
+child workers claim moves, search them, and share results back. The UCI protocol
+layer is functional for position setup, options, `isready`/`stop`/`quit`/`go`
+commands, and emits `info depth N ... pv` messages with reconstructed principal
+variations. The evaluation function is a simple material + PST baseline.
+
+**Implemented**: move generation (magic bitboards), alpha-beta search, quiescence
+search, PV reconstruction, transposition table, Zobrist hashing, DTS split points
+(with thrashing guard), cooperative stop, UCI protocol, and 44 tests across all
+modules.
+
+**Remaining**: end-to-end UCI integration testing (`./build uci run` with a GUI),
+deeper evaluation (tapered eval, king safety, passed pawns), time management,
+node-type aware split-point selection (DTS §3.x).
## Dependencies
diff --git a/STYLE_GUIDE.md b/STYLE_GUIDE.md
index 2581b5b..a02c90c 100644
--- a/STYLE_GUIDE.md
+++ b/STYLE_GUIDE.md
@@ -12,7 +12,7 @@ Multiple `.c` files are aggregated into a single translation unit by
`#include`-ing them, rather than compiling separately:
- `src/engine.c` pulls in `engine/threads.c`, `engine/thread.c`,
- `engine/moves.c`, `engine/ttable.c`.
+ `engine/moves.c`, `engine/ttable.c`, `engine/evaluate.c`, `engine/search.c`.
- `src/ipc.c` pulls in `ipc/go_args.c`, `ipc/moves.c`, `ipc/engine_message.c`,
`ipc/state.c`, `ipc/threads.c`.
- `src/main.c` and `tests/main.c` append `#include "fen.c"`,
@@ -35,7 +35,6 @@ consuming `.c` file `#define`s the sentinel before including the header:
Other gates in use:
- `MOVES_INTERNAL` — `include/engine/moves.h` (`__forloop_*` declarations).
-- `IPC_INTERNAL` — `include/ipc/state.h`.
### Test gating
@@ -139,6 +138,23 @@ Every "object" type follows an `init` / `deinit` pair.
## Macros
+### File-local constants
+
+File-scope constants that don't warrant a `#define` use anonymous enums:
+
+```c
+enum { ROOT_MAX_DEPTH = 100, MATE_WINDOW = 1000 };
+```
+
+Function-scope constants use the same pattern:
+
+```c
+for (int depth = 1; ...) {
+ enum { WORKERS = 4 };
+ ...
+}
+```
+
### Expression macros (no statement semantics)
Written without surrounding parentheses, relying on call-site precedence —
@@ -247,15 +263,60 @@ enum {
## Atomics
-Use `<stdatomic.h>` primitives directly:
+Use `<stdatomic.h>` primitives directly. The engine's threading model is
+**lock-free**: no `pthread_mutex` or `pthread_cond` is used anywhere. All
+inter-thread communication goes through atomics, with `sched_yield()` for
+back-off in spin loops.
+
+### Types in use
+
+- `atomic_int` — flags, counters, scores (`stop`, `finished`, `best_score`,
+ `workers`, `next_move_idx`, `nodes_searched`).
+- `atomic_bool` — boolean stop flags in `search_ctx_t` and local variables
+ in `child_thread`/`root_thread`.
+- `atomic_flag` — spinlock for the global `engine_message_handler_t` lock.
+- `atomic_uint_fast64_t` — transposition table keys.
+
+### Operations in use
+
+- `atomic_init(&x, v)` — before `pthread_create` (main thread).
+- `atomic_store(&x, v)` — at thread entry or to signal state changes.
+- `atomic_load(&x)` — to poll for state changes (stop signals, split-point
+ state).
+- `atomic_fetch_add(&x, 1)` / `atomic_fetch_sub(&x, 1)` — lock-free counters
+ (worker registration in `split_point_t.workers`, move claiming via
+ `next_move_idx`).
+- `atomic_compare_exchange_weak(&x, &expected, desired)` — lock-free pool
+ allocation (`__pool_alloc`) and best-score sharing (`split_point_share`).
+- `atomic_flag_test_and_set(&flag)` / `atomic_flag_clear(&flag)` — spinlock
+ acquire/release for engine message handlers.
+
+### Spin-wait pattern
+
+Threads poll with `sched_yield()` in tight loops:
+
+```c
+while (atomic_load(&sp->state) == SPLITPOINT_STATE_ACTIVE) {
+ sched_yield();
+}
+```
+
+### Two-phase initialization
+
+Atomics used across threads are initialized in two phases:
+
+1. `atomic_init(&x, v)` on the main thread before `pthread_create`.
+2. `atomic_store(&x, v)` at thread entry to ensure visibility.
-- `atomic_int`, `atomic_uint_fast64_t`, `atomic_flag`.
-- `atomic_init(&x, v)`, `atomic_load(&x)`, `atomic_store(&x, v)`.
-- `atomic_flag_test_and_set(&flag)` / `atomic_flag_clear(&flag)` for spinlocks.
+### Note on `_Atomic bool` vs `atomic_bool`
+
+Both forms appear in the codebase: `thread.c` uses the `<stdatomic.h>` typedef
+`atomic_bool` while `search.c` tests use the C11 keyword `_Atomic bool`. They
+are semantically equivalent. Prefer `atomic_bool` (the typedef) for consistency.
## Test Functions
-Embedded test functions:
+### Signature and convention
- Signature: `bool test_<thing>(void)`.
- Return `true` = pass, `false` = fail.
@@ -280,6 +341,54 @@ bool test_uci_cmd_t() {
}
```
+### Test block structure
+
+Test blocks live at the bottom of source files under `#ifdef TEST_MOD` and may
+need additional includes beyond the file's normal headers:
+
+```c
+#ifdef TEST_MOD
+#include <stdbool.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include "fen.h"
+
+// Cross-module .c inclusion for IPC types needed by tests
+#include "../src/ipc/go_args.c"
+#include "../src/ipc/moves.c"
+#include "../src/ipc/engine_message.c"
+
+bool test_example() { ... }
+#endif
+```
+
+### Anti-double-inclusion guard
+
+When a leaf `.c` file is compiled standalone *and* included from an aggregated
+TU, use `#ifndef TEST_MOD` to prevent duplicate symbols:
+
+```c
+#ifndef TEST_MOD
+#include "moves/king.c"
+#endif
+```
+
+### Test helper naming
+
+- `__double_prefix` for test helper structs: `__child_test_env_t`,
+ `__root_split_env_t`, `__claim_thread_args_t`.
+- `__double_prefix` for test helper functions: `__root_thread_run`,
+ `__child_test_setup`, `__wait_split_point_done`.
+- Local-scope constants via anonymous enum: `enum { WORKERS = 4 };`.
+
+### Thread-safety for debug counters
+
+Per-thread debug counters use `_Thread_local` storage to avoid races:
+
+```c
+static _Thread_local int spins = 0;
+```
+
## Build system (`build.c`)
- A self-rebuilding `nob.h`-style driver.
diff --git a/src/engine/thread.c b/src/engine/thread.c
index 1bb9a83..bbf2605 100644
--- a/src/engine/thread.c
+++ b/src/engine/thread.c
@@ -70,13 +70,10 @@ void *child_thread(void *_args) {
// nothing active right now: idle until the root publishes the next
// iteration's block or the search is stopped
if (sp == NULL) {
- static _Thread_local int spins = 0;
- if (++spins == 5000000) fprintf(stderr, "CHILD %d STUCK stop=%d (ptr=%p)\n", args->id, atomic_load(&args->stop), (void*)&args->stop);
sched_yield();
continue;
}
- fprintf(stderr,"CHILD %d found sp\n",args->id);
// register as a worker at this block so Merge knows the headcount
atomic_fetch_add(&sp->workers, 1);
@@ -174,7 +171,6 @@ void *root_thread(void *_args) {
? __pool_alloc(args->split_points, args->sp_pool_capacity)
: NULL;
- fprintf(stderr,"ROOT iter %d sp=%p\n",depth,(void*)sp);
if (sp != NULL) {
// Split (§2.4): snapshot the root node and hand the whole move list to
// the pool; the root works the list alongside its workers.
@@ -221,16 +217,9 @@ void *root_thread(void *_args) {
if (split_point_merge(sp)) {
atomic_store(&sp->state, SPLITPOINT_STATE_FREE);
}
- fprintf(stderr,"ROOT iter %d waiting w=%d\n",depth,atomic_load(&sp->workers));
- int spins=0;
while (atomic_load(&sp->state) == SPLITPOINT_STATE_ACTIVE) {
sched_yield();
- if (++spins==1000000) fprintf(stderr,"ROOT STUCK iter %d w=%d idx=%d\n",depth,atomic_load(&sp->workers),atomic_load(&sp->next_move_idx));
}
- while (atomic_load(&sp->state) == SPLITPOINT_STATE_ACTIVE) {
- sched_yield();
- }
- fprintf(stderr,"ROOT iter %d done\n",depth);
result.score = atomic_load(&sp->best_score);
result.best_move = sp->best_move;
diff --git a/src/uci.c b/src/uci.c
index 903528d..4ae56c6 100644
--- a/src/uci.c
+++ b/src/uci.c
@@ -11,27 +11,7 @@
#include "ipc/state.h"
#include "ipc/uci.h"
-void load_from_message(uci_cmd_t* cmd, const char* message) {
- char token[MAX_TOKEN_SIZE];
- int i = 0, k = 0;
-
- while (message[i] != 0 && message[i] != '\n' && message[i] != '\r') {
- if (message[i] != ' ') {
- token[k++] = message[i++];
- continue;
- }
- if (k == 0) {
- i++;
- continue;
- }
- i++;
- token[k] = 0;
- uci_cmd_add(cmd, token);
- k = 0;
- }
- token[k] = 0;
- uci_cmd_add(cmd, token);
-}
+void load_from_message(uci_cmd_t* cmd, const char* message);
// split a raw stdin chunk into lines and parse each into its own command.
void load_from_buffer(
diff --git a/src/uci/response.c b/src/uci/response.c
index 81459d6..f3fc2d9 100644
--- a/src/uci/response.c
+++ b/src/uci/response.c
@@ -3,6 +3,33 @@
#include <stdlib.h>
#include "uci/internals/response.h"
+
+// Tokenize a UCI message into a command. Supports double-quoted strings so
+// that multi-word values (e.g. FEN positions) survive tokenization.
+void load_from_message(uci_cmd_t* cmd, const char* message) {
+ char token[MAX_TOKEN_SIZE];
+ int i = 0, k = 0;
+ bool quoted = false;
+
+ while (message[i] != 0 && message[i] != '\n' && message[i] != '\r') {
+ if (message[i] == '"') {
+ quoted = !quoted;
+ i++;
+ continue;
+ }
+ if (message[i] == ' ' && !quoted) {
+ if (k == 0) { i++; continue; }
+ i++;
+ token[k] = 0;
+ uci_cmd_add(cmd, token);
+ k = 0;
+ continue;
+ }
+ token[k++] = message[i++];
+ }
+ token[k] = 0;
+ uci_cmd_add(cmd, token);
+}
#include "uci/state.h"
#include "fen.h"
@@ -50,23 +77,30 @@ void handle_idle(uci_state_t *state, uci_cmd_t cmd) {
char buffer[2048] = {0};
char name[64] = {0};
char *cursor = name;
- for (int i = 1; i < cmd.args_count; i++) {
- assert((long)cursor - (long)buffer < 2048);
+ int limit = 64;
+ int i = 1;
+ if (i < cmd.args_count && strcmp(uci_cmd_get_arg(cmd, i), "name") == 0) i++;
+ for (; i < cmd.args_count; i++) {
+ assert((long)cursor - (long)(limit == 64 ? name : buffer) < limit);
char *arg = uci_cmd_get_arg(cmd, i);
if (strcmp(arg, "value") == 0) {
- *(--cursor) = 0;
+ if (cursor > name && *(cursor - 1) == ' ') cursor--;
+ *cursor = 0;
cursor = buffer;
+ limit = 2048;
continue;
}
- cursor = stpncpy(cursor, arg, MAX_TOKEN_SIZE);
+ while (*arg) *(cursor++) = *(arg++);
*(cursor++) = ' ';
}
- *(--cursor) = 0;
+ if (cursor > name && *(cursor - 1) == ' ') cursor--;
+ if (cursor > buffer && *(cursor - 1) == ' ') cursor--;
+ *cursor = 0;
apply_option(state, name, buffer);
} else if (strcmp(cmd.root, "ucinewgame") == 0) {
} else if (strcmp(cmd.root, "position") == 0) {
int k = 0;
- char* mode = uci_cmd_get_arg(cmd, 0);
+ char* mode = uci_cmd_get_arg(cmd, k++);
if (strcmp(mode, "startpos") == 0) {
struct fen_load fen = load_fen(
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
@@ -83,13 +117,8 @@ void handle_idle(uci_state_t *state, uci_cmd_t cmd) {
state->position = fen.position;
} else { assert(0 && "Invalid argument for position command"); }
- if (cmd.args_count >= k) return;
+ if (cmd.args_count <= k) return;
assert(strcmp(uci_cmd_get_arg(cmd, k++), "moves") == 0);
- assert(state->moves->count <= cmd.args_count - k);
- int old_move_count = state->moves->count;
- state->moves->count = cmd.args_count - k;
-
- k += old_move_count;
for (; k < cmd.args_count; k++) {
char* move = uci_cmd_get_arg(cmd, k);
add_ipc_move(
@@ -102,15 +131,6 @@ void handle_idle(uci_state_t *state, uci_cmd_t cmd) {
}
} else if (strcmp(cmd.root, "stop") == 0) {
} else if (strcmp(cmd.root, "quit") == 0) {
- free(cmd.args);
-
- int settings_count = sizeof(state->option_settings)/sizeof(option_setting_t);
- for (int i = 0; i < settings_count; i++) {
- option_setting_t setting = state->option_settings[i];
- if (OPTION_STRING != setting.type) continue;
- free(setting.value.string->data);
- }
-
state->signal = UCI_SIGNAL_QUIT;
} else if (strcmp(cmd.root, "isready") == 0) {
state->current_state = UCI_STATE_SYNC;
@@ -210,3 +230,600 @@ void handle_halt(uci_state_t *state, uci_cmd_t cmd) {
// ---
state->current_state = UCI_STATE_IDLE;
}
+
+#ifdef TEST_MOD
+#include <stdbool.h>
+#include <unistd.h>
+
+static uci_cmd_t __make_cmd(const char *line) {
+ uci_cmd_t cmd;
+ uci_cmd_init(&cmd);
+ load_from_message(&cmd, line);
+ return cmd;
+}
+
+static void __free_cmd(uci_cmd_t cmd) { uci_cmd_deinit(cmd); }
+
+static char *__capture_uci(
+ void (*handler)(uci_state_t *, uci_cmd_t),
+ uci_state_t *state,
+ const char *line,
+ char *out,
+ int out_size
+) {
+ int pipefd[2];
+ if (pipe(pipefd) != 0) { out[0] = 0; return out; }
+ int saved = dup(STDOUT_FILENO);
+ dup2(pipefd[1], STDOUT_FILENO);
+ close(pipefd[1]);
+
+ uci_cmd_t cmd = __make_cmd(line);
+ handler(state, cmd);
+ __free_cmd(cmd);
+
+ fflush(stdout);
+ dup2(saved, STDOUT_FILENO);
+ close(saved);
+
+ int n = read(pipefd[0], out, out_size - 1);
+ close(pipefd[0]);
+ out[n > 0 ? n : 0] = 0;
+ return out;
+}
+
+// ---- UCI handshake ----
+
+bool test_uci_handshake_id_and_uciok() {
+ uci_state_t state;
+ uci_state_init(&state);
+
+ char out[2048];
+ __capture_uci(handle_initial, &state, "uci", out, sizeof(out));
+
+ bool ok = true;
+ ok &= strstr(out, "id name Gacrux") != NULL;
+ ok &= strstr(out, "id author") != NULL;
+ ok &= strstr(out, "uciok") != NULL;
+ ok &= state.current_state == UCI_STATE_IDLE;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_uci_handshake_prints_all_options() {
+ uci_state_t state;
+ uci_state_init(&state);
+
+ char out[4096];
+ __capture_uci(handle_initial, &state, "uci", out, sizeof(out));
+
+ bool ok = true;
+ ok &= strstr(out, "option name Threads type spin") != NULL;
+ ok &= strstr(out, "option name Hash type spin") != NULL;
+ ok &= strstr(out, "option name Clear Hash type button") != NULL;
+ ok &= strstr(out, "option name UCI_ShowCurrLine type check") != NULL;
+ ok &= strstr(out, "option name UCI_LimitStrength type check") != NULL;
+ ok &= strstr(out, "option name UCI_Elo type spin") != NULL;
+ ok &= strstr(out, "option name UCI_AnalyseMode type check") != NULL;
+ ok &= strstr(out, "option name UCI_Opponent type string") != NULL;
+ ok &= strstr(out, "option name UCI_EngineAbout type string") != NULL;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+// ---- isready ----
+
+bool test_isready_returns_readyok() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_sync, &state, "isready", out, sizeof(out));
+
+ bool ok = true;
+ ok &= strstr(out, "readyok") != NULL;
+ ok &= state.current_state == UCI_STATE_IDLE;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_isready_transitions_to_idle() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_SYNC;
+
+ char out[256];
+ __capture_uci(handle_uci, &state, "isready", out, sizeof(out));
+
+ bool ok = true;
+ ok &= strstr(out, "readyok") != NULL;
+ ok &= state.current_state == UCI_STATE_IDLE;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+// ---- debug ----
+
+bool test_debug_on_sets_flag() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state, "debug on", out, sizeof(out));
+
+ bool ok = state.debug == true;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_debug_off_clears_flag() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+ state.debug = true;
+
+ char out[256];
+ __capture_uci(handle_idle, &state, "debug off", out, sizeof(out));
+
+ bool ok = state.debug == false;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+// ---- setoption ----
+
+bool test_setoption_spin() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state,
+ "setoption name Threads value 4", out, sizeof(out));
+
+ bool ok = state.threads == 4;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_setoption_spin_clamps_too_low() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state,
+ "setoption name Threads value 0", out, sizeof(out));
+
+ bool ok = state.threads == 1;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_setoption_spin_clamps_too_high() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state,
+ "setoption name Threads value 99", out, sizeof(out));
+
+ bool ok = state.threads == 1;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_setoption_check_true() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state,
+ "setoption name UCI_AnalyseMode value true", out, sizeof(out));
+
+ bool ok = state.uci_analysemode == true;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_setoption_check_false() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+ state.uci_analysemode = true;
+
+ char out[256];
+ __capture_uci(handle_idle, &state,
+ "setoption name UCI_AnalyseMode value false", out, sizeof(out));
+
+ bool ok = state.uci_analysemode == false;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_setoption_button() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state,
+ "setoption name Clear Hash", out, sizeof(out));
+
+ bool ok = state.clear_hash == true;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_setoption_string() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state,
+ "setoption name UCI_Opponent value Magnus", out, sizeof(out));
+
+ bool ok = true;
+ ok &= state.uci_opponent.length == 6;
+ ok &= memcmp(state.uci_opponent.data, "Magnus", 6) == 0;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_setoption_hash_changes_value() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state,
+ "setoption name Hash value 128", out, sizeof(out));
+
+ bool ok = state.hash == 128;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+// ---- position ----
+
+bool test_position_startpos_loads() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ ipc_moves_t moves;
+ ipc_moves_init(&moves);
+ state.moves = &moves;
+
+ char out[256];
+ __capture_uci(handle_idle, &state,
+ "position startpos", out, sizeof(out));
+
+ bool ok = true;
+ ok &= state.position.bitboards[WHITE_PAWN] != 0;
+ ok &= state.position.turn == WHITE_TURN;
+
+ ipc_moves_deinit(&moves);
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_position_fen_loads() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ ipc_moves_t moves;
+ ipc_moves_init(&moves);
+ state.moves = &moves;
+
+ char out[256];
+ __capture_uci(handle_idle, &state,
+ "position fen \"k7/8/KQ6/8/8/8/8/8 w - - 0 1\"", out, sizeof(out));
+
+ bool ok = state.position.bitboards[WHITE_QUEEN] != 0;
+
+ ipc_moves_deinit(&moves);
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_position_startpos_moves() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ ipc_moves_t moves;
+ ipc_moves_init(&moves);
+ state.moves = &moves;
+
+ char out[256];
+ __capture_uci(handle_idle, &state,
+ "position startpos moves e2e4", out, sizeof(out));
+
+ bool ok = state.moves->count == 1;
+ if (ok) {
+ ok &= state.moves->data[0].from == 33;
+ ok &= state.moves->data[0].to == 35;
+ }
+
+ ipc_moves_deinit(&moves);
+ uci_state_deinit(&state);
+ return ok;
+}
+
+// ---- go ----
+
+bool test_go_depth_sets_args() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state, "go depth 10", out, sizeof(out));
+
+ bool ok = true;
+ ok &= state.go_args.depth == 10;
+ ok &= state.current_state == UCI_STATE_ACTIVE;
+ ok &= state.signal == UCI_SIGNAL_GO;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_go_wtime_btime_sets_args() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state,
+ "go wtime 60000 btime 60000 winc 1000 binc 1000", out, sizeof(out));
+
+ bool ok = true;
+ ok &= state.go_args.wtime == 60000;
+ ok &= state.go_args.btime == 60000;
+ ok &= state.go_args.winc == 1000;
+ ok &= state.go_args.binc == 1000;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_go_nodes_sets_args() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state, "go nodes 50000", out, sizeof(out));
+
+ bool ok = state.go_args.nodes == 50000;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_go_movetime_sets_args() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state, "go movetime 5000", out, sizeof(out));
+
+ bool ok = state.go_args.movetime == 5000;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_go_movestogo_sets_args() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state, "go movestogo 30", out, sizeof(out));
+
+ bool ok = state.go_args.movestogo == 30;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_go_mate_sets_args() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state, "go mate 3", out, sizeof(out));
+
+ bool ok = state.go_args.mate == 3;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_go_infinite_sets_flag() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state, "go infinite", out, sizeof(out));
+
+ bool ok = state.go_args.infinite == true;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_go_ponder_sets_flag() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state, "go ponder", out, sizeof(out));
+
+ bool ok = state.go_args.ponder == true;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_go_combined_args() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state,
+ "go depth 6 wtime 30000 btime 30000 winc 500 movestogo 20", out, sizeof(out));
+
+ bool ok = true;
+ ok &= state.go_args.depth == 6;
+ ok &= state.go_args.wtime == 30000;
+ ok &= state.go_args.btime == 30000;
+ ok &= state.go_args.winc == 500;
+ ok &= state.go_args.movestogo == 20;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_go_perft_sets_args() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state, "go perft 5", out, sizeof(out));
+
+ bool ok = state.go_args.perft == 5;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+// ---- state machine transitions ----
+
+bool test_active_stop_transitions_to_idle() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_ACTIVE;
+
+ char out[256];
+ __capture_uci(handle_active, &state, "stop", out, sizeof(out));
+
+ bool ok = true;
+ ok &= state.current_state == UCI_STATE_HALT;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_halt_sets_stop_signal() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_HALT;
+
+ char out[256];
+ __capture_uci(handle_halt, &state, "", out, sizeof(out));
+
+ bool ok = true;
+ ok &= state.signal == UCI_SIGNAL_STOP;
+ ok &= state.current_state == UCI_STATE_IDLE;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_active_isready_transitions_to_ping() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_ACTIVE;
+
+ char out[256];
+ __capture_uci(handle_active, &state, "isready", out, sizeof(out));
+
+ bool ok = state.current_state == UCI_STATE_PING;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_quit_sets_signal() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state, "quit", out, sizeof(out));
+
+ bool ok = state.signal == UCI_SIGNAL_QUIT;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_new_game_accepted() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.current_state = UCI_STATE_IDLE;
+
+ char out[256];
+ __capture_uci(handle_idle, &state, "ucinewgame", out, sizeof(out));
+
+ bool ok = state.current_state == UCI_STATE_IDLE;
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_full_handshake_sequence() {
+ uci_state_t state;
+ uci_state_init(&state);
+
+ ipc_moves_t moves;
+ ipc_moves_init(&moves);
+ state.moves = &moves;
+
+ char out[2048];
+
+ __capture_uci(handle_uci, &state, "uci", out, sizeof(out));
+ bool ok = state.current_state == UCI_STATE_IDLE;
+ ok &= strstr(out, "uciok") != NULL;
+
+ __capture_uci(handle_idle, &state, "isready", out, sizeof(out));
+ ok &= state.current_state == UCI_STATE_SYNC;
+
+ __capture_uci(handle_uci, &state, "isready", out, sizeof(out));
+ ok &= state.current_state == UCI_STATE_IDLE;
+ ok &= strstr(out, "readyok") != NULL;
+
+ __capture_uci(handle_idle, &state,
+ "setoption name Threads value 2", out, sizeof(out));
+ ok &= state.threads == 2;
+
+ __capture_uci(handle_idle, &state, "position startpos", out, sizeof(out));
+ ok &= state.position.bitboards[WHITE_PAWN] != 0;
+
+ __capture_uci(handle_idle, &state, "go depth 5", out, sizeof(out));
+ ok &= state.current_state == UCI_STATE_ACTIVE;
+ ok &= state.go_args.depth == 5;
+
+ __capture_uci(handle_active, &state, "stop", out, sizeof(out));
+ ok &= state.current_state == UCI_STATE_HALT;
+
+ __capture_uci(handle_halt, &state, "", out, sizeof(out));
+ ok &= state.current_state == UCI_STATE_IDLE;
+
+ ipc_moves_deinit(&moves);
+ uci_state_deinit(&state);
+ return ok;
+}
+#endif
diff --git a/src/uci/state.c b/src/uci/state.c
index 183f849..e108492 100644
--- a/src/uci/state.c
+++ b/src/uci/state.c
@@ -1,3 +1,4 @@
+#include <stdlib.h>
#include <string.h>
#include "uci/state.h"
@@ -180,7 +181,16 @@ void uci_state_init(uci_state_t *state) {
);
}
-void uci_state_deinit(uci_state_t *state) {}
+void uci_state_deinit(uci_state_t *state) {
+ int settings_count = sizeof(state->option_settings)/sizeof(option_setting_t);
+ for (int i = 0; i < settings_count; i++) {
+ option_setting_t *s = &state->option_settings[i];
+ if (s->type == OPTION_STRING && s->value.string && s->value.string->data) {
+ free(s->value.string->data);
+ s->value.string->data = NULL;
+ }
+ }
+}
void apply_option(uci_state_t *state, char *name, char *buffer) {
int settings_count = sizeof(state->option_settings)/sizeof(option_setting_t);
@@ -218,3 +228,147 @@ void apply_option(uci_state_t *state, char *name, char *buffer) {
}
}
+#ifdef TEST_MOD
+#include <stdbool.h>
+
+bool test_uci_state_init_defaults() {
+ uci_state_t state;
+ uci_state_init(&state);
+
+ bool ok = true;
+ ok &= state.signal == UCI_SIGNAL_NONE;
+ ok &= state.current_state == UCI_STATE_INITIAL;
+ ok &= state.threads == 1;
+ ok &= state.hash == 256;
+ ok &= state.clear_hash == false;
+ ok &= state.uci_showcurrline == false;
+ ok &= state.uci_showrefutations == false;
+ ok &= state.uci_limitstrength == false;
+ ok &= state.uci_elo == 800;
+ ok &= state.uci_analysemode == false;
+ ok &= strcmp(state.name, "Gacrux") == 0;
+ ok &= strcmp(state.author, "Aargh Rai <aarghrai.com>") == 0;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_apply_option_spin_valid() {
+ uci_state_t state;
+ uci_state_init(&state);
+
+ apply_option(&state, "Threads", "4");
+ bool ok = state.threads == 4;
+
+ apply_option(&state, "Hash", "128");
+ ok &= state.hash == 128;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_apply_option_spin_below_min() {
+ uci_state_t state;
+ uci_state_init(&state);
+
+ apply_option(&state, "Threads", "0");
+ bool ok = state.threads == 1;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_apply_option_spin_above_max() {
+ uci_state_t state;
+ uci_state_init(&state);
+
+ apply_option(&state, "Threads", "100");
+ bool ok = state.threads == 1;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_apply_option_check() {
+ uci_state_t state;
+ uci_state_init(&state);
+
+ apply_option(&state, "UCI_AnalyseMode", "true");
+ bool ok = state.uci_analysemode == true;
+
+ apply_option(&state, "UCI_AnalyseMode", "false");
+ ok &= state.uci_analysemode == false;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_apply_option_button() {
+ uci_state_t state;
+ uci_state_init(&state);
+
+ apply_option(&state, "Clear Hash", "");
+ bool ok = state.clear_hash == true;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_apply_option_string() {
+ uci_state_t state;
+ uci_state_init(&state);
+
+ apply_option(&state, "UCI_Opponent", "Carlsen");
+ bool ok = true;
+ ok &= state.uci_opponent.length == 7;
+ ok &= memcmp(state.uci_opponent.data, "Carlsen", 7) == 0;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_apply_option_unknown_ignored() {
+ uci_state_t state;
+ uci_state_init(&state);
+ state.debug = false;
+
+ apply_option(&state, "Nonexistent", "42");
+ bool ok = true;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_option_settings_count() {
+ uci_state_t state;
+ uci_state_init(&state);
+
+ int count = sizeof(state.option_settings) / sizeof(option_setting_t);
+ bool ok = count == 11;
+
+ uci_state_deinit(&state);
+ return ok;
+}
+
+bool test_option_settings_types() {
+ uci_state_t state;
+ uci_state_init(&state);
+
+ bool ok = true;
+ ok &= state.option_settings[0].type == OPTION_SPIN; // Threads
+ ok &= state.option_settings[1].type == OPTION_SPIN; // Hash
+ ok &= state.option_settings[2].type == OPTION_BUTTON; // Clear Hash
+ ok &= state.option_settings[3].type == OPTION_CHECK; // UCI_ShowCurrLine
+ ok &= state.option_settings[4].type == OPTION_CHECK; // UCI_ShowRefutations
+ ok &= state.option_settings[5].type == OPTION_CHECK; // UCI_LimitStrength
+ ok &= state.option_settings[6].type == OPTION_SPIN; // UCI_Elo
+ ok &= state.option_settings[7].type == OPTION_CHECK; // UCI_AnalyseMode
+ ok &= state.option_settings[8].type == OPTION_STRING; // UCI_Opponent
+ ok &= state.option_settings[9].type == OPTION_STRING; // UCI_EngineAbout
+ ok &= state.option_settings[10].type == OPTION_STRING;// UCI_SetPositionValue
+
+ uci_state_deinit(&state);
+ return ok;
+}
+#endif
+
diff --git a/tests/generated.c b/tests/generated.c
index fc10446..3789b0b 100644
--- a/tests/generated.c
+++ b/tests/generated.c
@@ -7,9 +7,11 @@
#include "../src/engine/moves.c"
#include "../src/engine/moves/king.c"
#include "../src/uci/command.c"
+#include "../src/uci/response.c"
+#include "../src/uci/state.c"
-int total_test_count = 43;
-bool (*tests[43])(void) = {
+int total_test_count = 86;
+bool (*tests[86])(void) = {
test_fen_no_passant,
test_fen_passant,
test_starting_position,
@@ -53,10 +55,53 @@ bool (*tests[43])(void) = {
test_black_king_corners,
test_uci_cmd_t,
test_invalid_cmd_uci_cmd_t,
+ test_uci_handshake_id_and_uciok,
+ test_uci_handshake_prints_all_options,
+ test_isready_returns_readyok,
+ test_isready_transitions_to_idle,
+ test_debug_on_sets_flag,
+ test_debug_off_clears_flag,
+ test_setoption_spin,
+ test_setoption_spin_clamps_too_low,
+ test_setoption_spin_clamps_too_high,
+ test_setoption_check_true,
+ test_setoption_check_false,
+ test_setoption_button,
+ test_setoption_string,
+ test_setoption_hash_changes_value,
+ test_position_startpos_loads,
+ test_position_fen_loads,
+ test_position_startpos_moves,
+ test_go_depth_sets_args,
+ test_go_wtime_btime_sets_args,
+ test_go_nodes_sets_args,
+ test_go_movetime_sets_args,
+ test_go_movestogo_sets_args,
+ test_go_mate_sets_args,
+ test_go_infinite_sets_flag,
+ test_go_ponder_sets_flag,
+ test_go_combined_args,
+ test_go_perft_sets_args,
+ test_active_stop_transitions_to_idle,
+ test_halt_sets_stop_signal,
+ test_active_isready_transitions_to_ping,
+ test_quit_sets_signal,
+ test_new_game_accepted,
+ test_full_handshake_sequence,
+ test_uci_state_init_defaults,
+ test_apply_option_spin_valid,
+ test_apply_option_spin_below_min,
+ test_apply_option_spin_above_max,
+ test_apply_option_check,
+ test_apply_option_button,
+ test_apply_option_string,
+ test_apply_option_unknown_ignored,
+ test_option_settings_count,
+ test_option_settings_types,
};
int max_test_name_size = 35;
-char test_names[43][100] = {
+char test_names[86][100] = {
"fen_no_passant",
"fen_passant",
"starting_position",
@@ -100,4 +145,47 @@ char test_names[43][100] = {
"black_king_corners",
"uci_cmd_t",
"invalid_cmd_uci_cmd_t",
+ "uci_handshake_id_and_uciok",
+ "uci_handshake_prints_all_options",
+ "isready_returns_readyok",
+ "isready_transitions_to_idle",
+ "debug_on_sets_flag",
+ "debug_off_clears_flag",
+ "setoption_spin",
+ "setoption_spin_clamps_too_low",
+ "setoption_spin_clamps_too_high",
+ "setoption_check_true",
+ "setoption_check_false",
+ "setoption_button",
+ "setoption_string",
+ "setoption_hash_changes_value",
+ "position_startpos_loads",
+ "position_fen_loads",
+ "position_startpos_moves",
+ "go_depth_sets_args",
+ "go_wtime_btime_sets_args",
+ "go_nodes_sets_args",
+ "go_movetime_sets_args",
+ "go_movestogo_sets_args",
+ "go_mate_sets_args",
+ "go_infinite_sets_flag",
+ "go_ponder_sets_flag",
+ "go_combined_args",
+ "go_perft_sets_args",
+ "active_stop_transitions_to_idle",
+ "halt_sets_stop_signal",
+ "active_isready_transitions_to_ping",
+ "quit_sets_signal",
+ "new_game_accepted",
+ "full_handshake_sequence",
+ "uci_state_init_defaults",
+ "apply_option_spin_valid",
+ "apply_option_spin_below_min",
+ "apply_option_spin_above_max",
+ "apply_option_check",
+ "apply_option_button",
+ "apply_option_string",
+ "apply_option_unknown_ignored",
+ "option_settings_count",
+ "option_settings_types",
};