Skip to content

Improve attack generation verification and Chess960 test structure#58

Open
winapiadmin wants to merge 6 commits into
mainfrom
initialization_order_fix
Open

Improve attack generation verification and Chess960 test structure#58
winapiadmin wants to merge 6 commits into
mainfrom
initialization_order_fix

Conversation

@winapiadmin
Copy link
Copy Markdown
Owner

No description provided.

@winapiadmin winapiadmin linked an issue May 10, 2026 that may be closed by this pull request
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 10, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8b018770-cd3e-4cf0-8fd5-73c3e450649a

📥 Commits

Reviewing files that changed from the base of the PR and between 6faa8b6 and acebcbb.

📒 Files selected for processing (1)
  • tests.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests.cpp

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added arithmetic operators for rank and file to support direction-based moves.
  • Tests

    • Added randomized validation for sliding-piece attack generation.
    • Improved perft test selection by grouping and sampling representative cases.
  • Refactor

    • Sliding-piece attack lookup updated to support both compile-time and runtime modes and adjusted public call behavior and linkage semantics.

Walkthrough

This PR moves bishop/rook slider implementations out of the header, changes _POSSIBLY_CONSTEXPR so precomputed table linkage differs under GENERATE_AT_RUNTIME, adds Rank/File Direction operators, and introduces randomized attack verification plus a debug perft testcase splitter.

Changes

Attack API and Coordinate Refactoring

Layer / File(s) Summary
Coordinate Operators
types.h
Adds constexpr operator+, operator-, operator+=, operator-= for Rank and File with Direction.
Header API Transformation
attacks.h
Adds #include <vector>; removes extern const magic/table/attacks declarations; replaces inline constexpr bishop()/rook() with non-constexpr forward declarations; makes queen()/slider<> inline wrappers that call external functions.
Attack Implementation
attacks.cpp
Implements attacks::bishop() and attacks::rook() that index precomputed magic tables. _POSSIBLY_CONSTEXPR is empty under GENERATE_AT_RUNTIME (was const), altering data linkage/constness. Minor whitespace change near namespace end.
Tests and Perft Helpers
tests.cpp
Adds <random>, sliderAttacks reference helper, randomized TEST_CASE("attacks") validating bishop/rook against reference, debug-only split_testcases() to bucket perft tests, and changes check_perfts() to accept and filter a mutable vector in non-release builds.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I nibble on ranks and files with care,

I hop through directions, swift and fair,
Tables moved out to roam and roam,
Random tests wander bits back home,
A rabbit cheers: the attacks come home.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to assess whether the description relates to the changeset. Add a pull request description explaining the changes, their rationale, and any relevant context for reviewers.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Improve attack generation verification and Chess960 test structure' accurately summarizes the main changes: adding attack verification tests and restructuring test logic.
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 initialization_order_fix

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.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3f37b627ca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tests.cpp
std::cerr << "(skipped)\n";
continue;
}
entries = split_testcases(entries);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore the debug perft cap

In a default CMake test build (empty CMAKE_BUILD_TYPE, so NDEBUG is not defined and IS_RELEASE is 0), this replaces the old > 1e6 skip with split_testcases, which keeps 30 cases up to 10M nodes and five cases up to 1B nodes. With the current test data that is about 2.7B expected nodes per piece mapping before the loop repeats for the three piece representations, so test_normal can run for an impractical amount of time or time out instead of remaining a quick debug check; keep the cap or bound the sampled buckets to debug-sized cases.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
attacks.h (1)

326-335: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

slider<>() is marked constexpr but unconditionally calls non-constexpr functions.

After this PR, bishop() and rook() are plain non-constexpr declarations, and queen() is inline (not constexpr), yet slider<>() retains the constexpr qualifier at line 326. The function cannot be evaluated as a constant expression; the constexpr contract is false. Change to inline to reflect the actual behavior.

