Skip to content

Fix/engine correctness and performance#1

Merged
ujandey merged 2 commits into
mainfrom
fix/engine-correctness-and-performance
May 6, 2026
Merged

Fix/engine correctness and performance#1
ujandey merged 2 commits into
mainfrom
fix/engine-correctness-and-performance

Conversation

@ujandey

@ujandey ujandey commented May 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fix 1 (LMR): Single null-window search with conditional full-depth re-search only when reduced or score < beta; eliminates the double full-depth re-search bug.
  • Fix 2 (fast eval): New _evaluate_material_fast() — O(64) material+PST+bishop-pair only — for quiescence stand_pat; avoids slow king-safety/pawn-map eval on every capture node.
  • Fix 3 (adaptive timeout): _timeout_mask tightened from 2047→255 for time controls < 100 ms so short moves don't overshoot their budget.
  • Fix 4 (TT mate scores): _score_to_tt / _score_from_tt encode/decode mate scores relative to the storing ply so retrieved values remain correct at other plies.
  • Fix 5 (two-pass legal moves): generate_all_legal_moves / get_legal_moves now set board.turn to match the requested side via try/finally, removing the stale-turn bug that caused wrong move counts when called out of turn; two-pass refresh replaces the unbounded recursive retry.
  • Fix 6 (in-place sort): order_moves uses list.sort() instead of sorted(), eliminating a full list copy per negamax node.
  • Fix 7 (perft try/finally): push/pop in perft wrapped in try/finally so board state is always restored even on exception.
  • Fix 8 (_extract_pv): Removed ValueError from the suppressed-exception tuple so real bugs surface instead of silently truncating the PV.
  • Fix 9 (en passant undo): Restructured board.undo_move to set the destination square to "." before restoring the captured pawn, closing a window where piece_positions temporarily held a phantom piece.

Test changes

  • test_perft.py: corrected kiwipete FEN to standard CPW (no c5 pawn, D2=2039 D3=97862); replaced broken promotions FEN with classic CPW Position 5 (n1n5/PPPk4/..., D1=24 D2=496 D3=9483); added depth-4 for startpos and endgame_ep.
  • test_state_integrity.py (new): 5 tests verifying full board-state restore after push/pop, timeout, stop flag, and UCI stop, plus generate_all_legal_moves side-independence.
  • test_notation_pgn_uci.py: relaxed go-background timing assertion 50 ms→500 ms for Windows thread-spawn overhead.

Test plan

  • pytest -q — all 97 tests pass (perft depth-4 included)
  • Kiwipete D1=48, D2=2039, D3=97862 verified against CPW reference
  • CPW Position 5 D1=24, D2=496, D3=9483 verified against CPW reference
  • All 5 state-integrity tests pass

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Fixed en passant move undoing to correctly restore game state and captured pieces.
  • Performance & Stability

    • Improved move generation accuracy and search robustness to enhance overall engine quality and game strength.
    • Optimized time management and evaluation speed for better performance under tight time controls.
    • Strengthened error recovery and state handling to ensure greater engine stability during searches.

ujandey and others added 2 commits May 7, 2026 00:07
Fix 1 (LMR): single null-window search + conditional full-depth re-search
when reduced or score < beta, avoiding duplicate re-search on cutoffs.

Fix 2 (fast eval): add _evaluate_material_fast() — O(64) material+PST+bishop
pair only — for quiescence stand_pat; full eval kept for depth-0 nodes.

Fix 3 (adaptive timeout): introduce _timeout_mask; tighten to 255 nodes for
time controls < 100 ms (was always 2047) so short moves don't overrun budget.

Fix 4 (TT mate scores): add _score_to_tt/_score_from_tt ply-distance encoding
so stored mate-in-N values remain correct when retrieved at different plies.

Fix 5 (two-pass generate_all_legal_moves): replace recursive refresh with a
non-recursive two-pass approach; always set board.turn to match the requested
side inside generate_all_legal_moves and get_legal_moves via try/finally.

