refactor: slim solver to card_game-native types
Per Rhys: card_game's solver is the real engine, so drop the redundant
adapter types in solitaire_data::solver rather than maintain a parallel
verdict/config/move vocabulary.
- Delete SolverResult, SolverConfig, SolverMove, and snapshot_to_solver_move.
The verdict now reads straight off card_game's return:
Ok(Some(instr)) = winnable (first move on the path)
Ok(None) = provably unwinnable
Err(_) = inconclusive (budget exceeded)
- SolveOutcome is now Result<Option<KlondikeInstruction>, SolveError>.
- try_solve / try_solve_from_state take plain (moves_budget, states_budget)
u64s; add DEFAULT_SOLVE_{MOVES,STATES}_BUDGET consts.
- snapshot_to_solver_move duplicated core's GameState::instruction_to_move,
so make that pub and have the hint convert the first-move instruction to
highlighted (from, to) piles through it. Re-export KlondikeInstruction
from solitaire_core.
- HintSolverConfig now holds { moves_budget, states_budget } instead of
wrapping the deleted SolverConfig.
- Update consumers: pending_hint, play_by_seed (verdict badge), game_plugin
(choose_winnable_seed), input_plugin, hud_plugin, and the gen_seeds /
gen_difficulty_seeds asset tools.
solver.rs drops 274 -> 140 lines. cargo test --workspace and
cargo clippy --workspace --all-targets -- -D warnings pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,9 @@ use bevy::tasks::{AsyncComputeTaskPool, Task, futures_lite::future};
|
||||
use bevy::window::AppLifecycle;
|
||||
use solitaire_core::KlondikePile;
|
||||
use solitaire_core::{DrawMode, game_state::{GameMode, GameState}};
|
||||
use solitaire_data::solver::{SolverConfig, SolverResult, try_solve};
|
||||
use solitaire_data::solver::{
|
||||
DEFAULT_SOLVE_MOVES_BUDGET, DEFAULT_SOLVE_STATES_BUDGET, try_solve,
|
||||
};
|
||||
#[allow(deprecated)]
|
||||
use solitaire_data::latest_replay_path;
|
||||
use solitaire_data::{
|
||||
@@ -321,13 +323,13 @@ fn seed_from_system_time() -> u64 {
|
||||
/// attempts have elapsed.
|
||||
///
|
||||
/// The solver classifies each deal as one of three verdicts:
|
||||
/// - [`SolverResult::Winnable`] — provably solvable; accept.
|
||||
/// - [`SolverResult::Inconclusive`] — budget exceeded, no proof
|
||||
/// either way; accept (we treat "we don't know" as winnable so
|
||||
/// the toggle never silently drops a player into the retry cap).
|
||||
/// - [`SolverResult::Unwinnable`] — provably dead; try the next seed.
|
||||
/// - `Ok(Some(_))` — winnable (provably solvable); accept.
|
||||
/// - `Err(_)` — inconclusive (budget exceeded, no proof either way);
|
||||
/// accept (we treat "we don't know" as winnable so the toggle never
|
||||
/// silently drops a player into the retry cap).
|
||||
/// - `Ok(None)` — provably dead; try the next seed.
|
||||
///
|
||||
/// If every seed in the retry window is `Unwinnable` (extremely
|
||||
/// If every seed in the retry window is provably dead (extremely
|
||||
/// unlikely on real inputs), the function returns the *last* tried
|
||||
/// seed so the player still gets a deal — better a possibly-unwinnable
|
||||
/// hand than an infinite loop.
|
||||
@@ -389,12 +391,18 @@ fn poll_pending_new_game_seed(
|
||||
/// Pure helper extracted for testability — `new_game_with_solver_*`
|
||||
/// engine tests in the same file exercise this path.
|
||||
pub(crate) fn choose_winnable_seed(initial_seed: u64, draw_mode: DrawMode) -> u64 {
|
||||
let cfg = SolverConfig::default();
|
||||
let mut seed = initial_seed;
|
||||
for _ in 0..SOLVER_DEAL_RETRY_CAP {
|
||||
match try_solve(seed, draw_mode, &cfg) {
|
||||
SolverResult::Winnable | SolverResult::Inconclusive => return seed,
|
||||
SolverResult::Unwinnable => {
|
||||
match try_solve(
|
||||
seed,
|
||||
draw_mode,
|
||||
DEFAULT_SOLVE_MOVES_BUDGET,
|
||||
DEFAULT_SOLVE_STATES_BUDGET,
|
||||
) {
|
||||
// Winnable (`Ok(Some)`) or inconclusive (`Err`) → accept as
|
||||
// "probably winnable"; only a proven dead deal (`Ok(None)`) retries.
|
||||
Ok(Some(_)) | Err(_) => return seed,
|
||||
Ok(None) => {
|
||||
seed = seed.wrapping_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user