Suggested change
-template <PieceType pt> [[nodiscard]] constexpr Bitboard slider(Square sq, Bitboard occupied) {
+template <PieceType pt> [[nodiscard]] inline Bitboard slider(Square sq, Bitboard occupied) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@attacks.h` around lines 326 - 335, The template function slider<PieceType> is
declared constexpr but calls non-constexpr functions bishop(), rook(), and
queen(), so change its declaration to inline (remove constexpr) to accurately
reflect runtime evaluation; update the signature of slider (template <PieceType
pt> inline Bitboard slider(Square sq, Bitboard occupied)) and keep the existing
if constexpr branches that call bishop, rook, and queen.
🧹 Nitpick comments (4)
attacks.cpp (1)

93-186: 💤 Low value

Encapsulate runtime-generated magic data with internal linkage.

When GENERATE_AT_RUNTIME is defined, _POSSIBLY_CONSTEXPR collapses to nothing, so RookMagics, BishopMagics, bishopData, BishopTable, BishopAttacks, rookData, RookTable, and RookAttacks all become mutable namespace-scope globals with external linkage. Since the header no longer exposes them (they’re strictly an implementation detail behind bishop()/rook()), wrapping them in an anonymous namespace (or marking them static) tightens the contract and prevents accidental external references at link time.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@attacks.cpp` around lines 93 - 186, The runtime-generated magic arrays and
tables (RookMagics, BishopMagics, bishopData, BishopTable, BishopAttacks,
rookData, RookTable, RookAttacks) currently have external linkage when
GENERATE_AT_RUNTIME is defined; wrap these symbols in an anonymous namespace (or
mark them static) so they get internal linkage, e.g., enclose the
declarations/definitions of RookMagics, BishopMagics and the results from
generate_magic_table() (bishopData/rookData and the derived
BishopTable/BishopAttacks/RookTable/RookAttacks) in an anonymous namespace block
to prevent accidental external linkage.
tests.cpp (2)

388-404: ⚡ Quick win

Use a deterministic seed so failures are reproducible.

std::random_device makes this test irreproducible across CI runs — when an assertion fails (e.g., on a regression in attacks::bishop/attacks::rook), the failing occupancy bitboard cannot be recovered to investigate. Seed mt19937_64 with a fixed constant and (optionally) log the seed and current bb on assertion to make triage tractable. Edge cases worth covering deterministically: occupied == 0, occupied == ~0ULL, and a single blocker on every ray.

♻️ Suggested change
-TEST_CASE("attacks") {
-    std::random_device rd;
-    std::mt19937_64 gen(rd());
-    std::uniform_int_distribution<uint64_t> dis;
-    for (Square sq = SQ_A1; sq < SQ_NONE; sq++) {
-        for (int i = 0; i < 10000; i++) {
-            uint64_t bb = dis(gen) & dis(gen);
-            REQUIRE(attacks::bishop(sq, bb) == sliderAttacks<false>(sq, bb));
-        }
-    }
-    for (Square sq = SQ_A1; sq < SQ_NONE; sq++) {
-        for (int i = 0; i < 10000; i++) {
-            uint64_t bb = dis(gen) & dis(gen);
-            REQUIRE(attacks::rook(sq, bb) == sliderAttacks<true>(sq, bb));
-        }
-    }
-}
+TEST_CASE("attacks") {
+    constexpr uint64_t kSeed = 0xC0DERABB17ULL;
+    std::mt19937_64 gen(kSeed);
+    std::uniform_int_distribution<uint64_t> dis;
+    for (Square sq = SQ_A1; sq < SQ_NONE; sq++) {
+        // deterministic edge cases first
+        REQUIRE(attacks::bishop(sq, 0ULL) == sliderAttacks<false>(sq, 0ULL));
+        REQUIRE(attacks::bishop(sq, ~0ULL) == sliderAttacks<false>(sq, ~0ULL));
+        REQUIRE(attacks::rook(sq, 0ULL) == sliderAttacks<true>(sq, 0ULL));
+        REQUIRE(attacks::rook(sq, ~0ULL) == sliderAttacks<true>(sq, ~0ULL));
+        for (int i = 0; i < 10000; i++) {
+            uint64_t bb = dis(gen) & dis(gen);
+            REQUIRE(attacks::bishop(sq, bb) == sliderAttacks<false>(sq, bb));
+            REQUIRE(attacks::rook(sq, bb) == sliderAttacks<true>(sq, bb));
+        }
+    }
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests.cpp` around lines 388 - 404, Replace the non-deterministic
random_device seed in the TEST_CASE("attacks") block with a fixed constant seed
for std::mt19937_64 (e.g. a literal uint64_t) so tests are reproducible;
additionally add deterministic checks for occupied==0, occupied==~0ULL and a
loop that constructs a single-blocker occupancy on every ray for each Square to
compare attacks::bishop/attacks::rook with sliderAttacks, and when calling
REQUIRE include/log the seed and the failing bb (occupancy) so failures can be
reproduced and triaged; update references in this test to use the fixed seed
generator (gen) and ensure both bishop and rook loops use the same deterministic
inputs.

264-272: 💤 Low value

Entries with > 1,000,000,000 nodes are silently dropped.

The bucket cascade ends at <= 1'000'000'000, so any future test case exceeding 1B nodes will be filtered out without warning rather than landing in a "huge" bucket. Today no entry breaches that threshold (max in the suite is ~525M), but it's a quiet footgun for future contributors. Either widen the last bucket to a sentinel (e.g., omit the upper bound) or add a comment documenting the deliberate cap.

♻️ Suggested change
-        else if (e.info.nodes <= 1'000'000'000)
-            bucket3.push_back(e);
+        else
+            bucket3.push_back(e); // anything above 10M lands here, including very heavy positions
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests.cpp` around lines 264 - 272, The current cascade over entries silently
drops any TestEntry whose e.info.nodes > 1'000'000'000 because the last
condition is <= 1'000'000'000; change the final branch to a no-upper-bound
catch-all (replace "else if (e.info.nodes <= 1'000'000'000)
bucket3.push_back(e);" with a plain "else bucket3.push_back(e);") or explicitly
add a fourth "huge" bucket and push those entries there, and add a brief comment
near bucket3/bucket4 explaining the chosen sentinel handling so future tests >1B
nodes are not lost.
types.h (1)

193-200: 💤 Low value

Direction values applied to Rank/File can yield out-of-domain results.

Direction is a square-step encoding (NORTH = 8, SOUTH = -8, etc.), not an axis-step. Adding NORTH to a Rank (or File) produces Rank(r + 8), which is nonsensical and silently leaves the [0,7] domain. The only current caller in tests.cpp::sliderAttacks happens to pass only EAST/WEST/DIR_NONE, whose numeric values (±1, 0) coincidentally work as axis offsets, but the type-level contract is misleading and easy to misuse going forward.

Consider asserting validity (or constraining with a separate axis-offset type) so that misuse is caught instead of silently propagating invalid values.

🛡️ Optional: assert valid 1-step axis usage
-constexpr Rank operator+(Rank s, Direction d) { return Rank(int(s) + int(d)); }
-constexpr Rank operator-(Rank s, Direction d) { return Rank(int(s) - int(d)); }
+constexpr Rank operator+(Rank s, Direction d) {
+    assert(d == EAST || d == WEST || d == DIR_NONE);
+    return Rank(int(s) + int(d));
+}
+constexpr Rank operator-(Rank s, Direction d) {
+    assert(d == EAST || d == WEST || d == DIR_NONE);
+    return Rank(int(s) - int(d));
+}

(Same for File and the compound forms.)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@types.h` around lines 193 - 200, The Rank/File arithmetic operators
(constexpr operator+, operator-, operator+=, operator-= for Rank and File)
accept Direction which is a square-step (e.g. NORTH=8) and can produce
out-of-domain values; change these operators so they either assert the Direction
is a 1-step axis offset (check that int(d) is -1,0,1) or replace the parameter
type with a dedicated AxisOffset/AxisStep type (e.g. AxisOffset) and update
callers (tests.cpp::sliderAttacks) accordingly; ensure all four operator
overloads for Rank and File perform the validation or use the new type so
invalid additions like +NORTH/+SOUTH are caught rather than silently creating
invalid Rank/File values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@attacks.h`:
- Around line 326-335: The template function slider<PieceType> is declared
constexpr but calls non-constexpr functions bishop(), rook(), and queen(), so
change its declaration to inline (remove constexpr) to accurately reflect
runtime evaluation; update the signature of slider (template <PieceType pt>
inline Bitboard slider(Square sq, Bitboard occupied)) and keep the existing if
constexpr branches that call bishop, rook, and queen.

---

Nitpick comments:
In `@attacks.cpp`:
- Around line 93-186: The runtime-generated magic arrays and tables (RookMagics,
BishopMagics, bishopData, BishopTable, BishopAttacks, rookData, RookTable,
RookAttacks) currently have external linkage when GENERATE_AT_RUNTIME is
defined; wrap these symbols in an anonymous namespace (or mark them static) so
they get internal linkage, e.g., enclose the declarations/definitions of
RookMagics, BishopMagics and the results from generate_magic_table()
(bishopData/rookData and the derived
BishopTable/BishopAttacks/RookTable/RookAttacks) in an anonymous namespace block
to prevent accidental external linkage.

In `@tests.cpp`:
- Around line 388-404: Replace the non-deterministic random_device seed in the
TEST_CASE("attacks") block with a fixed constant seed for std::mt19937_64 (e.g.
a literal uint64_t) so tests are reproducible; additionally add deterministic
checks for occupied==0, occupied==~0ULL and a loop that constructs a
single-blocker occupancy on every ray for each Square to compare
attacks::bishop/attacks::rook with sliderAttacks, and when calling REQUIRE
include/log the seed and the failing bb (occupancy) so failures can be
reproduced and triaged; update references in this test to use the fixed seed
generator (gen) and ensure both bishop and rook loops use the same deterministic
inputs.
- Around line 264-272: The current cascade over entries silently drops any
TestEntry whose e.info.nodes > 1'000'000'000 because the last condition is <=
1'000'000'000; change the final branch to a no-upper-bound catch-all (replace
"else if (e.info.nodes <= 1'000'000'000) bucket3.push_back(e);" with a plain
"else bucket3.push_back(e);") or explicitly add a fourth "huge" bucket and push
those entries there, and add a brief comment near bucket3/bucket4 explaining the
chosen sentinel handling so future tests >1B nodes are not lost.

In `@types.h`:
- Around line 193-200: The Rank/File arithmetic operators (constexpr operator+,
operator-, operator+=, operator-= for Rank and File) accept Direction which is a
square-step (e.g. NORTH=8) and can produce out-of-domain values; change these
operators so they either assert the Direction is a 1-step axis offset (check
that int(d) is -1,0,1) or replace the parameter type with a dedicated
AxisOffset/AxisStep type (e.g. AxisOffset) and update callers
(tests.cpp::sliderAttacks) accordingly; ensure all four operator overloads for
Rank and File perform the validation or use the new type so invalid additions
like +NORTH/+SOUTH are caught rather than silently creating invalid Rank/File
values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b749e216-230a-4da0-b92a-d6f7cbdab810

📥 Commits

Reviewing files that changed from the base of the PR and between 26cd9b4 and 3f37b62.

📒 Files selected for processing (5)
  • attacks.cpp
  • attacks.h
  • chess960_tests.cpp
  • tests.cpp
  • types.h
💤 Files with no reviewable changes (1)
  • chess960_tests.cpp

Reduce the number of elements taken from bucket3 from 5 to 1.
Reduce the limits for bucket2 and bucket3 sizes in optimization.
Replaced random device initialization with a fixed seed for reproducibility in tests.
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.

Initialization order... or something.

1 participant