diff options
| author | Aargh Rai <aargh.rai+git@gmail.com> | 2026-07-18 14:02:18 +0530 |
|---|---|---|
| committer | Aargh Rai <aargh.rai+git@gmail.com> | 2026-07-18 14:04:14 +0530 |
| commit | 9395e857db95481f6e2494d3a841632be0ff97c8 (patch) | |
| tree | dfaa6ab66ea5dd083c060a5dba0cf41ed9cb754c | |
| parent | c34b4c1b036058b2906556850cc4ded6ececcf89 (diff) | |
fixed moved gen & adding messages on asserts
| -rwxr-xr-x | .gitignore | 1 | ||||
| -rw-r--r-- | Plan.md | 100 | ||||
| -rw-r--r-- | src/engine/moves.c | 31 | ||||
| -rw-r--r-- | src/uci/response.c | 2 | ||||
| -rw-r--r-- | src/uci/state.c | 4 |
5 files changed, 28 insertions, 110 deletions
@@ -1,3 +1,4 @@ +.direnv build *.o generated diff --git a/Plan.md b/Plan.md deleted file mode 100644 index a300420..0000000 --- a/Plan.md +++ /dev/null @@ -1,100 +0,0 @@ -# Gacrux: Pseudo-Legal to Legal Move Conversion Plan - -## Current State -- Move generators produce **pseudo-legal** moves (follow piece movement rules but ignore checks/pins) -- `position_make_move()` has multiple bugs -- No attack detection or legal move filtering exists -- Perft test conditions are inverted - -## Phase 1: Fix Existing Bugs - -### `src/engine/moves.c` — `position_make_move()` -1. **Lines 94, 100**: Add missing semicolons after `assert(0)` -2. **Lines 54-55**: Black long castle modifies `WHITE_ROOK` → should be `BLACK_ROOK` -3. **Lines 71-73**: Black short castle modifies `WHITE_KING`/`WHITE_ROOK` → should be `BLACK_KING`/`BLACK_ROOK` -4. **Lines 96, 98**: `move.from` (u8) compared against `u64` bitboard constants → use square indices 63 and 56 -5. **Missing turn toggle**: `position->turn` never flips after a move - -### `test_perft_starting_position` -6. **Lines 169-176**: `==` → `!=` (test currently passes when counts are wrong) - -## Phase 2: Attack Detection - -Create `src/engine/moves/attack.c` with: -```c -bool square_attacked(position_t position, square_t square, u8 by_color); -``` - -Checks if any piece of `by_color` attacks `square`: -- **Pawn attacks**: Check diagonally forward for enemy pawns -- **Knight attacks**: Use existing `knight_moves[64]` lookup -- **King attacks**: Use existing `920078ULL` pattern -- **Sliding attacks**: Walk rays from target square (for now, ray-walking) - -## Phase 3: Legal Move Filtering - -Modify `get_moves()` (or add `get_legal_moves()`) to: -1. Generate all pseudo-legal moves -2. For each move, make it on a copy of the position -3. Find the friendly king in the resulting position -4. Check if the friendly king is attacked by the opponent -5. If attacked → remove the move (swap with last, decrement length) - -**Castling pre-checks** (in `king.c`): -- King not currently in check -- Squares king passes through are not attacked -- No pieces between king and rook - -## Phase 4: Magic Bitboards (Performance Optimization) - -Replace ray-walking with O(1) magic lookups for sliding piece attacks. - -### Data Structure -```c -typedef struct { - bitboard_t mask; // relevant occupancy bits (excludes edges) - bitboard_t *attacks; // pointer into attack table - bitboard_t magic; // the magic multiplier - int shift; // 64 - popcount(mask) -} magic_t; -``` - -### Attack Tables -- `rook_attacks[0x19000]` (~100KB) -- `bishop_attacks[0x1480]` (~5KB) - -### Index Computation -```c -unsigned index = ((occupied & magic.mask) * magic.magic) >> magic.shift; -return magic.attacks[index]; -``` - -### Initialization -At startup, for each square: -1. Compute mask (pseudo-attacks minus edges) -2. Enumerate all subsets of the mask -3. Compute true attacks via ray-walking (reference) -4. Find magic number via PRNG search - -### Usage -```c -// Move generation -bitboard_t attacks = get_rook_attacks(from_square, all_occupied); -attacks &= ~friendly_pieces; - -// Attack detection -bool square_attacked(...) { - if (get_rook_attacks(sq, occ) & (enemy_rooks | enemy_queens)) return true; - if (get_bishop_attacks(sq, occ) & (enemy_bishops | enemy_queens)) return true; - // ... -} -``` - -## Implementation Order -1. Fix bugs (Phase 1) -2. Add `square_attacked()` with ray-walking -3. Add legal move filtering -4. Fix castling legality -5. Verify perft d0-d7 -6. Uncomment + verify Kiwipete perft -7. Add magic bitboards (Phase 4) diff --git a/src/engine/moves.c b/src/engine/moves.c index 0c923a1..6f96109 100644 --- a/src/engine/moves.c +++ b/src/engine/moves.c @@ -76,7 +76,7 @@ int find_piece_on_square(position_t* p, int square) { if ((p->bitboards[BLACK_BISHOP] >> square) & 1) return BLACK_BISHOP; if ((p->bitboards[BLACK_KNIGHT] >> square) & 1) return BLACK_KNIGHT; if ((p->bitboards[BLACK_PAWN] >> square) & 1) return BLACK_PAWN; - assert(0); + assert(0 && "Failed to find the piece on the square"); } void position_make_move(position_t* position, move_t move) { @@ -140,24 +140,40 @@ void position_make_move(position_t* position, move_t move) { } if (move.flags & MOVE_PROMOTE_Q) { + if (move.flags & MOVE_CAPTURE) { + int to_remove_piece_type = find_piece_on_square(position, move.to); + position->bitboards[to_remove_piece_type] &= ~((bitboard_t)1 << move.to); + } int q_type = position->turn == WHITE_TURN ? WHITE_QUEEN : BLACK_QUEEN; position->bitboards[q_type] |= (bitboard_t)1 << move.to; position->turn = !position->turn; return; } if (move.flags & MOVE_PROMOTE_R) { + if (move.flags & MOVE_CAPTURE) { + int to_remove_piece_type = find_piece_on_square(position, move.to); + position->bitboards[to_remove_piece_type] &= ~((bitboard_t)1 << move.to); + } int q_type = position->turn == WHITE_TURN ? WHITE_ROOK : BLACK_ROOK; position->bitboards[q_type] |= (bitboard_t)1 << move.to; position->turn = !position->turn; return; } if (move.flags & MOVE_PROMOTE_B) { + if (move.flags & MOVE_CAPTURE) { + int to_remove_piece_type = find_piece_on_square(position, move.to); + position->bitboards[to_remove_piece_type] &= ~((bitboard_t)1 << move.to); + } int q_type = position->turn == WHITE_TURN ? WHITE_BISHOP : BLACK_BISHOP; position->bitboards[q_type] |= (bitboard_t)1 << move.to; position->turn = !position->turn; return; } if (move.flags & MOVE_PROMOTE_N) { + if (move.flags & MOVE_CAPTURE) { + int to_remove_piece_type = find_piece_on_square(position, move.to); + position->bitboards[to_remove_piece_type] &= ~((bitboard_t)1 << move.to); + } int q_type = position->turn == WHITE_TURN ? WHITE_KNIGHT : BLACK_KNIGHT; position->bitboards[q_type] |= (bitboard_t)1 << move.to; position->turn = !position->turn; @@ -206,6 +222,7 @@ int count_positions(position_t position, int depth) { moves_t moves; moves_init(&moves); get_legal_moves(&moves, position); + int count = 0; for (int i = 0; i < moves.length; i++) { position_t copy = position; @@ -221,6 +238,7 @@ void perft_divide(position_t position, int depth) { moves_t moves; moves_init(&moves); get_legal_moves(&moves, position); + int nodes = 0; for (int i = 0; i < moves.length; i++) { position_t copy = position; @@ -281,12 +299,11 @@ bool test_perft_position3() { d = count_positions(position, 3); if (d != 2812) return false; d = count_positions(position, 4); if (d != 43238) return false; d = count_positions(position, 5); if (d != 674624) return false; - perft_divide(position, 6); - d = count_positions(position, 6); - printf("%d\n", d); - if (d != 11030083) return false; - d = count_positions(position, 7); if (d != 178633661) return false; - d = count_positions(position, 8); if (d != 3009794393) return false; + d = count_positions(position, 6); if (d != 11030083) return false; + + // my move gen too slow to verify this + // d = count_positions(position, 7); if (d != 178633661) return false; + // d = count_positions(position, 8); if (d != 3009794393) return false; return true; } diff --git a/src/uci/response.c b/src/uci/response.c index da46cb7..ff89f8d 100644 --- a/src/uci/response.c +++ b/src/uci/response.c @@ -78,7 +78,7 @@ void handle_idle(uci_state_t *state, uci_cmd_t cmd) { return; } state->position = fen.position; - } else { assert(0); } + } else { assert(0 && "Invalid argument for position command"); } if (cmd.args_count >= k) return; assert(strcmp(uci_cmd_get_arg(cmd, k++), "moves") == 0); diff --git a/src/uci/state.c b/src/uci/state.c index ac043ed..012d951 100644 --- a/src/uci/state.c +++ b/src/uci/state.c @@ -104,7 +104,7 @@ void print_setting(option_setting_t setting) { printf("string default %s\n", setting.data.string_default); } else if (OPTION_BUTTON == setting.type) { printf("button\n"); - } else { assert(0); } + } else { assert(0 && "Failed to print the options"); } } void uci_state_init(uci_state_t *state) { @@ -207,7 +207,7 @@ void apply_option(uci_state_t *state, char *name, char *buffer) { memcpy(setting.value.string->data, buffer, setting.value.string->length); } else if (OPTION_BUTTON == setting.type) { *setting.value.button = true; - } else { assert(0); } + } else { assert(0 && "Failed to apply the option"); } memcpy(state->option_settings + i, &setting, sizeof(setting)); return; |
