Skip to content

Conversation

@jacksonthall22
Copy link
Member

@jacksonthall22 jacksonthall22 commented Oct 27, 2025

Summary by CodeRabbit

  • New Features

    • Restructured project as a reusable library with public API for generating SAN (Standard Algebraic Notation) chess move strings.
    • Added support for generating spoken-phrase variants of SAN notation.
    • Integrated external chess action-space module as a dependency.
  • Documentation

    • Expanded README with detailed explanations of SAN generation logic, discriminators, and examples.
  • Chores

    • Added project configuration with dependencies and build tooling.
    • Updated gitignore for development environments.

@coderabbitai
Copy link

coderabbitai bot commented Oct 27, 2025

📝 Walkthrough

Walkthrough

The PR restructures a chess SAN string generation project by migrating root-level Python scripts into a proper package under src/san_strings/, adding a git submodule for chess_action_space, introducing utility modules for move validation and SAN parsing, and configuring the project with pyproject.toml. Existing root-level generation scripts are removed and relocated.

Changes

Cohort / File(s) Summary
Project Configuration
.gitignore, .gitmodules, pyproject.toml, src/chess_action_space
Added IDE and virtual environment directories to gitignore; introduced chess_action_space git submodule; added Python project configuration with dependencies (chess, chess_action_space), dev tooling (uv, Ruff, Mypy), and build backend setup.
Documentation
README.md
Updated project description to reference 29,064 SAN strings (from 29,274); added introductory paragraphs explaining the small library's purpose; expanded "How it works" and "SAN generation algorithms" sections with detailed explanations, examples, and pseudocode for discriminators (file, rank, full-square); added notes on completeness and last update.
Deleted Root Scripts
gen_san_strings.py, gen_spoken_san_strings.py
Removed original Python scripts that generated SAN strings and converted them to spoken phrases; logic relocated to new package structure.
Core Package Module
src/san_strings/__init__.py
Introduced primary module with public functions: gen_sans_for_move() generating SAN variants for moves with piece-type filtering, gen_sans() aggregating all SANs, write_output_files() producing output files; includes move categorization logic for pawns, kings, and non-pawn pieces with disambiguation handling.
Chess Constants
src/san_strings/constants.py
Added chess-related constants: square sets (CORNER_SQUARES, EDGE_SQUARES), slider groupings (DIAGONAL_SLIDERS, ORTHOGONAL_SLIDERS), promotion piece definitions, capturable piece types, and precompiled regex patterns for parsing pawn and non-pawn SAN moves.
Spoken SAN Generation
src/san_strings/gen_spoken_san_strings.py
Relocated script that reads san_strings.txt, parses SAN strings into natural-language variants (handling castling, piece names, captures, promotions), generates combinations of phrase alternatives, and writes sorted results to spoken_san_strings.txt.
SAN Output Data
src/san_strings/san_strings_with_symbols.txt
Removed all plus-sign variants (check+, mate+ combinations) from entries, retaining only base forms with check or mate symbols.
Move Validation Utilities
src/san_strings/utils/__init__.py
Added comprehensive utility functions: piece-type classifiers (is_diagonal_slider, is_orthogonal_slider, is_slider), linguistic helper (ordinal), bitboard-based attack computation (attacked_by_mask), mask utilities, sliding ray helpers (extend_ray, extend_ray_away), move validation predicates (can_be_pawn_move, can_be_knight_move, etc.), and disambiguation checkers (can_require_file_disambiguator, can_require_rank_disambiguator, can_require_full_square_disambiguator).
SAN Parsing Module
src/san_strings/utils/san_parts.py
Introduced abstract base class SanParts and concrete implementations PawnSanParts, NBRQSanParts, KingSanParts for parsing and validating SAN notation; provides factory method from_san(), board position iteration, and check/checkmate determination; validates SANs by reconstructing candidate positions and cross-checking against actual moves.

Sequence Diagram(s)

