summaryrefslogtreecommitdiff
path: root/Plan.md
diff options
context:
space:
mode:
authorAargh Rai <aargh.rai+git@gmail.com>2026-07-16 14:08:24 +0530
committerAargh Rai <aargh.rai+git@gmail.com>2026-07-16 14:08:24 +0530
commit1a52152852ff29c727673fc80e8bf8eca5ed6365 (patch)
treee719771aa1b23f0b8774814ee907057f86f1dda1 /Plan.md
parent6ac448727e1e26c78055eaaa361217f64393d808 (diff)
starting pos, move count, test pass
Diffstat (limited to 'Plan.md')
-rw-r--r--Plan.md100
1 files changed, 100 insertions, 0 deletions
diff --git a/Plan.md b/Plan.md
new file mode 100644
index 0000000..a300420
--- /dev/null
+++ b/Plan.md
@@ -0,0 +1,100 @@
+# 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)