funman300 3e11e9e79a feat(engine): H-key hint runs on AsyncComputeTaskPool
Closes the last solver-on-main-thread hot path. The synchronous
v0.17.0 hint flow called solitaire_core::solver::try_solve_from_state
inline on every H press; median latency was ~2 ms but pathological
positions hit the SolverConfig::default() cap at ~120 ms — a visible
input stall on the same frame the player presses H.

Mirrors the d489e7a PendingNewGameSeed pattern. New module
pending_hint.rs holds:

  - PendingHintTask resource carrying an Option<HintTask> with
    handle: Task<HintTaskOutput> plus move_count_at_spawn for
    staleness detection.
  - HintTaskOutput enum: SolverMove { from, to } when the verdict
    is Winnable + a first_move; NeedsHeuristic when the solver
    returns Unwinnable or Inconclusive.
  - poll_pending_hint_task system: polls the task each frame and
    surfaces the result via the now-public emit_hint_visuals (or
    runs find_heuristic_hint on the live state for the
    NeedsHeuristic branch). Discards the result when
    GameState.move_count has advanced past move_count_at_spawn.
  - drop_pending_hint_on_state_change system: any
    StateChangedEvent drops the in-flight task. Cooperatively
    cancels via Bevy's Task Drop at the next await point.
  - PendingHintTask::spawn implements cancel-on-replace — a fresh
    H press while a previous task is in flight overwrites the
    handle, dropping the prior task.

input_plugin changes:

  - handle_keyboard_hint becomes a thin spawn point. Snapshots
    the live state, asks the solver via PendingHintTask::spawn,
    returns. No card-entity query, no event writers for the
    hint visual / toast — the polling system owns those.
  - emit_hint_visuals promoted to pub so pending_hint can call it.
  - find_heuristic_hint extracted as a pub helper for the
    NeedsHeuristic poll path.
  - InputPlugin registers PendingHintTask + the two new systems.
    drop-on-state-change is chained .before() poll so a move
    applied this frame cancels any in-flight task before its
    result can be surfaced.

Tests:

  - input_plugin: pressing_h_spawns_pending_hint_task (1) — pins
    the H-key wiring at one-frame granularity.
  - pending_hint: winnable_solver_emits_hint_after_async_completes,
    state_change_drops_in_flight_task,
    second_spawn_drops_first_in_flight_task (3) — drives the
    AsyncComputeTaskPool with a wall-clock-bounded loop mirroring
    the winnable_seed_search_* template.
  - Removed two now-stale synchronous tests
    (hint_uses_solver_when_winnable,
    hint_falls_back_to_heuristic_when_solver_inconclusive) — the
    behaviours they pinned now live in pending_hint::tests at the
    correct layer.

Workspace: 1168 passing tests / 0 failing, was 1166 (net +2:
removed 2 stale, added 4 new). cargo clippy --workspace
--all-targets -- -D warnings clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 18:01:51 -07:00

Solitaire Quest

A cross-platform Klondike Solitaire game written in Rust, with a card-theme system, full progression (XP / levels / achievements / daily challenges), and optional self-hosted sync so your stats follow you across machines.

Features

  • Klondike Solitaire — Draw One and Draw Three modes; foundations are unlocked (any Ace lands in any empty slot, the slot then claims that suit)
  • Card themes — bundled hayeah/playing-cards-assets default plus user-installable themes (drop a directory under the data dir or import a zip from Settings → Cosmetic)
  • Modern HUD — reserved top band keeps cards from crowding the score readout; the action bar auto-fades when the cursor leaves it so it can't compete with the play surface
  • Drag feel — every legal drop target is highlighted in green during drag; cards cast a soft drop shadow that lifts when picked up; the stock pile shows a remaining-count chip so you can see how close you are to a recycle
  • Keyboard navigation — Tab cycles focus through buttons, arrow keys move within picker rows, Enter activates; works across every modal and the HUD action bar
  • Progression — XP, levels, unlockable card backs and backgrounds
  • 19 Achievements — including secret ones
  • Daily Challenge — server-seeded so every player worldwide gets the same deal
  • Leaderboard — opt-in, powered by your own self-hosted server
  • Special Modes (unlocked at level 5): Zen, Time Attack, Challenge
  • Sync — pull/push stats across devices via a self-hosted server
  • Color-blind mode — blue tint on red-suit cards alongside the suit glyph

Building

Prerequisites

  • Rust stable toolchain (rustup install stable)
  • Linux: libasound2-dev libudev-dev libxkbcommon-dev
  • macOS: Xcode Command Line Tools
# Fast development build
cargo run -p solitaire_app --features bevy/dynamic_linking

# Release build
cargo build -p solitaire_app --release
./target/release/solitaire_app

Controls

Every action also has a visible UI button — keyboard shortcuts are optional accelerators.

Key Action
Left click / drag Move cards
Double click Auto-move card to its best legal destination
Right click Highlight legal moves for a card
Space / D Draw from stock
U Undo
H Hint (highlight a legal move)
N New game
Z Zen mode
G Forfeit (during pause)
Tab / Shift+Tab Cycle keyboard focus
Enter Activate focused button / auto-complete (when badge is lit)
Esc Pause / dismiss modal
F1 Help / controls
F11 Toggle fullscreen
S / A / P / O / L / M Stats / Achievements / Profile / Settings / Leaderboard / Menu

Card themes

The default theme ships embedded in the binary, so the game runs self-contained with no external assets. To install another theme, drop a directory containing a theme.ron manifest plus 53 SVG files (52 faces + 1 back) under the platform data dir's themes/ folder, or import a zip from Settings → Cosmetic. The picker chip lights up the moment a new theme is registered. Themes are SVG-based, so they rasterise cleanly at whatever resolution the window happens to be.

Sync Server (optional)

To sync stats across machines, run the self-hosted server. See README_SERVER.md for setup instructions.

Once the server is running, open Settings → Sync Backend, enter the server URL and your username, and register an account from within the game.

Running Tests

# All tests (982 passing as of v0.11.0)
cargo test --workspace

# Just game logic (no display required)
cargo test -p solitaire_core -p solitaire_sync -p solitaire_data -p solitaire_server

# Lint
cargo clippy --workspace --all-targets -- -D warnings

Credits

Built on Bevy and the wider Rust ecosystem (Tokio, Axum, sqlx, Serde, kira, and many more). Card faces come from hayeah/playing-cards-assets (MIT, derived from the public-domain vector-playing-cards library); the default card back is original work; the UI font is FiraMono-Medium (OFL). All audio is synthesized programmatically by this project. See CREDITS.md for the full list and license details.

Changelog

See CHANGELOG.md.

License

MIT — see LICENSE.

S
Description
A Klondike solitaire game built in Rust with Bevy, targeting Android and Linux desktop. Features classic card rendering, draw-one/draw-three modes, score tracking, undo/redo, daily challenges, and auto-complete detection.
https://klondike.aleshym.co/
Readme MIT 45 MiB
v0.39.0 Latest
2026-05-19 23:58:42 +00:00
Languages
Rust 95.7%
JavaScript 1.9%
HTML 1.3%
CSS 0.5%
Shell 0.4%
Other 0.2%