sequenceDiagram
    participant main as __main__
    participant gen_sans as gen_sans()
    participant iter_space as iter_action_space
    participant per_move as gen_sans_for_move()
    participant out as write_output_files()
    participant files as Output Files

    main->>out: write_output_files()
    out->>gen_sans: gen_sans()
    gen_sans->>iter_space: iterate all moves
    iter_space-->>gen_sans: move
    gen_sans->>per_move: gen_sans_for_move(move)
    per_move->>per_move: classify move type<br/>(pawn/king/nbrq)
    per_move->>per_move: generate base SAN
    per_move->>per_move: add disambiguators<br/>(file/rank/full)
    per_move->>per_move: add check/mate suffixes
    per_move-->>gen_sans: set of SAN strings
    gen_sans-->>out: aggregated SANs
    out->>out: sort by length<br/>then lexicographically
    out->>files: san_strings.txt<br/>(base SANs only)
    out->>files: san_strings_with_symbols.txt<br/>(with check/mate)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Areas requiring extra attention:

  • src/san_strings/utils/san_parts.py: Core SAN parsing logic with abstract base class, multiple concrete implementations, and position reconstruction validation. Ensure move generation, discriminator logic, and check/mate determination are sound.
  • src/san_strings/utils/__init__.py: Dense bitboard-based utilities and move validation predicates. Verify correctness of attack masks, sliding ray computations, and piece-specific move constraints.
  • src/san_strings/__init__.py: Move categorization, disambiguation rule application, and aggregation logic. Cross-check with utilities to ensure consistent validation.
  • README.md: Substantive documentation changes; verify technical accuracy of algorithm descriptions and discriminator explanations.
  • Submodule integration: chess_action_space introduction via .gitmodules and dependency in pyproject.toml; confirm proper initialization and import paths.

Poem

🐰 A rabbit hops through code so neat,
SAN strings now organized, complete!
From root to src, the scripts have moved,
New utils carved, and logic proved—
With pawns, kings, queens in perfect rows, 🎲
The action space arithmetically flows!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Feat/brute force solution' is vague and generic, using non-descriptive terms that don't convey meaningful information about the specific changes in the changeset. Replace with a more descriptive title that specifically indicates the main change, such as 'Refactor SAN generation to use brute force approach with submodule' or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/brute-force-solution

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.

@jacksonthall22 jacksonthall22 force-pushed the feat/brute-force-solution branch from e5c3ec3 to 572b2cb Compare October 27, 2025 02:31
@jacksonthall22 jacksonthall22 force-pushed the feat/brute-force-solution branch from dd66638 to 8df4dd7 Compare November 3, 2025 08:20
@jacksonthall22 jacksonthall22 linked an issue Nov 12, 2025 that may be closed by this pull request
@jacksonthall22 jacksonthall22 marked this pull request as ready for review November 12, 2025 22:17
Copy link

@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.

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
README.md (1)

19-24: Update README installation and function references—gen_san_strings.py no longer exists and referenced functions are not in the codebase.

Lines 19-24: The quickstart instructs pip install -r requirements.txt and python gen_san_strings.py, but:

  • gen_san_strings.py does not exist; the script is now src/san_strings/gen_spoken_san_strings.py
  • requirements.txt is outdated (lists only chess==1.9.1 while pyproject.toml requires chess>=1.11.2 and chess_action_space>=1.1.0)
  • Installation should use pip install . (or uv sync if using uv)

Lines 55-58: References to get_pawn_sans() and get_king_sans() functions in gen_san_strings.py are incorrect—these functions do not exist in the codebase. The gen_spoken_san_strings.py script is procedural, not function-based.

Update both sections to reflect the current package structure: use pyproject.toml for installation, reference the correct script location and name, and remove/correct the function references.

🧹 Nitpick comments (2)
.gitmodules (1)

1-3: Verify GitHub repository accessibility and dependency configuration consistency.

