Smoke-test report: the user could only initiate a drag from the bottom strip of a tableau card, not its visible face. Root cause was a fan- step mismatch between rendering and hit-testing. card_plugin::card_positions steps face-down cards by TABLEAU_FACEDOWN_FAN_FRAC (0.12) and face-up cards by TABLEAU_FAN_FRAC (0.25), so a column with 6 face-down + 1 face-up at the bottom renders the face-up card at base.y - 0.72 * card_h. input_plugin's card_position used a uniform 0.25 step for every position, computing the same card's hit-test centre as base.y - 1.5 * card_h — almost a full card height below the visible sprite. The hit-test AABB and the sprite AABB overlapped only over the bottom 0.61 * card_h, which matches the user's observation that only the bottom of the card responds to clicks. card_position now mirrors card_plugin's exact logic: walk the pile's preceding cards and step by TABLEAU_FACEDOWN_FAN_FRAC for face-down, TABLEAU_FAN_FRAC for face-up. TABLEAU_FACEDOWN_FAN_FRAC is now public for the same reason TABLEAU_FAN_FRAC already was — the renderer and the hit-tester have to agree by construction or this regression returns. Updates the existing find_draggable_skips_face_down_cards test that relied on the old uniform-fan geometry, and adds find_draggable_hits_face_up_card_with_face_down_cards_above_it as a regression test that fails without this fix. The during-drag rendering and pile_drop_rect still use the uniform TABLEAU_FAN_FRAC because the cards being dragged are guaranteed face-up, and a slightly oversized drop target reads as forgiving rather than wrong. Those call sites are intentionally untouched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Solitaire Quest
A cross-platform Klondike Solitaire game written in Rust, featuring a full progression system with 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
- Progression — XP, levels, unlockable card backs and backgrounds
- 18 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
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
| Key | Action |
|---|---|
| Left click / drag | Move cards |
| Right click | Highlight legal moves for a card |
| Space / D | Draw from stock |
| Z / Ctrl+Z | Undo |
| N | New game |
| S | Stats overlay |
| A | Achievements overlay |
| P | Profile overlay |
| O | Settings |
| L | Leaderboard |
| H | Help / controls |
| Enter | Auto-complete (when badge is lit) |
| Escape | Pause / clear selection |
| Arrow keys | Navigate card selection |
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
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 -- -D warnings
Credits
Built on Bevy and the wider Rust ecosystem (Tokio, Axum, sqlx, Serde, kira, and many more). Card faces and the default card back use xCards artwork (LGPL-3.0); 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.
License
MIT — see LICENSE.