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:
@@ -24,7 +24,9 @@ use bevy::input::ButtonInput;
|
||||
use bevy::prelude::*;
|
||||
use bevy::tasks::{AsyncComputeTaskPool, Task, futures_lite::future};
|
||||
use solitaire_core::DrawMode;
|
||||
use solitaire_data::solver::{SolverConfig, SolverResult, try_solve};
|
||||
use solitaire_data::solver::{
|
||||
DEFAULT_SOLVE_MOVES_BUDGET, DEFAULT_SOLVE_STATES_BUDGET, SolveOutcome, try_solve,
|
||||
};
|
||||
|
||||
use crate::events::{NewGameRequestEvent, StartPlayBySeedRequestEvent};
|
||||
use crate::font_plugin::FontResource;
|
||||
@@ -83,7 +85,7 @@ struct SeedInputDisplay;
|
||||
#[derive(Resource, Default)]
|
||||
struct PendingVerification {
|
||||
seed: Option<u64>,
|
||||
handle: Option<Task<SolverResult>>,
|
||||
handle: Option<Task<SolveOutcome>>,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -340,8 +342,14 @@ fn tick_debounce_and_spawn_solver_task(
|
||||
let draw_mode = settings
|
||||
.as_ref()
|
||||
.map_or(DrawMode::DrawOne, |s| s.0.draw_mode);
|
||||
let cfg = SolverConfig::default();
|
||||
let task = AsyncComputeTaskPool::get().spawn(async move { try_solve(seed, draw_mode, &cfg) });
|
||||
let task = AsyncComputeTaskPool::get().spawn(async move {
|
||||
try_solve(
|
||||
seed,
|
||||
draw_mode,
|
||||
DEFAULT_SOLVE_MOVES_BUDGET,
|
||||
DEFAULT_SOLVE_STATES_BUDGET,
|
||||
)
|
||||
});
|
||||
|
||||
pending.seed = Some(seed);
|
||||
pending.handle = Some(task);
|
||||
@@ -369,15 +377,15 @@ fn poll_solver_task(
|
||||
return;
|
||||
};
|
||||
match result {
|
||||
SolverResult::Winnable => {
|
||||
Ok(Some(_)) => {
|
||||
text.0 = "\u{2713} Provably winnable".to_string();
|
||||
color.0 = ACCENT_PRIMARY;
|
||||
}
|
||||
SolverResult::Inconclusive => {
|
||||
Err(_) => {
|
||||
text.0 = "? Likely winnable (search timed out)".to_string();
|
||||
color.0 = TEXT_SECONDARY;
|
||||
}
|
||||
SolverResult::Unwinnable => {
|
||||
Ok(None) => {
|
||||
text.0 = "\u{2717} Provably unwinnable".to_string();
|
||||
color.0 = TEXT_DISABLED;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user