The submodule configuration is syntactically correct. However, ensure that:

  1. The repository at https://github.com/hyprchs/chess_action_space.git is accessible (public or properly authenticated)
  2. The dependency in pyproject.toml correctly references this submodule (e.g., with a path dependency or proper version constraint)
  3. Developer setup documentation exists for initializing the submodule after cloning

If this is a new submodule, consider adding a note in your project's setup documentation instructing developers to run git clone --recurse-submodules or git submodule update --init --recursive after pulling.

src/san_strings/gen_spoken_san_strings.py (1)

9-9: Drop the unused enumerate index.

Since the loop index isn’t used, you can drop enumerate (or rename the binding to _) to satisfy Ruff’s B007 warning.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d84d21f and 74b1194.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • .gitignore (1 hunks)
  • .gitmodules (1 hunks)
  • README.md (1 hunks)
  • gen_san_strings.py (0 hunks)
  • gen_spoken_san_strings.py (0 hunks)
  • pyproject.toml (1 hunks)
  • src/chess_action_space (1 hunks)
  • src/san_strings/__init__.py (1 hunks)
  • src/san_strings/constants.py (1 hunks)
  • src/san_strings/gen_spoken_san_strings.py (1 hunks)
  • src/san_strings/san_strings_with_symbols.txt (0 hunks)
  • src/san_strings/utils/__init__.py (1 hunks)
  • src/san_strings/utils/san_parts.py (1 hunks)
💤 Files with no reviewable changes (3)
  • gen_spoken_san_strings.py
  • src/san_strings/san_strings_with_symbols.txt
  • gen_san_strings.py
🧰 Additional context used
🧬 Code graph analysis (2)
src/san_strings/utils/san_parts.py (1)
src/san_strings/utils/__init__.py (6)
  • attacked_by_mask (37-80)
  • extend_ray (158-178)
  • is_diagonal_slider (16-17)
  • is_orthogonal_slider (20-21)
  • is_slider (24-25)
  • ordinal (28-34)
src/san_strings/__init__.py (2)
src/san_strings/utils/__init__.py (6)
  • can_be_king_move (328-360)
  • can_be_nbrq_move_by (281-302)
  • can_be_pawn_move (228-278)
  • can_require_file_disambiguator (363-385)
  • can_require_full_square_disambiguator (413-434)
  • can_require_rank_disambiguator (388-410)
src/san_strings/utils/san_parts.py (20)
  • KingSanParts (862-972)
  • NBRQSanParts (417-859)
  • PawnSanParts (210-414)
  • SanParts (28-207)
  • copy (206-207)
  • check_or_mate (71-72)
  • check_or_mate (75-78)
  • rendered (82-82)
  • rendered (259-265)
  • rendered (454-462)
  • rendered (878-884)
  • can_cause_check (168-176)
  • can_cause_checkmate (179-185)
  • to_square (67-68)
  • file_disambiguator (251-252)
  • file_disambiguator (446-447)
  • is_capture (63-64)
  • promotion_piece_type (255-256)
  • piece_type (59-60)
  • rank_disambiguator (450-451)
🪛 Ruff (0.14.4)
src/san_strings/gen_spoken_san_strings.py

9-9: Loop control variable i not used within loop body

Rename unused i to _i

(B007)


33-33: Avoid specifying long messages outside the exception class

(TRY003)

src/san_strings/utils/san_parts.py

35-37: Avoid specifying long messages outside the exception class

(TRY003)


77-77: Avoid specifying long messages outside the exception class

(TRY003)


150-152: Avoid specifying long messages outside the exception class

(TRY003)


160-162: Avoid specifying long messages outside the exception class

(TRY003)


201-201: Avoid specifying long messages outside the exception class

(TRY003)


222-224: Avoid specifying long messages outside the exception class

(TRY003)


232-234: Avoid specifying long messages outside the exception class

(TRY003)


238-240: Avoid specifying long messages outside the exception class

(TRY003)


