summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAargh Rai <aargh.rai+git@gmail.com>2026-08-15 13:55:35 +0530
committerAargh Rai <aargh.rai+git@gmail.com>2026-08-15 13:55:35 +0530
commit378cc549ab442c845ac9d62cc8cef6977073fe4d (patch)
tree83b4575d7ad80cdd4f694f596f5655e164e0478f
parent7d36403836511ab0745791ce98498cf5112633c1 (diff)
md files
-rw-r--r--PROJECT.md242
-rw-r--r--STYLE_GUIDE.md303
-rw-r--r--include/engine/threads.h50
-rw-r--r--include/uci/internals/command.h2
-rw-r--r--src/engine/thread.c12
-rw-r--r--src/engine/threads.c16
-rw-r--r--src/uci.c3
-rw-r--r--src/uci/command.c15
8 files changed, 624 insertions, 19 deletions
diff --git a/PROJECT.md b/PROJECT.md
new file mode 100644
index 0000000..5a3826a
--- /dev/null
+++ b/PROJECT.md
@@ -0,0 +1,242 @@
+# Gacrux
+
+A chess engine written in C, implementing the Universal Chess Interface (UCI)
+protocol. It supports parallel search across multiple threads using a
+bitboard-based position representation with magic-bitboard move generation and a
+transposition table backed by Zobrist hashing.
+
+## Project Layout
+
+```
+gacrux/
+├── build # Self-rebuilding C build binary (compiled from build.c)
+├── build.c # Build system: self-rebuilding, uses fork()/exec() to compile sources
+├── justfile # Task recipes (dev, test, analysis)
+├── shell.nix # Nix devShell flake (gcc, python, optional Rust/Node/web libs)
+├── .envrc # direnv: `use nix`
+├── LICENSE # GPLv3
+├── include/ # Public headers
+│ ├── ints.h # Fixed-width integer typedefs (u8..u128, i8..i128, f32, f64)
+│ ├── bitboard.h # position_t bitboard representation, castling/turn enums
+│ ├── fen.h # FEN string loader API
+│ ├── random.h # PRNG for magic numbers & Zobrist keys
+│ ├── 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
+│ │ └── ttable.h # Transposition table (Zobrist hashing, buckets)
+│ ├── ipc/ # Inter-process / thread IPC headers
+│ │ ├── state.h # ipc_state_t: shared atomic event state
+│ │ ├── threads.h # Links engine threads to IPC state
+│ │ ├── uci.h # UCI thread entry point & IPC handling
+│ │ ├── engine_message.h # Engine info messages (depth, score, pv, bestmove)
+│ │ ├── go_args.h # `go` command arguments (time/depth/node limits, etc.)
+│ │ └── moves.h # IPC move types (from/to square pairs)
+│ └── uci/ # UCI protocol headers
+│ ├── state.h # uci_state_t: options, signals, position, FEN
+│ └── internals/
+│ ├── command.h # uci_cmd_t: parsed UCI command/token buffer
+│ └── response.h # UCI state machine (initial/idle/sync/ping/active/halt)
+├── src/ # C source
+│ ├── main.c # Entry point: inits magic + zobrist, spawns UCI + engine threads
+│ ├── uci.c # UCI stdin read loop + IPC signal handling + engine message emission
+│ ├── engine.c # Aggregates all engine .c includes
+│ ├── ipc.c # Aggregates all IPC .c includes
+│ ├── fen.c # FEN string parser → position_t
+│ ├── bitboard.c # position_starting(), print helpers, position validation
+│ ├── random.c # xorshift64 PRNG (random_u64, random_magic)
+│ ├── uci/
+│ │ ├── command.c # UCI command tokenization/parsing
+│ │ ├── state.c # UCI engine option definitions (Threads, Hash, etc.)
+│ │ └── response.c # UCI state machine command handlers
+│ ├── ipc/
+│ │ ├── state.c # Atomic event wait/set for IPC synchronization
+│ │ ├── threads.c # Threads <-> IPC state linking
+│ │ ├── engine_message.c # Push/pop engine info messages (atomic ready flag)
+│ │ ├── go_args.c # should_continue() for search limit checks
+│ │ └── 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
+│ ├── ttable.c # Zobrist init, transposition table insert/find
+│ └── moves/
+│ ├── vec.c # Dynamic move list (moves_t) management
+│ ├── magic.c # Magic bitboard attack table generation
+│ ├── knight.c # Knight move generation
+│ ├── pawn.c # Pawn push/capture (incl. en passant) generation
+│ ├── rook.c # Rook move generation (magic bitboards)
+│ ├── bishop.c # Bishop move generation (magic bitboards)
+│ ├── queen.c # Queen move generation
+│ ├── king.c # King move generation + castling + tests
+│ └── attack.c # square_attacked() detection
+├── tests/
+│ ├── main.c # Test runner: executes all discovered tests w/ timing
+│ ├── generated.c # Auto-generated test table (created by find_tests.o)
+│ ├── generated/ # Directory created at build time
+│ └── find_tests.c # Scans src/ for `bool test_*()` functions, emits generated.c
+├── symbol_analyzer/ # Python static-analysis tool
+│ ├── main.py # Walks include/ + src/, extracts symbols/imports/usage, emits analysis.js
+│ ├── parser.py # Token-based C definition parser (structs, enums, functions, typedefs)
+│ ├── symbols.py # Struct/enum symbol extraction
+│ ├── extractor.py # Character-level C tokenizer (handles macros, depth tracking)
+│ └── analysis.js # Output data consumed by an HTML visualization
+├── resources/
+│ └── uci/
+│ ├── uci_min.txt # Minimal UCI spec reference
+│ └── UCI-Protocol-Specification.txt # Full UCI protocol spec
+├── output/ # Build artifacts (.o files)
+└── opt/ # External / vendored projects (gitignored)
+ ├── en-croissant/ # React + Tauri chess GUI
+ └── stockfish/ # Stockfish binary (for reference testing)
+```
+
+## Build System
+
+The build system (`build.c`) is a single C program inspired by
+[tsoding/nob.h](https://github.com/tsoding/nob.h). It has three notable
+properties:
+
+1. **Self-rebuilding:** On startup, `build.c` checks `needs_rebuild()` (compares
+ its own source mtime against the binary). If `build.c` changed since the last
+ build, it recompiles and `execvp`s itself, then continues. This lets the build
+ script "upgrade itself" transparently.
+
+2. **Modes:** Takes a subcommand (`uci`, `test`, `clean`) with an optional `run`
+ flag:
+ - `./build uci` — compiles `src/ipc.c`, `src/engine.c`, `src/uci.c`, and
+ `src/main.c` (with ASan + debug) into `output/main.o`.
+ - `./build uci run` — compiles then `exec`s the engine (runs the UCI loop).
+ - `./build test` — compiles `tests/find_tests.c`, runs it to regenerate
+ `tests/generated.c`, then compiles `tests/main.c` into `output/test.o`.
+ - `./build test run` — compiles tests then runs the test binary.
+ - `./build clean` — removes the `output/` directory.
+
+3. **Compilation** uses `gcc -fsanitize=address -g -Iinclude/` for all targets.
+
+The `justfile` wraps these:
+- `just dev` → builds and runs the engine (`build uci` + `build uci run`).
+- `just test` → builds and runs tests (`build test` + `build test run`).
+- `just analysis` → runs the Python symbol analyzer and opens the visualization.
+
+## Architecture
+
+### Concurrency Model
+
+The engine uses a **multi-threaded, event-driven** architecture:
+
+- **UCI thread** (`src/uci.c:uci`): A pthread that continuously reads from stdin
+ (non-blocking), parses UCI commands, updates `uci_state`, and emits engine info
+ messages + best moves to stdout. It runs the entire UCI state machine.
+
+- **Engine search threads** (`src/engine/thread.c`): A pool of pthreads managed
+ 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`).
+
+- **IPC layer** (`src/ipc/`): Shared `ipc_state_t` containing an atomic `event`
+ field that acts as a synchronization primitive. Events flow between UCI and
+ engine threads:
+ - `EVENT_ENGINE_GO` → engine starts searching.
+ - `EVENT_ENGINE_RUNNING` → engine is actively searching (UCI loop polls).
+ - `EVENT_ENGINE_STOP` / `EVENT_QUIT` → engine must stop.
+
+ The UCI thread uses `engine_message_handler_t` (atomic `ready` flag ring buffer
+ pattern) to push search info (depth, score, principal variation) to the UCI
+ thread without locks.
+
+### Position Representation
+
+Uses **64-bit bitboards** (`bitboard_t`, one per piece type × color, 12 total)
+stored in a `position_t` struct. Squares are indexed 0–63 (0 = a1, 7 = h1,
+56 = a8, 63 = h8). The starting position is hardcoded in `position_starting()`
+(`src/bitboard.c`).
+
+### Move Generation
+
+- **Pseudolegal move generation** per piece in `src/engine/moves/*.c`, using
+ magic bitboards for sliding pieces (rook, bishop, queen) via precomputed attack
+ tables (`src/engine/moves/magic.c`).
+- **Legal move filtering** in `src/engine/moves.c:get_legal_moves()` — generates
+ pseudolegal moves, then filters out moves that leave own king in check via
+ `square_attacked()`.
+- **Make-move** (`position_make_move`) handles all special moves: castling,
+ en passant, promotions, captures, castling-right updates.
+
+### Transposition Table
+
+- **Zobrist hashing** (`src/engine/ttable.c`): Random 64-bit keys per piece×square,
+ castling rights, turn, en-passant. Initialized once at startup.
+- **Transposition table** (`include/engine/ttable.h`): Bucket-based hash table
+ (4 entries per bucket, `TT_BUCKET_SIZE`), with atomic keys for lock-free lookup.
+ Insertion replaces the shallowest entry when a bucket is full (replacement
+ scheme).
+
+### UCI Protocol
+
+- Implements the standard UCI state machine (`include/uci/internals/response.h`):
+ `INITIAL` → `IDLE` → (`SYNC` / `PING` / `ACTIVE` / `HALT`).
+- Supports all standard UCI options: `Threads`, `Hash`, `Clear Hash`,
+ `UCI_ShowCurrLine`, `UCI_ShowRefutations`, `UCI_LimitStrength`, `UCI_Elo`,
+ `UCI_AnalyseMode`, `UCI_Opponent`, `UCI_EngineAbout`, `UCI_SetPositionValue`.
+- UCI spec reference: `resources/uci/uci_min.txt` and `UCI-Protocol-Specification.txt`.
+
+### 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 discovery** (`tests/find_tests.c`): Scours `src/` for `bool test_*()`
+ signatures, generates `tests/generated.c` with a function-pointer table and
+ name list.
+- **Test runner** (`tests/main.c`): Iterates the table, executes each test, prints
+ pass/fail with timing. Perft tests verify move generation correctness against
+ known node counts at various depths (starting position, Kiwipete, position 3).
+
+### Symbol Analyzer
+
+A Python static-analysis tool (`symbol_analyzer/`) that tokenizes C source files,
+extracts struct/enum/function/typedef definitions and symbol usage, resolves
+transitive includes, and computes unused/used symbol sets per file. Outputs
+`analysis.js` (JSON) for visualization via an HTML page.
+
+## Building & Running
+
+```sh
+# Build the build system, then build the engine
+./build uci
+
+# Run the engine (reads UCI commands from stdin)
+./build uci run
+
+# Or via just
+just dev # build + run
+
+# Build & run tests
+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.
+
+## Dependencies
+
+- **Build/runtime**: `gcc` (with ASan + pthreads), `python3` (for analysis).
+- **DevShell**: Nix expression (`shell.nix`) pins gcc, python313, clang-tools,
+ and optionally Rust toolchain, Node.js/pnpm, and WebKitGTK (for the symbol
+ analyzer HTML visualization and the en-croissant GUI).
+- **Reference engines**: `opt/stockfish/` bundles the Stockfish binary for
+ comparison; `opt/en-croissant/` is a React/Tauri chess GUI in `opt/`.
+
+## License
+
+[GPLv3](LICENSE) — "Gacrux by Aargh Rai <aarghrai.com>".
diff --git a/STYLE_GUIDE.md b/STYLE_GUIDE.md
new file mode 100644
index 0000000..2581b5b
--- /dev/null
+++ b/STYLE_GUIDE.md
@@ -0,0 +1,303 @@
+# Gacrux C Style Guide
+
+This document describes the coding conventions used throughout the Gacrux chess
+engine. It is derived directly from the existing source so that new code matches
+the established style.
+
+## Files & Compilation Units
+
+### Aggregation via `#include "*.c"`
+
+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`.
+- `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"`,
+ `#include "bitboard.c"`, `#include "random.c"` at the **bottom** of the file,
+ after `main()`.
+
+This means each leaf `.c` (e.g. `src/fen.c`, `src/random.c`) is a fragment
+compiled only when included, never standalone.
+
+### Internal-define gating
+
+Headers expose internal-only declarations behind a `_INTERNAL` guard. The
+consuming `.c` file `#define`s the sentinel before including the header:
+
+```c
+#define ENGINE_THREADS_INTERNAL
+#include "engine/threads.h"
+```
+
+Other gates in use:
+
+- `MOVES_INTERNAL` — `include/engine/moves.h` (`__forloop_*` declarations).
+- `IPC_INTERNAL` — `include/ipc/state.h`.
+
+### Test gating
+
+`#ifdef TEST_MOD` blocks at the bottom of source files define `test_*`
+functions and pull in any extras (e.g. `#include "fen.h"`,
+`#include "bitboard.c"`). The test runner (`tests/main.c`) defines
+`TEST_MOD` first so all tests are visible.
+
+## Indentation & Whitespace
+
+- **2 spaces** per indent level. No tabs.
+- No trailing whitespace.
+- A single blank line separates most statements; dense blocks omit blank
+ lines between related short statements.
+- Long function signatures wrap one parameter per line, indented 2 spaces,
+ with the opening `{` on its own line:
+
+```c
+void option_setting_combo(
+ option_setting_t *target,
+ char *option_name,
+ char **combinations,
+ int combinations_count,
+ int default_index,
+ combo_t *combo
+) {
+ ...
+}
+```
+
+When the signature fits, the `{` stays on the same line:
+
+```c
+int main(int argc, char** argv) {
+ ...
+}
+```
+
+## Braces
+
+- Opening brace on the same line as `if`/`else`/`for`/`while` when the condition
+ fits, or on its own line after a multi-line condition.
+- `else` on the same line as the closing brace: `} else if (...)`.
+- Single-statement bodies often omit braces:
+
+```c
+if (i < MAX_TOKEN_SIZE) cmd->root[i] = 0;
+```
+
+```c
+for (int j = 0; m; j++) {
+ if (i & (1 << j)) {
+ occs[i] |= (bitboard_t)1 << __builtin_ctzll(m);
+ }
+ m &= m - 1;
+}
+```
+
+## Naming
+
+| Kind | Convention | Example |
+|-----------------|------------------------|-------------------------------------------|
+| Functions | `snake_case` | `get_pawn_moves`, `position_make_move` |
+| Typedef structs | `name_t` | `position_t`, `move_t`, `ipc_state_t` |
+| Named structs | `struct name` | `struct threads`, `struct child_thread_args` |
+| Enums/constants | anonymous `enum { }` | `enum { WHITE_KING, ..., PIECE_TYPE_COUNT }` |
+| Enum values | `UPPER_SNAKE_CASE` | `WHITE_SHORT_CASTLE`, `BLACK_TURN` |
+| Macros/constants| `UPPER_SNAKE_CASE` | `BITMASK_RANK_1`, `TT_BUCKET_SIZE` |
+| Private funcs | `_single_prefix` | `_add_move`, `_add_ipc_move` |
+| Internal funcs | `__double_prefix` | `__ttable_insert`, `__threads_args_init` |
+| File-scope vars | `static` | `static magic_t rook_table[64];` |
+
+### Struct vs. typedef choice
+
+- Use `typedef struct { ... } name_t;` (anonymous) for opaque/value types that
+ are only ever referenced through the typedef — `position_t`, `move_t`,
+ `move_t`, `ipc_state_t`, `tentry_t`, `magic_t`.
+- Use `struct name { ... };` (named, no typedef) when the type must be
+ identified by name (forward declarations, self-reference, or union members) —
+ `struct engine_message`, `struct threads`, `struct child_thread_args`.
+
+### Self-referential structs
+
+```c
+typedef struct engine_message {
+ atomic_int ready;
+ ...
+ struct engine_message *next;
+} engine_message_t;
+```
+
+## Function Lifecycle Convention
+
+Every "object" type follows an `init` / `deinit` pair.
+
+- `init` operating on a caller-owned struct takes a pointer:
+ `void xxx_init(xxx_t *out)` / `void xxx_init(struct xxx *handler)`.
+- `deinit` frees internal resources and takes a pointer (or by-value handle for
+ trivially-copyable types): `void xxx_deinit(xxx_t out)`,
+ `void uci_cmd_deinit(uci_cmd_t cmd)`.
+
+## Macros
+
+### Expression macros (no statement semantics)
+
+Written without surrounding parentheses, relying on call-site precedence —
+notably the bitmask aggregates:
+
+```c
+#define whites(position) \
+ position.bitboards[WHITE_KING] | \
+ position.bitboards[WHITE_QUEEN] | \
+ ...
+```
+
+Single-expression macros may be parenthesized:
+
+```c
+#define occupied_by(bitboard, index) (bitboard & ((bitboard_t)1 << index))
+```
+
+### Variadic wrapper macros
+
+A thin macro delegates to an underscore-prefixed function, building a
+designated-initializer struct inline:
+
+```c
+#define add_move(moves, from, to, ...) _add_move(\
+ moves, \
+ from, \
+ to, \
+ (struct add_move_params) { .flags = 0, __VA_ARGS__ }\
+)
+```
+
+```c
+#define ttable_insert(ttable, ...) __ttable_insert( \
+ ttable, \
+ (struct ttable_insert_args) { __VA_ARGS__ } \
+)
+```
+
+### Statement macros
+
+Multi-statement macros use `do { ... } while (0)` (the build system does this):
+
+```c
+#define RUN_CMD(...) do { \
+ if (fork() == 0) { \
+ char* args[] = __VA_ARGS__; \
+ ... \
+ } else { \
+ wait(&status); \
+ } \
+} while (0);
+```
+
+## Function signatures & parameter style
+
+- Pointer-to-struct receiver is the first parameter:
+ `void get_king_moves(moves_t *moves, position_t position)`.
+- Small structs/positional params passed by value: `move_t move`,
+ `position_t position`, `bitboard_t occ`.
+- `int`/pointer params are not typedef'd; use `u8`/`u16`/`u32`/`u64` (from
+ `include/ints.h`) for widths.
+- `const`-ness is not heavily used; raw pointers are common even for
+ read-only access.
+
+## Enums
+
+Anonymous enums for constant sets:
+
+```c
+enum { WHITE_KING, WHITE_QUEEN, ..., PIECE_TYPE_COUNT };
+```
+
+```c
+enum { TT_EXACT, TT_LOWERBOUND, TT_UPPERBOUND };
+```
+
+Flag bits use explicit bit shifts:
+
+```c
+enum {
+ WHITE_SHORT_CASTLE = 1 << 0,
+ WHITE_LONG_CASTLE = 1 << 1,
+ ...
+};
+```
+
+## Error Handling
+
+- `assert(condition)` for internal invariants and unreachable code:
+ `assert(0 && "Failed to find magic number")`, `assert(0 && "Invalid argument...")`
+- `bool`-returning predicates for validity checks:
+ `bool check_valid_position(position_t position)`, returns `true`/`false`.
+- Lookup functions return `NULL` on miss: `tentry_t *ttable_find(...)`.
+- Error-as-data via a `bool failed` struct field:
+ `struct fen_load { bool failed; position_t position; };`
+
+## Comments
+
+- `//` for inline and short comments.
+- `/* ... */` for multi-line explanations.
+- Trailing inline comments often document field semantics, sometimes aligned:
+ `i16 score; // depth analysed`
+- Explanatory comments about timing constraints appear in the UCI layer:
+ `// TIMEOUT NOTICE: THE FOLLOWING BLOCK SHOULDN'T TAKE MORE THAN 5 SECONDS`
+
+## Atomics
+
+Use `<stdatomic.h>` primitives directly:
+
+- `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.
+
+## Test Functions
+
+Embedded test functions:
+
+- Signature: `bool test_<thing>(void)`.
+- Return `true` = pass, `false` = fail.
+- Use `assert()` for intermediate checks; `return false` to fail a specific
+ assertion so the runner can report it.
+
+```c
+bool test_uci_cmd_t() {
+ uci_cmd_t cmd;
+ uci_cmd_init(&cmd);
+ uci_cmd_add(&cmd, "john");
+ uci_cmd_add(&cmd, "debug");
+ uci_cmd_add(&cmd, "on");
+
+ if (strncmp(cmd.root, "debug", MAX_TOKEN_SIZE) != 0) {
+ uci_cmd_deinit(cmd);
+ return false;
+ }
+ ...
+ uci_cmd_deinit(cmd);
+ return true;
+}
+```
+
+## Build system (`build.c`)
+
+- A self-rebuilding `nob.h`-style driver.
+- Defines `CC` macro: `#define CC "gcc", "-fsanitize=address", "-g", GLOBAL_INCLUDE`
+- `needs_rebuild(bin, src)` compares mtimes via `stat()`.
+- Subprocesses launched via `fork()`/`execvp()` inside a `RUN_CMD` `do/while(0)`
+ macro, with `wait(&status)`.
+- Output object files live in `output/`.
+
+## Include ordering
+
+The canonical order (see `src/uci.c`, `src/main.c`) is:
+
+1. System headers: `<string.h>`, `<stdio.h>`, `<stdlib.h>`, `<unistd.h>`, etc.
+2. Blank line.
+3. Project headers using path-qualified includes: `"uci/internals/command.h"`,
+ `"ipc/state.h"`, `"engine/magic.h"`.
+
+Some older files (`src/bitboard.c`, `src/engine/moves.c`) place the project
+header first or a system header last; prefer the system-first / blank-line /
+project order for new files.
diff --git a/include/engine/threads.h b/include/engine/threads.h
index 4815a4c..b5f2163 100644
--- a/include/engine/threads.h
+++ b/include/engine/threads.h
@@ -5,10 +5,43 @@
#include <stdatomic.h>
#include "bitboard.h"
+#include "engine/ttable.h"
#include "ipc/engine_message.h"
#include "ipc/go_args.h"
-struct thread_args {
+enum { SPLITPOINT_STATE_FREE, SPLITPOINT_STATE_ACTIVE };
+enum { NODE_PV, NODE_CUT, NODE_ALL };
+typedef struct split_point {
+ atomic_int state;
+ atomic_int next_move_idx;
+ atomic_int alpha, beta;
+ atomic_int best_score;
+ atomic_int workers;
+ move_t best_score;
+
+ struct split_point *parent;
+ int ply;
+ int node_type;
+ int confidence;
+ position_t position_snapshot;
+ moves_t moves;
+} split_point_t;
+
+struct child_thread_args {
+ // signal sent by threads manager
+ atomic_int stop;
+
+ // signal sent by thread
+ atomic_int finished;
+
+ int id;
+ go_args_t *go_args;
+
+ position_t position;
+ int sp_stack;
+ int
+};
+struct root_thread_args {
// signal sent by threads manager
atomic_int stop;
@@ -21,9 +54,14 @@ struct thread_args {
engine_message_handler_t *message;
};
+typedef union {
+ struct child_thread_args child_thread_args;
+ struct root_thread_args root_thread_args;
+} thread_args_t;
+
struct threads {
pthread_t *threads;
- struct thread_args *args;
+ thread_args_t *args;
size_t count;
size_t capacity;
@@ -31,11 +69,14 @@ struct threads {
engine_messages_t *engine_messages;
go_args_t *go_args;
position_t *position;
+
+ ttable_t ttable;
+ void* split_point_pool;
};
#ifdef ENGINE_THREADS_INTERNAL
void __threads_args_init(
- struct thread_args* args,
+ thread_args_t* args,
struct threads *handler,
int i
);
@@ -48,6 +89,7 @@ bool threads_go_loop(struct threads *handler);
bool threads_all_done(struct threads *handler);
void threads_cleanup(struct threads *handler);
-void *engine_thread(void *args);
+void *child_thread(void *args);
+void *root_thread(void *args);
#endif // ENGINE_THREADS_H
diff --git a/include/uci/internals/command.h b/include/uci/internals/command.h
index 2994b04..21df9c0 100644
--- a/include/uci/internals/command.h
+++ b/include/uci/internals/command.h
@@ -11,7 +11,7 @@ typedef struct {
bool empty;
} uci_cmd_t;
-uci_cmd_t uci_cmd_init();
+void uci_cmd_init(uci_cmd_t *cmd);
void uci_cmd_add(uci_cmd_t *cmd, const char *token);
void uci_cmd_set_root(uci_cmd_t *cmd, const char *token);
void uci_cmd_append_arg(uci_cmd_t *cmd, const char *token);
diff --git a/src/engine/thread.c b/src/engine/thread.c
index 017e9b6..25e583e 100644
--- a/src/engine/thread.c
+++ b/src/engine/thread.c
@@ -1,8 +1,8 @@
#include "engine/threads.h"
#include <stdio.h>
-void *engine_thread(void *_args) {
- struct thread_args *args = (struct thread_args*)_args;
+void *child_thread(void *_args) {
+ struct child_thread_args *args = (struct child_thread_args*)_args;
args->stop = 0;
int depth = 0;
@@ -32,3 +32,11 @@ void *engine_thread(void *_args) {
args->stop = 2;
}
+// DTS requires a root thread which is initialize the search tree, and the
+// others who use that search tree. it's as if root_thread is the owner of the
+// tree
+void *root_thread(void *_args) {
+ struct root_thread_args *args = (struct root_thread_args*)_args;
+ args->stop = 0;
+}
+
diff --git a/src/engine/threads.c b/src/engine/threads.c
index 5afe155..541efb1 100644
--- a/src/engine/threads.c
+++ b/src/engine/threads.c
@@ -48,7 +48,7 @@ void threads_go(struct threads *handler) {
pthread_create(
handler->threads + i,
NULL,
- engine_thread,
+ i == 0 ? root_thread : child_thread,
handler->args + i
);
}
@@ -78,10 +78,22 @@ void threads_cleanup(struct threads *handler) {
}
void __threads_args_init(
- struct thread_args* args,
+ thread_args_t* _args,
struct threads *handler,
int i
) {
+ if (i > 0) {
+ struct child_thread_args *args = _args;
+ atomic_init(&args->stop, 0);
+ atomic_init(&args->finished, 0);
+ args->id = i;
+ args->position = handler->position;
+ args->go_args = handler->go_args;
+ args->message = handler->engine_messages->data[i];
+ return;
+ }
+
+ struct root_thread_args *args = _args;
atomic_init(&args->stop, 0);
atomic_init(&args->finished, 0);
args->id = i;
diff --git a/src/uci.c b/src/uci.c
index f842328..7b1ab8c 100644
--- a/src/uci.c
+++ b/src/uci.c
@@ -37,7 +37,8 @@ void *uci(ipc_state_t *ipc_state) {
// https://stackoverflow.com/a/41559081
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
- uci_cmd_t cmd = uci_cmd_init();
+ uci_cmd_t cmd;
+ uci_cmd_init(&cmd);
size_t n;
char buf[1024];
diff --git a/src/uci/command.c b/src/uci/command.c
index 583b887..327e0e2 100644
--- a/src/uci/command.c
+++ b/src/uci/command.c
@@ -3,14 +3,10 @@
#include <stdlib.h>
#include "uci/internals/command.h"
-uci_cmd_t uci_cmd_init() {
- uci_cmd_t output = {0};
-
- output.empty = true;
- output.args_capacity = 32;
- output.args = calloc(sizeof(char) * MAX_TOKEN_SIZE, output.args_capacity);
-
- return output;
+void uci_cmd_init(uci_cmd_t *cmd) {
+ cmd->empty = true;
+ cmd->args_capacity = 32;
+ cmd->args = calloc(sizeof(char) * MAX_TOKEN_SIZE, output.args_capacity);
}
bool is_valid_cmd(const char *token) {
@@ -122,7 +118,8 @@ bool test_uci_cmd_t() {
}
bool test_invalid_cmd_uci_cmd_t() {
- uci_cmd_t cmd = uci_cmd_init();
+ uci_cmd_t cmd;
+ uci_cmd_init(&cmd);
uci_cmd_add(&cmd, "john");
uci_cmd_add(&cmd, "debug");