Skip to content

0.8.0#40

Merged
JosunLP merged 6 commits into
mainfrom
development
Jun 12, 2026
Merged

0.8.0#40
JosunLP merged 6 commits into
mainfrom
development

Conversation

@JosunLP

@JosunLP JosunLP commented Jun 12, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added analyze, bench, and perft CLI commands for position analysis, engine benchmarking, and move-generation verification.
    • FEN position loading support for analysis and play modes.
    • Difficulty levels (1–10) for engine play with configurable skill-based search parameters.
    • Draw-by-repetition and fifty-move rule claim support.
  • Improvements

    • Enhanced search engine with better pruning, move ordering, and game history integration.
    • Animated, TTY-aware terminal CLI with progress indicators and improved board rendering.
    • Expanded internationalization across 8 languages.
  • Documentation

    • Added comprehensive contribution guide, security policy, and structured issue/PR templates.

Add Electron desktop app with Svelte UI and workflows
…ed UI

- Introduced TerminalConfig struct to configure interactive terminal games.
- Added support for human vs. engine gameplay, allowing players to compete against a built-in engine.
- Implemented engine move logic with animated search display and move announcements.
- Created a new tui module for terminal UI enhancements, including animated banners and spinners.
- Improved draw claim logic to handle available claims more gracefully.
- Added FEN parsing capabilities for castling rights and piece placement in the Board struct.
…terminal input handling

- Added Zobrist hashing for ancestor positions and game history to detect draw by repetition in the SearchEngine.
- Introduced `set_game_history` method to update the engine's game history.
- Implemented `is_repetition` method to check for repetitions during the search.
- Added a test case to validate draw detection against game history.
- Refactored terminal input handling to separate command parsing from game logic, introducing `GameCommand` enum for better command management.
- Removed the TUI module, consolidating terminal output functionality into the CLI.
@JosunLP JosunLP self-assigned this Jun 12, 2026
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@JosunLP, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 47 minutes and 37 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6d2adb53-7044-4ae6-89f7-41f56d22d652

📥 Commits

Reviewing files that changed from the base of the PR and between 78893ce and aeea11f.

📒 Files selected for processing (13)
  • locales/de.yml
  • locales/en.yml
  • locales/es.yml
  • locales/fr.yml
  • locales/ja.yml
  • locales/pt.yml
  • locales/ru.yml
  • locales/zh-CN.yml
  • src/cli/analyze.rs
  • src/cli/board_renderer.rs
  • src/cli/fen.rs
  • src/cli/play.rs
  • src/cli/watch.rs
📝 Walkthrough

Walkthrough

This PR releases CheckAI v0.8.0, delivering a modular CLI architecture with six new interactive commands (play, watch, analyze, bench, perft, uci), a major search engine overhaul with improved heuristics and repetition detection, comprehensive i18n support across eight languages, and updated community governance files. The refactoring centralizes command handling into a reusable CLI module while maintaining backward compatibility for UCI and server modes.

Changes

v0.8.0 Release