271-271: Unused method argument: place_kings

(ARG002)


274-276: Avoid specifying long messages outside the exception class

(TRY003)


304-304: Avoid specifying long messages outside the exception class

(TRY003)


356-360: Avoid specifying long messages outside the exception class

(TRY003)


389-389: Avoid specifying long messages outside the exception class

(TRY003)


407-407: Avoid specifying long messages outside the exception class

(TRY003)


429-431: Avoid specifying long messages outside the exception class

(TRY003)


433-433: Avoid specifying long messages outside the exception class

(TRY003)


435-435: Avoid specifying long messages outside the exception class

(TRY003)


468-468: Unused method argument: place_kings

(ARG002)


471-473: Avoid specifying long messages outside the exception class

(TRY003)


499-504: Avoid specifying long messages outside the exception class

(TRY003)


518-522: Avoid specifying long messages outside the exception class

(TRY003)


561-565: Avoid specifying long messages outside the exception class

(TRY003)


584-588: Avoid specifying long messages outside the exception class

(TRY003)


622-626: Avoid specifying long messages outside the exception class

(TRY003)


650-655: Avoid specifying long messages outside the exception class

(TRY003)


691-695: Avoid specifying long messages outside the exception class

(TRY003)


719-723: Avoid specifying long messages outside the exception class

(TRY003)


727-729: Avoid specifying long messages outside the exception class

(TRY003)


825-825: Avoid specifying long messages outside the exception class

(TRY003)


847-847: Avoid specifying long messages outside the exception class

(TRY003)


849-851: Avoid specifying long messages outside the exception class

(TRY003)


890-890: Unused method argument: place_kings

(ARG002)


893-895: Avoid specifying long messages outside the exception class

(TRY003)


920-924: Avoid specifying long messages outside the exception class

(TRY003)


949-949: Avoid specifying long messages outside the exception class

(TRY003)


954-954: Avoid specifying long messages outside the exception class

(TRY003)


963-963: Avoid specifying long messages outside the exception class

(TRY003)


965-967: Avoid specifying long messages outside the exception class

(TRY003)

src/san_strings/__init__.py

39-39: Avoid specifying long messages outside the exception class

(TRY003)

src/san_strings/utils/__init__.py

206-206: Avoid specifying long messages outside the exception class

(TRY003)


265-265: Avoid specifying long messages outside the exception class

(TRY003)


287-290: Avoid specifying long messages outside the exception class

(TRY003)


302-302: Avoid specifying long messages outside the exception class

(TRY003)


356-358: Avoid specifying long messages outside the exception class

(TRY003)


369-369: Avoid specifying long messages outside the exception class

(TRY003)


371-371: Avoid specifying long messages outside the exception class

(TRY003)


394-394: Avoid specifying long messages outside the exception class

(TRY003)


396-396: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (3)
.gitignore (1)

3-4: Thanks for ignoring local environment artifacts.

Adding .idea/ and .venv/ keeps editor config and virtualenv clutter out of version control.

pyproject.toml (1)

1-40: Configuration looks consistent.

Good to see the project metadata, dependency pinning, and tool configs spelled out together; the local path override for chess_action_space should keep the submodule and package in sync.

src/san_strings/constants.py (1)

1-52: Constants and regexes look solid.

Nice to see the python-chess masks reused here; the pawn and non-pawn SAN patterns cover the expected disambiguators and suffixes cleanly.

@@ -0,0 +1 @@
Subproject commit 2631675d3a1c8bdae9781d520ac7d922d080abbc
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify submodule integration and document initialization requirements.

Adding a git submodule introduces coordination challenges that need proper documentation and dependency configuration. For projects with subpackages such as those with git submodules, documentation in a README file or tool configuration is required to clarify the steps needed.


🏁 Script executed:

#!/bin/bash
# Verify submodule configuration and documentation
set -e

echo "=== Checking .gitmodules ==="
if [ -f .gitmodules ]; then
  echo "✓ .gitmodules exists"
  cat .gitmodules
