diff options
| author | Aargh Rai <aargh.rai+git@gmail.com> | 2026-08-15 13:55:35 +0530 |
|---|---|---|
| committer | Aargh Rai <aargh.rai+git@gmail.com> | 2026-08-15 13:55:35 +0530 |
| commit | 378cc549ab442c845ac9d62cc8cef6977073fe4d (patch) | |
| tree | 83b4575d7ad80cdd4f694f596f5655e164e0478f | |
| parent | 7d36403836511ab0745791ce98498cf5112633c1 (diff) | |
md files
| -rw-r--r-- | PROJECT.md | 242 | ||||
| -rw-r--r-- | STYLE_GUIDE.md | 303 | ||||
| -rw-r--r-- | include/engine/threads.h | 50 | ||||
| -rw-r--r-- | include/uci/internals/command.h | 2 | ||||
| -rw-r--r-- | src/engine/thread.c | 12 | ||||
| -rw-r--r-- | src/engine/threads.c | 16 | ||||
| -rw-r--r-- | src/uci.c | 3 | ||||
| -rw-r--r-- | src/uci/command.c | 15 |
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; @@ -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"); |