Layer / File(s) Summary
Community Governance & Release Documentation
.github/CONTRIBUTING.md, .github/ISSUE_TEMPLATE/*, .github/SECURITY.md, CHANGELOG.md, docs/changelog.md, README.md, docs/guide/getting-started.md, docs/index.md
New contribution guidelines, structured issue templates (bug/feature), security policy, and updated release notes document v0.8.0 features (FEN loading, i18n completion, animated CLI, engine overhauls).
Version Bumps & Localization
Cargo.toml, desktop/package.json, npm/package.json, wasm/Cargo.toml, web/package.json, docs/.vitepress/config.mts, src/api.rs, locales/* (8 languages)
All package manifests bumped from 0.7.0 to 0.8.0; three new terminal dependencies added (crossterm, indicatif, unicode-width); comprehensive localization keys added for play/watch/analyze/bench/perft/uci commands across en/de/es/fr/ja/pt/ru/zh-CN.
Search Engine Architecture Overhaul
src/search.rs
Major refactoring: generation-based transposition table aging with static_eval caching, in-tree + pre-root repetition detection with game history support, hard time/node limit enforcement, enhanced move ordering (killers, counter-moves, gravity-style history), extensive pruning (null-move, LMR, futility, razoring), improved quiescence with SEE-based delta pruning, and full swap-algorithm SEE implementation. Public API: SearchLimits (depth/time/skill-level constructors), IterationInfo progress snapshots, score_to_mate_in helper, and set_game_history method.
CLI Module Foundation & Theming
src/cli/mod.rs, src/cli/theme.rs
Establish modular CLI architecture: CliCommand trait, CliContext with theme detection, TTY color detection (NO_COLOR env respected), terminal width/display-width utilities, and character-boundary-preserving truncation.
Game & Position Support (FEN, Types, Drawing)
src/game.rs, src/types.rs, src/cli/fen.rs
Add FEN parsing (from_fen with strict 4–6 field validation, position history seeding), FEN export, SearchPosition conversion, and Zobrist history hashing for repetition; extend CastlingRights::from_fen and Board::from_piece_placement parsing; expose count_position_repetitions and available_draw_claim (threefold/fifty-move priority).
Terminal UI: Board Rendering & Display Utilities
src/cli/board_renderer.rs, src/cli/panel.rs, src/cli/score.rs, src/cli/progress.rs
Implement ASCII/Unicode board renderer with coordinate labels, last-move/check highlights, and flipped perspective; provide score formatting (pawn/mate notation, perspective-aware, loss em-dash fallback), evaluation bars (logistic scaling), count humanization (k/M/G), panel rendering (titles, tables, results with width calculation), duration formatting, and theme-gated spinners/progress bars.
Play Mode: Interactive Chess Playing
src/cli/play.rs, src/cli/level.rs, src/terminal.rs
Implement checkai play with engine/human opponent selection, 10-level difficulty ladder (depth/movetime/TT mapping), REPL loop for moves/undo/hint/resign/draw claiming, threefold/fifty-move detection, board rendering, move history, and game result panels. Refactor terminal input to robustly parse coordinate moves and keyword commands without UTF-8 panics.
Watch Mode: Engine vs Engine Exhibition
src/cli/watch.rs
Implement checkai watch to run two independent engines with per-side configurable difficulty, move-delay animation, max-move cap safety, move tickers with eval bars, and automatic draw claiming.
Analyze & Benchmark Commands
src/cli/analyze.rs, src/cli/bench.rs
Implement checkai analyze for position evaluation (single depth sweep) and game annotation (CP-loss markers, quality labels, better-move suggestions); implement checkai bench against fixed 6-position suite with depth or time budgets, NPS reporting, and totals.
UCI Protocol & Perft Verification
src/cli/uci.rs, src/cli/perft.rs
Implement complete UCI engine protocol: command parsing (position, go, setoption Hash, stop, quit), move serialization (promotion), clock-based time control calculation, search thread management with iteration callbacks. Implement checkai perft for move-generation validation: recursive leaf-node counting, divide mode per-root-move subtotals, reference-total comparison.
Welcome Screen & Main CLI Wiring
src/cli/welcome.rs, src/main.rs
Implement welcome screen with optional animated line-by-line reveal, command table, quick-start examples, version/locale display, and docs link. Refactor main.rs: add pub mod cli, wire subcommands via CliCommand trait, add global --no-color flag, conditionally gate version checks on interactivity, use colored version output with i18n labels.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes


🐰 A modular CLI emerges,
Search heuristics leap ahead,
Chess moves animate! ✨🎮♟️

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch development

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 14

Caution

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

⚠️ Outside diff range comments (1)
docs/guide/getting-started.md (1)

156-162: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Outdated behavior description for checkai play command.

Line 162 states "This starts an interactive two-player game," but according to the changelog (line 25 of docs/changelog.md), the v0.8.0 default behavior has changed: checkai play now defaults to playing against the engine (level 5), with --vs human required for two-player mode. This Getting Started guide still documents the old behavior, which will confuse users following the quick-start instructions.

Please update this section to reflect the new default (engine play) and mention the --vs human option for two-player games.

Suggested fix
- ### Play in the Terminal
+ ### Play in the Terminal
  
- ```bash
+ ```bash
- checkai play
+ checkai play                  # Play vs the engine (default, level 5)
+ checkai play --vs human       # Two-player game
- ```
+ ```
  
- This starts an interactive two-player game with a colored board display. Type `help` for available commands.
+ This starts a game against the engine at level 5. For a local two-player game, use `--vs human`. Type `help` in-game for available commands.
🤖 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 `@docs/guide/getting-started.md` around lines 156 - 162, Update the "Play in
the Terminal" section to reflect that `checkai play` now defaults to playing vs
the engine (level 5) and that two-player mode requires `--vs human`; change the
example block to show both usages (`checkai play` with a comment like "Play vs
the engine (default, level 5)" and `checkai play --vs human` for a two-player
game) and update the descriptive sentence to say "This starts a game against the
engine at level 5. For a local two-player game, use `--vs human`. Type `help`
in-game for available commands." Refer to the `checkai play` command and the
`--vs human` option when making the edits.
🤖 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.

Inline comments:
In `@src/cli/analyze.rs`:
- Around line 254-256: The loop currently does "if game.is_over() { break; }"
which silently ignores any remaining tokens and returns success; change this to
return an error when the position is terminal but there are still unread move
tokens. Locate the replay loop around game.is_over() in analyze.rs (the function
driving move replay/analysis), and instead of breaking, check the token
iterator/remaining tokens (e.g., the moves iterator used to feed the game) and
return a Result::Err (or propagate an error) with a clear message like
"unexpected tokens after game over" so malformed move lists fail rather than
succeed.

In `@src/cli/board_renderer.rs`:
- Around line 128-141: The render_cell function is emitting a non-ASCII
dark-square glyph ("·") for empty squares; update render_cell to honor the
renderer's ASCII mode by checking the renderer flag (e.g., a boolean on self
like ascii or ascii_mode) and only use the dimmed "·" glyph when ASCII mode is
disabled—when ASCII mode is enabled emit plain " " (space) for empty dark
squares; locate and modify render_cell (and keep piece_symbol usage intact) so
the None if is_dark branch chooses between "·".dimmed().to_string() and "
".to_string() based on the renderer's ASCII setting.

In `@src/cli/fen.rs`:
- Around line 165-172: In history_hashes, don't silently ignore invalid FENs;
replace the filter_map(game_from_fen(...).ok()) pattern with code that attempts
game_from_fen for each entry in position_history, logs a warning (including the
offending fen string and the parse error) when game_from_fen fails, and
otherwise computes the zobrist::hash_position for the parsed Game and collects
it; keep behavior of skipping bad entries but ensure each rejection is logged so
callers like SearchEngine::is_repetition can be audited.
- Around line 38-40: The CLI FEN rank parser currently accepts '0' because it
uses ch.to_digit(10) without validating the range; update the parsing in the
loop that iterates over row.chars() (using variables ch, skip, file) to reject
digit '0' and only accept digits 1..=8 (i.e., treat skip == 0 as an error),
returning or propagating a parse error for invalid counts (including '0' and
values >8) instead of adding them to file. Ensure the error path mirrors the
shared parser's behavior so malformed ranks like "08/..." are rejected from the
CLI code path as well.

In `@src/cli/play.rs`:
- Around line 292-293: The resign/draw action handlers currently call
self.print_result() immediately after self.game.process_action(&action), causing
the result panel to be printed twice when the REPL loop also prints upon
self.game.is_over(); remove the immediate self.print_result() calls from the
resign and draw branches (the spots that follow
self.game.process_action(&action) in play.rs) so that only the main REPL loop
prints the final result once; ensure you only remove those explicit prints and
leave process_action and game state changes intact.
- Around line 420-425: The undo() logic in play.rs currently sets plies = 1 for
human-vs-human (engine_color.is_none()), which removes only one half-move and
contradicts the command description and localized strings; update the plies
calculation in fn undo(&mut self) so that a "full move" always removes two plies
(i.e., use plies = 2 for both engine and human-vs-human cases or simply set
plies = 2 unconditionally) so undo() matches help_table() and the play.cmd_undo
locale entries.
- Around line 156-171: claim_available_draw currently discards the result of
game.process_action and always returns a bool; change it to propagate the
process_action outcome (e.g., return Result<bool, _> or a domain Error) so
callers can handle failures: compute reason using repetition_count and
halfmove_clock as before, call game.process_action(&action) and if it returns an
Err propagate that Err to the caller instead of ignoring it, and on Ok return
game.is_over(); update callers (play and watch) to surface/log the error instead
of silently continuing.

In `@src/cli/score.rs`:
- Around line 72-78: humanize_count currently chooses suffix based on integer
ranges before formatting, which can produce outputs like "1000.0k" at cutovers;
update humanize_count to pick the suffix by computing the scaled f64 value
(e.g., let scaled = n as f64 / 1_000.0 for k) and after rounding to one decimal
check if the rounded value >= 1000.0 and if so promote to the next suffix (M/G)
instead of returning "1000.0k"; implement this logic inside the humanize_count
function so you compute scaled values, round to the desired precision, and
promote when rounded >= 1000.0 to avoid emitting thresholds like "1000.0k" or
"1000.0M".

In `@src/cli/theme.rs`:
- Around line 78-94: truncate_chars currently appends an ellipsis even when max
== 0 which produces a glyph wider than requested; update the function
truncate_chars to early-return an empty String when max == 0 before doing any
width calculations or appending '…', keeping the existing behavior for max >= 1
(so max == 1 still returns "…"). This change should be made inside the
truncate_chars function to handle the zero-width case safely.

In `@src/cli/uci.rs`:
- Around line 250-263: The code sets limits.move_time_ms to at least
MIN_BUDGET_MS even when remaining time is less, which can overshoot the GUI
clock; modify the capped calculation in the block that matches on
Color::White/Color::Black so that if remaining <= MIN_BUDGET_MS you use
remaining (or skip applying the MIN_BUDGET_MS floor), otherwise apply the
existing budget.min(remaining.saturating_sub(50)).max(MIN_BUDGET_MS) logic;
update the assignment to limits.move_time_ms accordingly so move_time_ms never
exceeds the actual remaining time.

In `@src/cli/watch.rs`:
- Around line 124-180: The loop can exit for multiple reasons (result.best_move
None, game.make_move error, claim_available_draw, etc.) but the epilogue only
checks game.is_over(), so the move-cap banner (t!("watch.move_cap_reached", cap
= self.max_moves)) can be printed incorrectly; introduce a small exit reason
flag (e.g., a boolean cap_reached or an enum exit_reason) scoped outside the
loop, set cap_reached = true exactly where you detect the move-cap condition
(the place that currently breaks when moves limit reached — reference
self.max_moves), leave other early exits unchanged (result.best_move,
game.make_move, claim_available_draw) but set a non-cap reason if desired, and
then, after the loop, use that flag (instead of only game.is_over()) to decide
whether to print result_panel(&game, started.elapsed()) or the move-cap message.

In `@src/game.rs`:
- Around line 149-156: Game::from_fen currently calls CastlingRights::from_fen
which silently ignores unknown characters; add explicit validation of the
castling field (parts[2]) before constructing castling rights: accept exactly
"-" or a non-empty string composed only of characters in the allowed set
{'K','Q','k','q'} with no other characters or duplicates, otherwise return the
same malformed-FEN error used elsewhere; after this check you can safely call
CastlingRights::from_fen (refer to Game::from_fen and CastlingRights::from_fen
in the diff) so malformed inputs like "KX" fail instead of being normalized.

In `@src/search.rs`:
- Around line 1129-1137: The 50-move draw check (`pos.halfmove_clock >= 100`) is
currently executed before mate/stalemate detection and is placed before the
quiescence call, causing leaf nodes to ignore claimable draws and turning
checkmates into draws; update the search flow so that the depth-based quiescence
call (the branch calling self.quiescence(pos, alpha, beta, ply) when depth <= 0)
remains, but move the halfmove-clock draw test out of this fast path and into
the common post-move-generation/mate-detection area used by both normal search
and quiescence: perform legal-move generation and check for mate/stalemate
first, then, if no mate/stalemate, evaluate the `pos.halfmove_clock >= 100`
condition and return DRAW_SCORE as appropriate; ensure both the main search
function and quiescence (calls to self.quiescence) follow this order so the
50-move rule never preempts mate detection.

In `@src/types.rs`:
- Around line 488-492: The FEN parser currently accepts '0' because it uses
c.to_digit(10); update the rank-parsing logic (the loop over rank_str.chars()
used by Game::from_fen) to reject zero as an empty-square run length: after
converting the char to a digit, explicitly check that the digit is between 1 and
8 (not 0) and return an Err with a clear message (e.g., "FEN rank '...' contains
invalid digit '0'") for '0' or any out-of-range value; keep the existing
overflow check for file > 8.

---

Outside diff comments:
In `@docs/guide/getting-started.md`:
- Around line 156-162: Update the "Play in the Terminal" section to reflect that
`checkai play` now defaults to playing vs the engine (level 5) and that
two-player mode requires `--vs human`; change the example block to show both
usages (`checkai play` with a comment like "Play vs the engine (default, level
5)" and `checkai play --vs human` for a two-player game) and update the
descriptive sentence to say "This starts a game against the engine at level 5.
For a local two-player game, use `--vs human`. Type `help` in-game for available
commands." Refer to the `checkai play` command and the `--vs human` option when
making the edits.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e776acce-ecc8-4cc0-b8a3-2f498678b22d

📥 Commits

Reviewing files that changed from the base of the PR and between 98c9cc1 and 78893ce.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • wasm/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (49)
  • .github/CONTRIBUTING.md
  • .github/ISSUE_TEMPLATE/bug_report.md
  • .github/ISSUE_TEMPLATE/bug_report.yml
  • .github/ISSUE_TEMPLATE/config.yml
  • .github/ISSUE_TEMPLATE/feature_request.md
  • .github/ISSUE_TEMPLATE/feature_request.yml
  • .github/PULL_REQUEST_TEMPLATE.md
  • .github/SECURITY.md
  • CHANGELOG.md
  • Cargo.toml
  • README.md
  • desktop/package.json
  • docs/.vitepress/config.mts
  • docs/changelog.md
  • docs/guide/getting-started.md
  • docs/index.md
  • locales/de.yml
  • locales/en.yml
  • locales/es.yml
  • locales/fr.yml
  • locales/ja.yml
  • locales/pt.yml
  • locales/ru.yml
  • locales/zh-CN.yml
  • npm/README.md
  • npm/package.json
  • src/api.rs
  • src/cli/analyze.rs
  • src/cli/bench.rs
  • src/cli/board_renderer.rs
  • src/cli/fen.rs
  • src/cli/level.rs
  • src/cli/mod.rs
  • src/cli/panel.rs
  • src/cli/perft.rs
  • src/cli/play.rs
  • src/cli/progress.rs
  • src/cli/score.rs
  • src/cli/theme.rs
  • src/cli/uci.rs
  • src/cli/watch.rs
  • src/cli/welcome.rs
  • src/game.rs
  • src/main.rs
  • src/search.rs
  • src/terminal.rs
  • src/types.rs
  • wasm/Cargo.toml
  • web/package.json
💤 Files with no reviewable changes (2)
  • .github/ISSUE_TEMPLATE/bug_report.md
  • .github/ISSUE_TEMPLATE/feature_request.md

Comment thread src/cli/analyze.rs
Comment thread src/cli/board_renderer.rs
Comment thread src/cli/fen.rs
Comment thread src/cli/fen.rs
Comment thread src/cli/play.rs Outdated
Comment thread src/cli/uci.rs
Comment thread src/cli/watch.rs
Comment thread src/game.rs
Comment thread src/search.rs
Comment thread src/types.rs
JosunLP and others added 2 commits June 12, 2026 19:47
- analyze.rs: return error when move list contains tokens after game-over
- board_renderer.rs: honor --ascii flag for empty dark squares (· → .)
- fen.rs: reject invalid skip count '0' in FEN rank parser (allow 1–8 only)
- fen.rs: log warning for unparseable history FEN entries instead of silent drop
- play.rs + watch.rs: propagate process_action errors from claim_available_draw
- locales: add analyze.moves_after_game_over translation key for all 8 languages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@JosunLP
JosunLP merged commit 01a3a08 into main Jun 12, 2026
33 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.

2 participants