else
  echo "✗ .gitmodules not found"
  exit 1
fi

echo ""
echo "=== Checking pyproject.toml dependency declaration ==="
if grep -q "chess_action_space" pyproject.toml 2>/dev/null; then
  echo "✓ chess_action_space found in pyproject.toml"
  rg -A 2 "chess_action_space" pyproject.toml
else
  echo "✗ chess_action_space not declared in pyproject.toml"
fi

echo ""
echo "=== Checking README for submodule initialization docs ==="
if [ -f README.md ]; then
  if grep -qi "git clone.*recursive\|submodule\|chess_action_space" README.md; then
    echo "✓ README mentions submodule/initialization"
    rg -i "git clone.*recursive|submodule|chess_action_space" README.md
  else
    echo "⚠ README may not document submodule initialization"
  fi
else
  echo "⚠ README.md not found"
fi

echo ""
echo "=== Checking for setup scripts (setup.py or pre-install hooks) ==="
if [ -f setup.py ]; then
  echo "setup.py exists - checking for submodule initialization"
  rg -i "submodule|git.*init" setup.py
fi

if [ -f pyproject.toml ]; then
  if grep -q "\[build-system\]" pyproject.toml; then
    echo "pyproject.toml has build-system config"
  fi
fi

Length of output: 621



---

🏁 Script executed:

```shell
#!/bin/bash
# Get full context on dependencies and check for additional docs
set -e

echo "=== Full pyproject.toml dependencies context ==="
rg -B 3 -A 5 'chess_action_space' pyproject.toml

echo ""
echo "=== Checking for development/installation documentation ==="
for file in CONTRIBUTING.md INSTALL.md docs/INSTALL.md docs/setup.md docs/development.md docs/README.md; do
  if [ -f "$file" ]; then
    echo "Found: $file"
    if grep -qi "submodule\|git clone.*recursive\|chess_action_space" "$file"; then
      echo "  ✓ Contains submodule/setup guidance"
      rg -i "submodule|git clone.*recursive|chess_action_space" "$file"
    else
      echo "  ✗ Does not mention submodule setup"
    fi
  fi
done

echo ""
echo "=== Checking pyproject.toml build-system backend ==="
grep -A 3 "\[build-system\]" pyproject.toml || echo "No [build-system] found"

Length of output: 547



---

🏁 Script executed:

```shell
#!/bin/bash
# Final check: verify README exists and get its content to assess where/how to add docs
if [ -f README.md ]; then
  echo "=== README.md exists - checking length and structure ==="
  wc -l README.md
  echo ""
  echo "=== First 50 lines of README.md ==="
  head -50 README.md
  echo ""
  echo "=== Checking for Installation/Setup section ==="
  grep -n "## Installation\|## Setup\|## Getting Started\|## Development" README.md || echo "No standard setup section found"
else
  echo "README.md not found"
fi

Length of output: 3103


Update README installation instructions to document submodule initialization requirement.

The README's "Run it yourself" section now has outdated installation instructions that don't account for the git submodule dependency. Developers cloning without --recursive will get an empty src/chess_action_space/ directory and encounter broken imports or build failures without clear guidance.

Update the installation section to document:

  • Use git clone --recursive (or run git submodule update --init --recursive after cloning)
  • That chess_action_space is a required submodule dependency
🤖 Prompt for AI Agents
In src/chess_action_space around lines 1 to 1, the README installation
instructions are missing mention of the required git submodule; update the "Run
it yourself" section to instruct users to clone the repo with git clone
--recursive or run git submodule update --init --recursive after cloning, and
explicitly state that src/chess_action_space is a required submodule dependency
so developers know to initialize/update submodules before building.

Comment on lines +50 to +67
file = san_string[idx + 1]
rank = san_string[idx + 2]
assert file in 'abcdefgh'
assert rank in '12345678'
idx += 3

# Ex. 'rook takes' or 'rook takes e 4' or 'rook takes rook'
words.append(
('', f'{file} {rank}', 'pawn', 'knight', 'bishop', 'rook', 'queen')
)
elif c == '=':
words.append(('', 'equals'))
# break
else:
words.append(c)

idx += 1

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Captures drop everything after the destination square.

Inside the capture branch we do idx += 3, then the loop’s trailing idx += 1 fires. That means any suffix after the destination (promotion =Q, +, #, etc.) is skipped entirely. For example, exd8=Q never sees the = token, so the promotion wording is missing; Qxe4+ drops the check suffix. Please adjust the index math so we only skip the destination squares, not the rest of the SAN string.

Apply this fix:

-                idx += 3
+                idx += 2

With that change, captures still consume the destination square, and the following iteration will process promotion/check/mate markers correctly.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
file = san_string[idx + 1]
rank = san_string[idx + 2]
assert file in 'abcdefgh'
assert rank in '12345678'
idx += 3
# Ex. 'rook takes' or 'rook takes e 4' or 'rook takes rook'
words.append(
('', f'{file} {rank}', 'pawn', 'knight', 'bishop', 'rook', 'queen')
)
elif c == '=':
words.append(('', 'equals'))
# break
else:
words.append(c)
idx += 1
file = san_string[idx + 1]
rank = san_string[idx + 2]
assert file in 'abcdefgh'
assert rank in '12345678'
idx += 2
# Ex. 'rook takes' or 'rook takes e 4' or 'rook takes rook'
words.append(
('', f'{file} {rank}', 'pawn', 'knight', 'bishop', 'rook', 'queen')
)
elif c == '=':
words.append(('', 'equals'))
# break
else:
words.append(c)
idx += 1
🤖 Prompt for AI Agents
In src/san_strings/gen_spoken_san_strings.py around lines 50 to 67, the capture
branch currently does idx += 3 then the loop's trailing idx += 1 runs, causing
one extra character after the destination square to be skipped (dropping
promotion/check/mate markers). Change the capture branch so it only advances
past the destination square before the loop's trailing increment — replace idx
+= 3 with idx += 2 — ensuring the subsequent iteration will process any '=' '+',
'#' or other suffixes.

Comment on lines +256 to +263
if dr == 2:
# Must be white pawn move
if (from_rank, to_rank) != (1, 3):
return False
elif dr == -2:
# Must be black pawn move
if (from_rank, to_rank) != (6, 4):
return False
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix double-step pawn checks
Because dr is computed as from_rank - to_rank, white double pushes produce -2 and black double pushes produce +2. The current branches assume the opposite, so both (from_rank, to_rank) == (1, 3) and (6, 4) fail their guards and every two-square pawn advance (e.g., e4, ...e5) gets rejected. That prevents gen_sans_for_move from ever emitting SAN strings for double pawn pushes. Please flip the sign-specific branches so both colors pass their respective validation.

-        if dr == 2:
-            # Must be white pawn move
-            if (from_rank, to_rank) != (1, 3):
+        if dr == -2:
+            # Must be white pawn move
+            if (from_rank, to_rank) != (1, 3):
                 return False
-        elif dr == -2:
-            # Must be black pawn move
-            if (from_rank, to_rank) != (6, 4):
+        elif dr == 2:
+            # Must be black pawn move
+            if (from_rank, to_rank) != (6, 4):
                 return False
🤖 Prompt for AI Agents
In src/san_strings/utils/__init__.py around lines 256 to 263, the double-step
pawn check uses dr = from_rank - to_rank but treats dr sign backwards; flip the
sign-specific branches so white double pushes use dr == -2 and validate
(from_rank, to_rank) == (1, 3), and black double pushes use dr == 2 and validate
(from_rank, to_rank) == (6, 4); update the comments to match the corrected sign
logic.

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.

Some moves with disambiguators cannot cause check/checkmate

2 participants