Fix 6 (in-place sort): order_moves now calls moves.sort() instead of sorted(),
eliminating a full list copy on every negamax node.

Fix 7 (perft try/finally): wrap push/perft/pop in try/finally so board state
is always restored even if perft raises.

Fix 8 (_extract_pv): remove ValueError from suppressed exceptions so real bugs
surface instead of silently truncating the PV.

Fix 9 (en passant undo): restructure board.undo_move so destination square is
set to "." before restoring the captured pawn when undoing en passant, removing
a window where piece_positions contained a phantom piece.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
test_perft.py: use standard kiwipete FEN (no c5 pawn, matches CPW reference
values D2=2039, D3=97862); replace broken promotions position with the classic
CPW Position 5 (n1n5/PPPk4/8/...) whose D1/D2/D3 values are well-known;
add depth-4 cases for startpos (197281) and endgame_ep (43238).

test_state_integrity.py (new): 5 tests verifying board state is fully restored
after push/pop, search timeout, stop flag, and UCI stop; and that
generate_all_legal_moves honours the requested side regardless of board.turn.

test_notation_pgn_uci.py: relax go-runs-in-background timing assertion from
50 ms to 500 ms to accommodate Windows thread-spawn overhead without losing
the non-blocking guarantee.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR enhances the chess engine's robustness and performance through three interconnected improvements: corrected en passant undo logic in the board state manager, significant refactoring of the move generator to add time management, TT integration with mate scoring, fast material evaluation, and defensive state guards, and expanded test coverage validating state integrity across search and board operations.

Changes

Board State and Search Engine Improvements

Layer / File(s) Summary
Board State Fix
engine/board.py
undo_move now correctly handles en passant captures by restoring the captured piece to its original square (not the end square) and clearing the end square, ensuring proper state restoration.
Core Move Generation and Search
engine/move_generator.py
Major refactoring: get_legal_moves now verifies pseudo-legal moves via make_move/undo_move; generate_all_legal_moves refreshes zobrist hash on stale state; perft and perft_divide wrapped in try/finally guards; order_moves sorts in place; new _score_to_tt and _score_from_tt encode/decode mate distances for transposition table storage; _evaluate_material_fast replaces full evaluation in quiescence stand-pat; negamax updated to use TT encoding/decoding and adds late-move reductions; timeout polling made configurable via _timeout_mask.
Exception Handling
engine/move_generator.py
_extract_pv exception handling widened to catch IndexError, KeyError, and TypeError for robustness.
Test Coverage—Timing Adjustment
tests/test_notation_pgn_uci.py
Tolerance in test_go_runs_in_background_and_stop_finishes_bestmove increased from 0.05s to 0.5s to account for slower execution.
Test Coverage—Perft Data
tests/test_perft.py
Test expectations expanded: startpos and endgame_ep now include depth-4 node counts; kiwipete updated with corrected FEN and depth 2–3 expectations; new promotions test case added with depths 1–3.
Test Coverage—State Integrity
tests/test_state_integrity.py
New comprehensive module introducing snapshot(board) helper and SearchStateIntegrityTests class verifying board state restoration after timeouts, stop signals, and fuzzing; UciStopStateIntegrityTests verifying position consistency after UCI stop sequences.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 With en passant fixed and search so bright,
Our moves now legal, states set right—
Fast evals dash through quiescence's night,
TT scores dance in encoded light,
While fuzzy tests guard logic's might! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title "Fix/engine correctness and performance" is overly broad and vague, using generic descriptors that don't clearly convey the specific nature of the changes despite the PR involving multiple targeted fixes. Revise the title to be more specific, such as "Fix en passant undo and improve move generation correctness" or "Improve engine correctness with faster evaluation and TT handling" to better reflect the primary changes.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/engine-correctness-and-performance

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ujandey ujandey merged commit 2fa0f04 into main May 6, 2026
4 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant