From 5c992cbdca1b97405023e0f7ea844baeb9c99d55 Mon Sep 17 00:00:00 2001 From: funman300 Date: Thu, 11 Jun 2026 16:01:11 -0700 Subject: [PATCH] refactor: replace local DrawMode with upstream klondike::DrawStockConfig (#82) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DrawMode was a 1:1 mirror of klondike::DrawStockConfig (DrawOne/DrawThree). Delete it and use the upstream type everywhere; re-export DrawStockConfig from solitaire_core. config_for assigns draw_stock directly and draw_mode() returns session.config().inner.draw_stock. Serde is unchanged — DrawStockConfig serialises to the same "DrawOne"/"DrawThree" named variants, so persisted game_state.json / replay JSON stay byte-compatible (no migration). Field/method/variable names containing draw_mode are unchanged. 35 files, mechanical type swap across all crates. Implemented via a multi-agent workflow (core → per-crate consumers → verify). cargo test --workspace and clippy --workspace --all-targets -- -D warnings green. Closes #82 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/bin/gen_difficulty_seeds.rs | 4 +- solitaire_assetgen/src/bin/gen_seeds.rs | 4 +- solitaire_core/src/game_state.rs | 47 ++++++++--------- solitaire_core/src/klondike_adapter.rs | 16 +----- solitaire_core/src/lib.rs | 3 +- solitaire_core/src/proptest_tests.rs | 7 ++- solitaire_data/src/replay.rs | 10 ++-- solitaire_data/src/settings.rs | 10 ++-- solitaire_data/src/stats.rs | 52 +++++++++---------- solitaire_data/src/storage.rs | 16 +++--- solitaire_data/src/weekly.rs | 10 ++-- solitaire_engine/src/achievement_plugin.rs | 8 +-- solitaire_engine/src/auto_complete_plugin.rs | 6 +-- solitaire_engine/src/card_plugin.rs | 48 ++++++++--------- solitaire_engine/src/challenge_plugin.rs | 6 +-- solitaire_engine/src/cursor_plugin.rs | 12 ++--- .../src/daily_challenge_plugin.rs | 8 +-- solitaire_engine/src/feedback_anim_plugin.rs | 8 +-- solitaire_engine/src/game_plugin.rs | 32 ++++++------ solitaire_engine/src/home_plugin.rs | 12 ++--- solitaire_engine/src/hud_plugin.rs | 24 ++++----- solitaire_engine/src/input_plugin.rs | 42 +++++++-------- solitaire_engine/src/pause_plugin.rs | 42 +++++++-------- solitaire_engine/src/pending_hint.rs | 4 +- solitaire_engine/src/play_by_seed_plugin.rs | 4 +- solitaire_engine/src/radial_menu.rs | 6 +-- solitaire_engine/src/replay_overlay/tests.rs | 8 +-- solitaire_engine/src/replay_playback.rs | 8 +-- solitaire_engine/src/selection_plugin.rs | 4 +- solitaire_engine/src/settings_plugin.rs | 12 ++--- solitaire_engine/src/stats_plugin.rs | 4 +- solitaire_engine/src/sync_plugin.rs | 4 +- solitaire_engine/src/time_attack_plugin.rs | 6 +-- solitaire_engine/src/win_summary_plugin.rs | 10 ++-- solitaire_wasm/src/lib.rs | 34 ++++++------ 35 files changed, 257 insertions(+), 274 deletions(-) diff --git a/solitaire_assetgen/src/bin/gen_difficulty_seeds.rs b/solitaire_assetgen/src/bin/gen_difficulty_seeds.rs index aa59e91..b4d1844 100644 --- a/solitaire_assetgen/src/bin/gen_difficulty_seeds.rs +++ b/solitaire_assetgen/src/bin/gen_difficulty_seeds.rs @@ -19,7 +19,7 @@ //! --per-tier Seeds to emit per tier (default 40) //! --help Print this message -use solitaire_core::DrawMode; +use solitaire_core::DrawStockConfig; use solitaire_core::game_state::GameState; // Budget boundaries defining each tier. A seed belongs to the lowest tier @@ -74,7 +74,7 @@ fn main() { std::process::exit(1); } - let draw_mode = DrawMode::DrawOne; + let draw_mode = DrawStockConfig::DrawOne; let num_tiers = BUDGETS.len(); let mut buckets: Vec> = vec![Vec::with_capacity(per_tier); num_tiers]; let mut tried: u64 = 0; diff --git a/solitaire_assetgen/src/bin/gen_seeds.rs b/solitaire_assetgen/src/bin/gen_seeds.rs index 12b4871..17d719a 100644 --- a/solitaire_assetgen/src/bin/gen_seeds.rs +++ b/solitaire_assetgen/src/bin/gen_seeds.rs @@ -17,7 +17,7 @@ //! --count Number of Winnable seeds to emit (default 75) //! --help Print this message -use solitaire_core::DrawMode; +use solitaire_core::DrawStockConfig; use solitaire_core::game_state::GameState; use solitaire_core::{DEFAULT_SOLVE_MOVES_BUDGET, DEFAULT_SOLVE_STATES_BUDGET}; @@ -68,7 +68,7 @@ fn main() { std::process::exit(1); } - let draw_mode = DrawMode::DrawOne; + let draw_mode = DrawStockConfig::DrawOne; let mut found: Vec = Vec::with_capacity(count); let mut tried: u64 = 0; let mut seed = start; diff --git a/solitaire_core/src/game_state.rs b/solitaire_core/src/game_state.rs index 39c99c6..4d0942b 100644 --- a/solitaire_core/src/game_state.rs +++ b/solitaire_core/src/game_state.rs @@ -1,6 +1,6 @@ use crate::error::MoveError; use crate::klondike_adapter::{ - DrawMode, KlondikeAdapter, SavedInstruction, + KlondikeAdapter, SavedInstruction, foundation_from_slot as adapter_foundation_from_slot, skip_cards_from_count as adapter_skip_cards_from_count, tableau_from_index as adapter_tableau_from_index, @@ -101,7 +101,7 @@ pub enum GameMode { /// `KlondikeInstruction` serde, which produces named enum variants. #[derive(Debug, Clone, Serialize)] struct PersistedGameState { - pub draw_mode: DrawMode, + pub draw_mode: DrawStockConfig, pub mode: GameMode, pub elapsed_seconds: u64, pub seed: u64, @@ -134,7 +134,7 @@ enum AnyInstruction { /// them. #[derive(Debug, Clone, Deserialize)] struct PersistedGameStateIn { - pub draw_mode: DrawMode, + pub draw_mode: DrawStockConfig, #[serde(default)] pub mode: GameMode, pub elapsed_seconds: u64, @@ -306,12 +306,12 @@ impl<'de> Deserialize<'de> for GameState { impl GameState { /// Creates a new Classic-mode game dealt from the given seed and draw mode. - pub fn new(seed: u64, draw_mode: DrawMode) -> Self { + pub fn new(seed: u64, draw_mode: DrawStockConfig) -> Self { Self::new_with_mode(seed, draw_mode, GameMode::Classic) } /// Creates a new game with an explicit `GameMode`. - pub fn new_with_mode(seed: u64, draw_mode: DrawMode, mode: GameMode) -> Self { + pub fn new_with_mode(seed: u64, draw_mode: DrawStockConfig, mode: GameMode) -> Self { Self { mode, elapsed_seconds: 0, @@ -325,11 +325,8 @@ impl GameState { /// Whether the player draws one or three cards from the stock per turn. /// Derived from the underlying session config (set once at deal time). - pub fn draw_mode(&self) -> DrawMode { - match self.session.config().inner.draw_stock { - DrawStockConfig::DrawOne => DrawMode::DrawOne, - DrawStockConfig::DrawThree => DrawMode::DrawThree, - } + pub fn draw_mode(&self) -> DrawStockConfig { + self.session.config().inner.draw_stock } /// Current game score, derived from the upstream session stats. @@ -405,11 +402,11 @@ impl GameState { !self.check_win() && self.check_auto_complete() } - fn new_session(seed: u64, draw_mode: DrawMode) -> Session { + fn new_session(seed: u64, draw_mode: DrawStockConfig) -> Session { Session::new(Klondike::with_seed(seed), Self::session_config(draw_mode)) } - fn session_config(draw_mode: DrawMode) -> SessionConfig { + fn session_config(draw_mode: DrawStockConfig) -> SessionConfig { SessionConfig { inner: Self::replay_config(draw_mode), // The −15 WXP undo penalty is now applied by the upstream score @@ -419,7 +416,7 @@ impl GameState { } } - fn replay_config(draw_mode: DrawMode) -> KlondikeConfig { + fn replay_config(draw_mode: DrawStockConfig) -> KlondikeConfig { // Always allow foundation returns during replay, regardless of the // player's current `take_from_foundation` setting. A move recorded // when the rule was enabled must replay correctly even if the player @@ -587,7 +584,7 @@ impl GameState { /// mode. `draw_mode()` is otherwise fixed at deal time, so tests that need /// a specific mode use this instead of mutating a field. #[cfg(feature = "test-support")] - pub fn set_test_draw_mode(&mut self, draw_mode: DrawMode) { + pub fn set_test_draw_mode(&mut self, draw_mode: DrawStockConfig) { self.session = Self::new_session(self.seed, draw_mode); } @@ -1148,7 +1145,7 @@ impl GameState { /// "Winnable deals only" retry loop. pub fn solve_fresh_deal( seed: u64, - draw_mode: DrawMode, + draw_mode: DrawStockConfig, moves_budget: u64, states_budget: u64, ) -> SolveOutcome { @@ -1177,7 +1174,7 @@ mod tests { const MAX_STEPS: usize = 160; for seed in 1..=MAX_SEED { - let mut game = GameState::new(seed, DrawMode::DrawOne); + let mut game = GameState::new(seed, DrawStockConfig::DrawOne); game.take_from_foundation = true; for _ in 0..MAX_STEPS { @@ -1226,7 +1223,7 @@ mod tests { /// iteration limit (shouldn't happen in practice). fn game_at_first_recycle() -> Option { for seed in 1..=256_u64 { - let mut game = GameState::new(seed, DrawMode::DrawOne); + let mut game = GameState::new(seed, DrawStockConfig::DrawOne); for _ in 0..200 { if game.stock_cards().is_empty() && !game.waste_cards().is_empty() { // This draw will recycle. @@ -1259,7 +1256,7 @@ mod tests { fn undo_applies_minus_15_penalty_via_upstream_score() { // A foundation move scores +10 upstream; undoing it nets the move score // back to 0 and adds the −15 undo penalty, which `score()` floors at 0. - let mut game = GameState::new(1, DrawMode::DrawOne); + let mut game = GameState::new(1, DrawStockConfig::DrawOne); // Find and play any scoring move, then undo it. let scoring_move = game .possible_instructions() @@ -1319,13 +1316,13 @@ mod tests { fn solve_fresh_deal_is_deterministic() { let a = GameState::solve_fresh_deal( 7, - DrawMode::DrawOne, + DrawStockConfig::DrawOne, DEFAULT_SOLVE_MOVES_BUDGET, DEFAULT_SOLVE_STATES_BUDGET, ); let b = GameState::solve_fresh_deal( 7, - DrawMode::DrawOne, + DrawStockConfig::DrawOne, DEFAULT_SOLVE_MOVES_BUDGET, DEFAULT_SOLVE_STATES_BUDGET, ); @@ -1335,7 +1332,7 @@ mod tests { #[test] fn winnable_verdict_carries_a_first_move() { // Contract: a first move is present iff the verdict is winnable. - let outcome = GameState::solve_fresh_deal(7, DrawMode::DrawOne, 5_000, 5_000); + let outcome = GameState::solve_fresh_deal(7, DrawStockConfig::DrawOne, 5_000, 5_000); let winnable = matches!(outcome, Ok(Some(_))); let has_move = outcome.ok().flatten().is_some(); assert_eq!(winnable, has_move); @@ -1343,7 +1340,7 @@ mod tests { #[test] fn solve_first_move_uses_live_game_state() { - let mut game = GameState::new(42, DrawMode::DrawOne); + let mut game = GameState::new(42, DrawStockConfig::DrawOne); game.draw().expect("draw must succeed"); let outcome = game.solve_first_move(5_000, 5_000); @@ -1354,7 +1351,7 @@ mod tests { #[test] fn zero_state_budget_is_inconclusive() { - let outcome = GameState::solve_fresh_deal(7, DrawMode::DrawOne, 5_000, 0); + let outcome = GameState::solve_fresh_deal(7, DrawStockConfig::DrawOne, 5_000, 0); assert!(matches!(outcome, Err(SolveError::StatesBudgetExceeded))); } @@ -1362,9 +1359,9 @@ mod tests { fn budget_is_passed_through_not_clamped() { // This seed is Inconclusive at 1k states but Winnable at 5k — proving the // budget reaches the solver unchanged. - let easy = GameState::solve_fresh_deal(0xD1FF_0000_0000_0012, DrawMode::DrawOne, 1_000, 1_000); + let easy = GameState::solve_fresh_deal(0xD1FF_0000_0000_0012, DrawStockConfig::DrawOne, 1_000, 1_000); let medium = - GameState::solve_fresh_deal(0xD1FF_0000_0000_0012, DrawMode::DrawOne, 5_000, 5_000); + GameState::solve_fresh_deal(0xD1FF_0000_0000_0012, DrawStockConfig::DrawOne, 5_000, 5_000); assert!(easy.is_err()); assert!(matches!(medium, Ok(Some(_)))); } diff --git a/solitaire_core/src/klondike_adapter.rs b/solitaire_core/src/klondike_adapter.rs index 8fb40bf..6c666dc 100644 --- a/solitaire_core/src/klondike_adapter.rs +++ b/solitaire_core/src/klondike_adapter.rs @@ -16,15 +16,6 @@ use klondike::{ }; use serde::{Deserialize, Serialize}; -/// Whether cards are drawn one at a time or three at a time from the stock. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub enum DrawMode { - /// Draw one card from stock per turn. - DrawOne, - /// Draw three cards from stock per turn; only the top is playable. - DrawThree, -} - /// Bridges `solitaire_core` game config and scoring to the upstream `klondike` crate. /// /// This type is intentionally zero-sized: it does not carry mutable runtime @@ -35,12 +26,9 @@ pub struct KlondikeAdapter; impl KlondikeAdapter { /// Build a [`KlondikeConfig`] from draw mode and foundation house-rule setting. - pub fn config_for(draw_mode: DrawMode, take_from_foundation: bool) -> KlondikeConfig { + pub fn config_for(draw_mode: DrawStockConfig, take_from_foundation: bool) -> KlondikeConfig { KlondikeConfig { - draw_stock: match draw_mode { - DrawMode::DrawOne => DrawStockConfig::DrawOne, - DrawMode::DrawThree => DrawStockConfig::DrawThree, - }, + draw_stock: draw_mode, move_from_foundation: if take_from_foundation { MoveFromFoundationConfig::Allowed } else { diff --git a/solitaire_core/src/lib.rs b/solitaire_core/src/lib.rs index acefd8f..702f431 100644 --- a/solitaire_core/src/lib.rs +++ b/solitaire_core/src/lib.rs @@ -13,8 +13,7 @@ pub mod klondike_adapter; // when decoding instructions to piles in `instruction_to_piles`) and do not // appear in any public method signature. pub use card_game::{Card, Session, SolveError}; -pub use klondike::{Foundation, Klondike, KlondikeInstruction, KlondikePile, Tableau}; -pub use klondike_adapter::DrawMode; +pub use klondike::{DrawStockConfig, Foundation, Klondike, KlondikeInstruction, KlondikePile, Tableau}; // Solvability check API (delegates to `card_game::Session::solve`); replaces the // former `solitaire_data::solver` wrapper module. diff --git a/solitaire_core/src/proptest_tests.rs b/solitaire_core/src/proptest_tests.rs index 596daa7..3ec7cc4 100644 --- a/solitaire_core/src/proptest_tests.rs +++ b/solitaire_core/src/proptest_tests.rs @@ -1,9 +1,8 @@ use card_game::{Card, Game}; -use klondike::{Foundation, KlondikePile, KlondikeInstruction, SkipCards, Tableau}; +use klondike::{DrawStockConfig, Foundation, KlondikePile, KlondikeInstruction, SkipCards, Tableau}; use proptest::prelude::*; use crate::game_state::GameState; -use crate::klondike_adapter::DrawMode; use crate::klondike_adapter::{ InvalidSavedInstruction, SavedDstFoundation, SavedDstTableau, SavedFoundation, SavedInstruction, SavedKlondikePile, SavedKlondikePileStack, SavedSkipCards, SavedTableau, @@ -52,8 +51,8 @@ fn all_cards(game: &GameState) -> Vec { cards } -fn draw_mode_strategy() -> impl Strategy { - prop_oneof![Just(DrawMode::DrawOne), Just(DrawMode::DrawThree)] +fn draw_mode_strategy() -> impl Strategy { + prop_oneof![Just(DrawStockConfig::DrawOne), Just(DrawStockConfig::DrawThree)] } /// Apply a sequence of random actions to a game, silently ignoring errors. diff --git a/solitaire_data/src/replay.rs b/solitaire_data/src/replay.rs index 4b60b82..86d9480 100644 --- a/solitaire_data/src/replay.rs +++ b/solitaire_data/src/replay.rs @@ -26,7 +26,7 @@ use std::path::{Path, PathBuf}; use chrono::NaiveDate; use serde::{Deserialize, Serialize}; -use solitaire_core::{DrawMode, game_state::GameMode}; +use solitaire_core::{DrawStockConfig, game_state::GameMode}; use solitaire_core::klondike_adapter::SavedKlondikePile; const LATEST_REPLAY_FILE_NAME: &str = "latest_replay.json"; @@ -124,7 +124,7 @@ pub struct Replay { /// `GameState::new_with_mode(seed, draw_mode, mode)`. pub seed: u64, /// Draw mode the recorded game was played in. - pub draw_mode: DrawMode, + pub draw_mode: DrawStockConfig, /// Game mode the recorded game was played in. pub mode: GameMode, /// Total wall-clock seconds the win took. Used for the Stats UI @@ -180,7 +180,7 @@ impl Replay { /// latter directly when the upload task resolves. pub fn new( seed: u64, - draw_mode: DrawMode, + draw_mode: DrawStockConfig, mode: GameMode, time_seconds: u64, final_score: i32, @@ -453,7 +453,7 @@ mod tests { let date = NaiveDate::from_ymd_opt(2026, 5, 2).expect("valid date"); Replay::new( 12345, - DrawMode::DrawThree, + DrawStockConfig::DrawThree, GameMode::Classic, 134, 5_120, @@ -596,7 +596,7 @@ mod tests { let date = NaiveDate::from_ymd_opt(2026, 5, 2).expect("valid date"); Replay::new( id as u64, - DrawMode::DrawOne, + DrawStockConfig::DrawOne, GameMode::Classic, 60, id, diff --git a/solitaire_data/src/settings.rs b/solitaire_data/src/settings.rs index 8923b06..8e8b63b 100644 --- a/solitaire_data/src/settings.rs +++ b/solitaire_data/src/settings.rs @@ -9,7 +9,7 @@ use std::io; use std::path::{Path, PathBuf}; use serde::{Deserialize, Serialize}; -use solitaire_core::{DrawMode, game_state::DifficultyLevel}; +use solitaire_core::{DrawStockConfig, game_state::DifficultyLevel}; const SETTINGS_FILE_NAME: &str = "settings.json"; @@ -101,7 +101,7 @@ pub struct WindowGeometry { pub struct Settings { /// Draw mode selected for new games. #[serde(default = "default_draw_mode")] - pub draw_mode: DrawMode, + pub draw_mode: DrawStockConfig, /// Linear SFX volume in `[0.0, 1.0]`. Applied to kira's SFX channel gain. #[serde(default = "default_sfx_volume")] pub sfx_volume: f32, @@ -288,8 +288,8 @@ pub struct Settings { pub touch_input_mode: TouchInputMode, } -fn default_draw_mode() -> DrawMode { - DrawMode::DrawOne +fn default_draw_mode() -> DrawStockConfig { + DrawStockConfig::DrawOne } fn default_sfx_volume() -> f32 { @@ -392,7 +392,7 @@ pub const SOLVER_DEAL_RETRY_CAP: u32 = 50; impl Default for Settings { fn default() -> Self { Self { - draw_mode: DrawMode::DrawOne, + draw_mode: DrawStockConfig::DrawOne, sfx_volume: default_sfx_volume(), music_volume: default_music_volume(), animation_speed: AnimSpeed::Normal, diff --git a/solitaire_data/src/stats.rs b/solitaire_data/src/stats.rs index ae16f60..6339235 100644 --- a/solitaire_data/src/stats.rs +++ b/solitaire_data/src/stats.rs @@ -2,10 +2,10 @@ //! //! [`StatsSnapshot`] is defined in `solitaire_sync` and re-exported here. //! This module adds the [`StatsExt`] extension trait, which supplies the -//! `update_on_win` method that depends on [`DrawMode`] from `solitaire_core`. +//! `update_on_win` method that depends on [`DrawStockConfig`] from `solitaire_core`. use chrono::Utc; -use solitaire_core::{DrawMode, game_state::GameMode}; +use solitaire_core::{DrawStockConfig, game_state::GameMode}; pub use solitaire_sync::StatsSnapshot; @@ -18,9 +18,9 @@ pub trait StatsExt { /// /// Tracks lifetime totals only — per-mode best scores and times are /// updated separately via [`StatsExt::update_per_mode_bests`] so the - /// long-standing call sites that only know about [`DrawMode`] keep + /// long-standing call sites that only know about [`DrawStockConfig`] keep /// compiling. - fn update_on_win(&mut self, score: i32, time_seconds: u64, draw_mode: &DrawMode); + fn update_on_win(&mut self, score: i32, time_seconds: u64, draw_mode: &DrawStockConfig); /// Updates the per-mode best score and fastest-win-time fields for the /// given [`GameMode`]. Call alongside [`StatsExt::update_on_win`] from @@ -37,7 +37,7 @@ pub trait StatsExt { } impl StatsExt for StatsSnapshot { - fn update_on_win(&mut self, score: i32, time_seconds: u64, draw_mode: &DrawMode) { + fn update_on_win(&mut self, score: i32, time_seconds: u64, draw_mode: &DrawStockConfig) { let prev_wins = self.games_won; self.games_played += 1; self.games_won += 1; @@ -64,8 +64,8 @@ impl StatsExt for StatsSnapshot { }; match draw_mode { - DrawMode::DrawOne => self.draw_one_wins += 1, - DrawMode::DrawThree => self.draw_three_wins += 1, + DrawStockConfig::DrawOne => self.draw_one_wins += 1, + DrawStockConfig::DrawThree => self.draw_three_wins += 1, } self.last_modified = Utc::now(); @@ -135,7 +135,7 @@ mod tests { #[test] fn first_win_sets_all_fields() { let mut s = StatsSnapshot::default(); - s.update_on_win(1500, 120, &DrawMode::DrawOne); + s.update_on_win(1500, 120, &DrawStockConfig::DrawOne); assert_eq!(s.games_played, 1); assert_eq!(s.games_won, 1); assert_eq!(s.win_streak_current, 1); @@ -152,7 +152,7 @@ mod tests { fn streak_tracks_across_wins() { let mut s = StatsSnapshot::default(); for _ in 0..3 { - s.update_on_win(100, 60, &DrawMode::DrawOne); + s.update_on_win(100, 60, &DrawStockConfig::DrawOne); } assert_eq!(s.win_streak_current, 3); assert_eq!(s.win_streak_best, 3); @@ -161,8 +161,8 @@ mod tests { #[test] fn record_abandoned_resets_streak_and_increments_played() { let mut s = StatsSnapshot::default(); - s.update_on_win(100, 60, &DrawMode::DrawOne); - s.update_on_win(100, 60, &DrawMode::DrawOne); + s.update_on_win(100, 60, &DrawStockConfig::DrawOne); + s.update_on_win(100, 60, &DrawStockConfig::DrawOne); assert_eq!(s.win_streak_current, 2); s.record_abandoned(); assert_eq!(s.games_played, 3); @@ -174,35 +174,35 @@ mod tests { #[test] fn fastest_win_takes_minimum() { let mut s = StatsSnapshot::default(); - s.update_on_win(100, 300, &DrawMode::DrawOne); - s.update_on_win(100, 120, &DrawMode::DrawOne); - s.update_on_win(100, 500, &DrawMode::DrawOne); + s.update_on_win(100, 300, &DrawStockConfig::DrawOne); + s.update_on_win(100, 120, &DrawStockConfig::DrawOne); + s.update_on_win(100, 500, &DrawStockConfig::DrawOne); assert_eq!(s.fastest_win_seconds, 120); } #[test] fn avg_time_is_correct_rolling_average() { let mut s = StatsSnapshot::default(); - s.update_on_win(100, 100, &DrawMode::DrawOne); - s.update_on_win(100, 200, &DrawMode::DrawOne); - s.update_on_win(100, 300, &DrawMode::DrawOne); + s.update_on_win(100, 100, &DrawStockConfig::DrawOne); + s.update_on_win(100, 200, &DrawStockConfig::DrawOne); + s.update_on_win(100, 300, &DrawStockConfig::DrawOne); assert_eq!(s.avg_time_seconds, 200); } #[test] fn best_score_updates_only_on_higher_score() { let mut s = StatsSnapshot::default(); - s.update_on_win(500, 60, &DrawMode::DrawOne); - s.update_on_win(300, 60, &DrawMode::DrawOne); + s.update_on_win(500, 60, &DrawStockConfig::DrawOne); + s.update_on_win(300, 60, &DrawStockConfig::DrawOne); assert_eq!(s.best_single_score, 500); - s.update_on_win(800, 60, &DrawMode::DrawOne); + s.update_on_win(800, 60, &DrawStockConfig::DrawOne); assert_eq!(s.best_single_score, 800); } #[test] fn negative_score_treated_as_zero() { let mut s = StatsSnapshot::default(); - s.update_on_win(-50, 60, &DrawMode::DrawOne); + s.update_on_win(-50, 60, &DrawStockConfig::DrawOne); assert_eq!(s.best_single_score, 0); assert_eq!(s.lifetime_score, 0); } @@ -210,8 +210,8 @@ mod tests { #[test] fn draw_three_wins_tracked_separately() { let mut s = StatsSnapshot::default(); - s.update_on_win(100, 60, &DrawMode::DrawOne); - s.update_on_win(100, 60, &DrawMode::DrawThree); + s.update_on_win(100, 60, &DrawStockConfig::DrawOne); + s.update_on_win(100, 60, &DrawStockConfig::DrawThree); assert_eq!(s.draw_one_wins, 1); assert_eq!(s.draw_three_wins, 1); } @@ -221,7 +221,7 @@ mod tests { let mut s = StatsSnapshot::default(); // Build a streak of 5. for _ in 0..5 { - s.update_on_win(100, 60, &DrawMode::DrawOne); + s.update_on_win(100, 60, &DrawStockConfig::DrawOne); } assert_eq!(s.win_streak_best, 5); // Lose (abandon), resetting current. @@ -229,7 +229,7 @@ mod tests { assert_eq!(s.win_streak_current, 0); assert_eq!(s.win_streak_best, 5, "best must survive the loss"); // Win once — current becomes 1, best must remain 5. - s.update_on_win(100, 60, &DrawMode::DrawOne); + s.update_on_win(100, 60, &DrawStockConfig::DrawOne); assert_eq!(s.win_streak_current, 1); assert_eq!( s.win_streak_best, 5, @@ -243,7 +243,7 @@ mod tests { lifetime_score: u64::MAX - 100, ..Default::default() }; - s.update_on_win(200, 60, &DrawMode::DrawOne); + s.update_on_win(200, 60, &DrawStockConfig::DrawOne); assert_eq!( s.lifetime_score, u64::MAX, diff --git a/solitaire_data/src/storage.rs b/solitaire_data/src/storage.rs index d23a9d2..a272043 100644 --- a/solitaire_data/src/storage.rs +++ b/solitaire_data/src/storage.rs @@ -279,7 +279,7 @@ fn cleanup_tmp_files_in(dir: &Path) { mod tests { use super::*; use crate::stats::{StatsExt, StatsSnapshot}; - use solitaire_core::DrawMode; + use solitaire_core::DrawStockConfig; use std::env; fn tmp_path(name: &str) -> PathBuf { @@ -292,7 +292,7 @@ mod tests { let _ = fs::remove_file(&path); let mut stats = StatsSnapshot::default(); - stats.update_on_win(1000, 180, &DrawMode::DrawOne); + stats.update_on_win(1000, 180, &DrawStockConfig::DrawOne); save_stats_to(&path, &stats).expect("save"); let loaded = load_stats_from(&path); @@ -381,7 +381,7 @@ mod tests { let path = gs_path("round_trip"); let _ = fs::remove_file(&path); - let gs = GameState::new(12345, DrawMode::DrawOne); + let gs = GameState::new(12345, DrawStockConfig::DrawOne); save_game_state_to(&path, &gs).expect("save"); let loaded = load_game_state_from(&path).expect("load"); @@ -410,7 +410,7 @@ mod tests { let path = gs_path("won_skip"); let _ = fs::remove_file(&path); - let mut gs = GameState::new(99, DrawMode::DrawOne); + let mut gs = GameState::new(99, DrawStockConfig::DrawOne); gs.set_test_won(true); save_game_state_to(&path, &gs).expect("save should be no-op, not error"); assert!( @@ -423,7 +423,7 @@ mod tests { fn delete_game_state_removes_file() { use solitaire_core::game_state::GameState; let path = gs_path("delete"); - let gs = GameState::new(1, DrawMode::DrawOne); + let gs = GameState::new(1, DrawStockConfig::DrawOne); save_game_state_to(&path, &gs).expect("save"); assert!(path.exists()); delete_game_state_at(&path).expect("delete"); @@ -441,7 +441,7 @@ mod tests { fn save_game_state_is_atomic() { use solitaire_core::game_state::GameState; let path = gs_path("atomic"); - let gs = GameState::new(55, DrawMode::DrawThree); + let gs = GameState::new(55, DrawStockConfig::DrawThree); save_game_state_to(&path, &gs).expect("save"); let tmp = path.with_extension("json.tmp"); assert!(!tmp.exists(), ".tmp must be cleaned up after rename"); @@ -512,7 +512,7 @@ mod tests { let path = gs_path("v4_mid_game"); let _ = fs::remove_file(&path); - let mut gs = GameState::new(42, DrawMode::DrawOne); + let mut gs = GameState::new(42, DrawStockConfig::DrawOne); // Draw several times to populate the instruction history with // RotateStock entries and expose waste cards for further moves. @@ -619,7 +619,7 @@ mod tests { .expect("schema v3 must be accepted and migrated to v4"); // The loaded game should match a fresh game that had one draw applied. - let mut expected = GameState::new(42, DrawMode::DrawOne); + let mut expected = GameState::new(42, DrawStockConfig::DrawOne); expected.draw().expect("draw must succeed on a fresh game"); assert_eq!(loaded, expected, "migrated v3 game state must match equivalent v4 state"); } diff --git a/solitaire_data/src/weekly.rs b/solitaire_data/src/weekly.rs index 96254cc..f7f9933 100644 --- a/solitaire_data/src/weekly.rs +++ b/solitaire_data/src/weekly.rs @@ -4,7 +4,7 @@ //! increments matching counters in `PlayerProgress::weekly_goal_progress`. use chrono::{Datelike, NaiveDate}; -use solitaire_core::DrawMode; +use solitaire_core::DrawStockConfig; /// XP awarded each time a weekly goal is just completed. pub const WEEKLY_GOAL_XP: u64 = 75; @@ -36,7 +36,7 @@ pub struct WeeklyGoalDef { pub struct WeeklyGoalContext { pub time_seconds: u64, pub used_undo: bool, - pub draw_mode: DrawMode, + pub draw_mode: DrawStockConfig, } impl WeeklyGoalDef { @@ -47,7 +47,7 @@ impl WeeklyGoalDef { WeeklyGoalKind::WinGame => true, WeeklyGoalKind::WinWithoutUndo => !ctx.used_undo, WeeklyGoalKind::WinUnder { seconds } => ctx.time_seconds < seconds, - WeeklyGoalKind::WinDrawThree => ctx.draw_mode == DrawMode::DrawThree, + WeeklyGoalKind::WinDrawThree => ctx.draw_mode == DrawStockConfig::DrawThree, } } } @@ -106,7 +106,7 @@ mod tests { WeeklyGoalContext { time_seconds: time, used_undo: undo, - draw_mode: DrawMode::DrawOne, + draw_mode: DrawStockConfig::DrawOne, } } @@ -114,7 +114,7 @@ mod tests { WeeklyGoalContext { time_seconds: time, used_undo: false, - draw_mode: DrawMode::DrawThree, + draw_mode: DrawStockConfig::DrawThree, } } diff --git a/solitaire_engine/src/achievement_plugin.rs b/solitaire_engine/src/achievement_plugin.rs index 777dd63..5d987f2 100644 --- a/solitaire_engine/src/achievement_plugin.rs +++ b/solitaire_engine/src/achievement_plugin.rs @@ -819,7 +819,7 @@ mod tests { app.world_mut() .resource_mut::() .0 - .set_test_draw_mode(solitaire_core::DrawMode::DrawThree); + .set_test_draw_mode(solitaire_core::DrawStockConfig::DrawThree); app.world_mut().write_message(GameWonEvent { score: 500, @@ -868,7 +868,7 @@ mod tests { app.world_mut() .resource_mut::() .0 - .set_test_draw_mode(solitaire_core::DrawMode::DrawThree); + .set_test_draw_mode(solitaire_core::DrawStockConfig::DrawThree); app.world_mut().write_message(GameWonEvent { score: 500, @@ -1393,7 +1393,7 @@ mod tests { use crate::replay_playback::ReplayPlaybackState; use chrono::NaiveDate; - use solitaire_core::{DrawMode, game_state::GameMode}; + use solitaire_core::{DrawStockConfig, game_state::GameMode}; use solitaire_data::{Replay, ReplayMove}; /// Headless app variant that injects a default `ReplayPlaybackState` @@ -1409,7 +1409,7 @@ mod tests { fn dummy_replay() -> Replay { Replay::new( 1, - DrawMode::DrawOne, + DrawStockConfig::DrawOne, GameMode::Classic, 10, 100, diff --git a/solitaire_engine/src/auto_complete_plugin.rs b/solitaire_engine/src/auto_complete_plugin.rs index 5b50584..55fae1b 100644 --- a/solitaire_engine/src/auto_complete_plugin.rs +++ b/solitaire_engine/src/auto_complete_plugin.rs @@ -169,7 +169,7 @@ mod tests { use crate::table_plugin::TablePlugin; use solitaire_core::{Foundation, KlondikePile, Tableau}; use solitaire_core::card::{Deck, Rank, Suit}; - use solitaire_core::{DrawMode, game_state::GameState}; + use solitaire_core::{DrawStockConfig, game_state::GameState}; fn headless_app() -> App { let mut app = App::new(); @@ -183,7 +183,7 @@ mod tests { } fn seeded_state_with_auto_move() -> (GameState, (KlondikePile, KlondikePile)) { - let mut g = GameState::new(1, DrawMode::DrawOne); + let mut g = GameState::new(1, DrawStockConfig::DrawOne); g.set_test_stock_cards(Vec::new()); g.set_test_waste_cards(Vec::new()); for foundation in [ @@ -227,7 +227,7 @@ mod tests { #[test] fn detect_activates_when_auto_completable() { let mut app = headless_app(); - let mut g = GameState::new(42, DrawMode::DrawOne); + let mut g = GameState::new(42, DrawStockConfig::DrawOne); g.set_test_auto_completable(true); app.world_mut().resource_mut::().0 = g; app.world_mut().write_message(StateChangedEvent); diff --git a/solitaire_engine/src/card_plugin.rs b/solitaire_engine/src/card_plugin.rs index 2c1573b..b5caf43 100644 --- a/solitaire_engine/src/card_plugin.rs +++ b/solitaire_engine/src/card_plugin.rs @@ -18,7 +18,7 @@ use bevy::sprite::Anchor; use bevy::window::WindowResized; use solitaire_core::{Foundation, KlondikePile, Tableau}; use solitaire_core::card::{Card, Rank, Suit}; -use solitaire_core::{DrawMode, game_state::GameState}; +use solitaire_core::{DrawStockConfig, game_state::GameState}; use crate::animation_plugin::{CARD_ANIM_Z_LIFT, CardAnim, EffectiveSlideDuration}; use crate::card_animation::CardAnimation; @@ -789,8 +789,8 @@ fn sync_cards( // and its rank/suit peek behind the incoming card. let waste_buffer_id: Option = { let visible = match game.draw_mode() { - DrawMode::DrawOne => 1_usize, - DrawMode::DrawThree => 3_usize, + DrawStockConfig::DrawOne => 1_usize, + DrawStockConfig::DrawThree => 3_usize, }; let waste_cards = game.waste_cards(); (waste_cards.len() > visible) @@ -958,8 +958,8 @@ fn card_positions(game: &GameState, layout: &Layout) -> Vec<((Card, bool), Vec2, // shows up to 3 fanned in X (matching the standard Klondike presentation). let render_start = if is_waste { let visible = match game.draw_mode() { - DrawMode::DrawOne => 1_usize, - DrawMode::DrawThree => 3_usize, + DrawStockConfig::DrawOne => 1_usize, + DrawStockConfig::DrawThree => 3_usize, }; // Render one extra card so that the card sliding off the waste // during a draw animation is still present in the world at z=0 @@ -972,7 +972,7 @@ fn card_positions(game: &GameState, layout: &Layout) -> Vec<((Card, bool), Vec2, let mut y_offset = 0.0_f32; let rendered_len = cards[render_start..].len(); for (slot, (card, face_up)) in cards[render_start..].iter().enumerate() { - let x_offset = if is_waste && matches!(game.draw_mode(), DrawMode::DrawThree) { + let x_offset = if is_waste && matches!(game.draw_mode(), DrawStockConfig::DrawThree) { // When len > visible, slot 0 is a hidden buffer card kept at // x=0 to prevent a flash during the draw tween. When len ≤ // visible (small pile), every card is visible and should fan @@ -2566,7 +2566,7 @@ mod tests { #[test] fn card_positions_includes_all_52_cards_at_game_start() { // At game start waste is empty, so all 52 cards are across stock + tableau. - let g = GameState::new(42, solitaire_core::DrawMode::DrawOne); + let g = GameState::new(42, solitaire_core::DrawStockConfig::DrawOne); let layout = crate::layout::compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true); let positions = card_positions(&g, &layout); assert_eq!(positions.len(), 52); @@ -2574,8 +2574,8 @@ mod tests { #[test] fn waste_draw_one_only_renders_top_card() { - use solitaire_core::DrawMode; - let mut g = GameState::new(42, DrawMode::DrawOne); + use solitaire_core::DrawStockConfig; + let mut g = GameState::new(42, DrawStockConfig::DrawOne); // Draw 3 cards so the waste pile has 3 cards. for _ in 0..3 { let _ = g.draw(); @@ -2612,8 +2612,8 @@ mod tests { #[test] fn waste_draw_three_renders_up_to_three_fanned_cards() { - use solitaire_core::DrawMode; - let mut g = GameState::new(42, DrawMode::DrawThree); + use solitaire_core::DrawStockConfig; + let mut g = GameState::new(42, DrawStockConfig::DrawThree); // 5 draw() calls in Draw-Three mode accumulates multiple waste cards. for _ in 0..5 { let _ = g.draw(); @@ -2666,8 +2666,8 @@ mod tests { // Regression: slot.saturating_sub(1) always hid slot-0 even when the // pile was too small to have a buffer card, collapsing 2 visible cards // onto x=0 instead of fanning them. - use solitaire_core::DrawMode; - let mut g = GameState::new(42, DrawMode::DrawThree); + use solitaire_core::DrawStockConfig; + let mut g = GameState::new(42, DrawStockConfig::DrawThree); // Draw exactly once — in Draw-Three mode with a full stock this gives // 3 waste cards (still ≤ visible=3, so no hidden buffer needed). let _ = g.draw(); @@ -2709,8 +2709,8 @@ mod tests { /// top card so that hiding it (`Visibility::Hidden`) leaves no visible gap. #[test] fn waste_draw_one_buffer_card_at_same_xy_as_top() { - use solitaire_core::DrawMode; - let mut g = GameState::new(42, DrawMode::DrawOne); + use solitaire_core::DrawStockConfig; + let mut g = GameState::new(42, DrawStockConfig::DrawOne); // Draw 3 times so the waste pile has 3 cards and the buffer exists. for _ in 0..3 { let _ = g.draw(); @@ -2740,7 +2740,7 @@ mod tests { #[test] fn card_positions_tableau_cards_are_fanned_downward() { - let g = GameState::new(42, solitaire_core::DrawMode::DrawOne); + let g = GameState::new(42, solitaire_core::DrawStockConfig::DrawOne); let layout = crate::layout::compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true); let positions = card_positions(&g, &layout); @@ -3083,7 +3083,7 @@ mod tests { #[test] fn facedown_cards_use_tighter_fan_than_uniform_faceup_fan() { - let g = GameState::new(42, solitaire_core::DrawMode::DrawOne); + let g = GameState::new(42, solitaire_core::DrawStockConfig::DrawOne); let layout = crate::layout::compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true); let positions = card_positions(&g, &layout); @@ -3533,7 +3533,7 @@ mod tests { #[test] fn stock_card_count_helper_reads_zero_for_empty_stock() { - let g = GameState::new(42, solitaire_core::DrawMode::DrawOne); + let g = GameState::new(42, solitaire_core::DrawStockConfig::DrawOne); let mut g_empty_stock = g.clone(); g_empty_stock.set_test_stock_cards(Vec::new()); assert_eq!(stock_card_count(&g_empty_stock), 0); @@ -3805,8 +3805,8 @@ mod tests { #[test] fn waste_pile_cards_have_strictly_increasing_z() { - use solitaire_core::DrawMode; - let mut g = GameState::new(42, DrawMode::DrawThree); + use solitaire_core::DrawStockConfig; + let mut g = GameState::new(42, DrawStockConfig::DrawThree); for _ in 0..5 { let _ = g.draw(); } @@ -3849,8 +3849,8 @@ mod tests { /// offsets or flips the fan direction is caught immediately. #[test] fn waste_cards_do_not_overlap_stock_column_on_portrait() { - use solitaire_core::DrawMode; - let mut g = GameState::new(42, DrawMode::DrawThree); + use solitaire_core::DrawStockConfig; + let mut g = GameState::new(42, DrawStockConfig::DrawThree); for _ in 0..5 { let _ = g.draw(); } @@ -3885,8 +3885,8 @@ mod tests { #[test] fn waste_pile_draw_one_cards_have_distinct_z() { - use solitaire_core::DrawMode; - let mut g = GameState::new(42, DrawMode::DrawOne); + use solitaire_core::DrawStockConfig; + let mut g = GameState::new(42, DrawStockConfig::DrawOne); for _ in 0..3 { let _ = g.draw(); } diff --git a/solitaire_engine/src/challenge_plugin.rs b/solitaire_engine/src/challenge_plugin.rs index 887cdb3..e7bfce9 100644 --- a/solitaire_engine/src/challenge_plugin.rs +++ b/solitaire_engine/src/challenge_plugin.rs @@ -117,7 +117,7 @@ mod tests { use crate::game_plugin::GamePlugin; use crate::progress_plugin::ProgressPlugin; use crate::table_plugin::TablePlugin; - use solitaire_core::{DrawMode, game_state::GameState}; + use solitaire_core::{DrawStockConfig, game_state::GameState}; fn headless_app() -> App { let mut app = App::new(); @@ -135,7 +135,7 @@ mod tests { fn challenge_win_advances_index() { let mut app = headless_app(); app.world_mut().resource_mut::().0 = - GameState::new_with_mode(1, DrawMode::DrawOne, GameMode::Challenge); + GameState::new_with_mode(1, DrawStockConfig::DrawOne, GameMode::Challenge); app.world_mut().write_message(GameWonEvent { score: 500, @@ -224,7 +224,7 @@ mod tests { .0 .challenge_index = 2; app.world_mut().resource_mut::().0 = - GameState::new_with_mode(1, DrawMode::DrawOne, GameMode::Challenge); + GameState::new_with_mode(1, DrawStockConfig::DrawOne, GameMode::Challenge); app.world_mut().write_message(GameWonEvent { score: 500, diff --git a/solitaire_engine/src/cursor_plugin.rs b/solitaire_engine/src/cursor_plugin.rs index 2b0ff86..0726979 100644 --- a/solitaire_engine/src/cursor_plugin.rs +++ b/solitaire_engine/src/cursor_plugin.rs @@ -36,7 +36,7 @@ use bevy::prelude::*; use bevy::window::{CursorIcon, PrimaryWindow, SystemCursorIcon}; use solitaire_core::card::Card; use solitaire_core::{Foundation, KlondikePile, Tableau}; -use solitaire_core::{DrawMode, game_state::GameState}; +use solitaire_core::{DrawStockConfig, game_state::GameState}; use crate::card_plugin::RightClickHighlight; use crate::layout::{Layout, LayoutResource}; @@ -437,7 +437,7 @@ fn tableau_or_stack_pos( base.x, base.y - layout.card_size.y * layout.tableau_fan_frac * (index as f32), ) - } else if matches!(pile, KlondikePile::Stock) && game.draw_mode() == DrawMode::DrawThree { + } else if matches!(pile, KlondikePile::Stock) && game.draw_mode() == DrawStockConfig::DrawThree { let pile_len = game.waste_cards().len(); let visible_start = pile_len.saturating_sub(3); let slot = index.saturating_sub(visible_start) as f32; @@ -563,9 +563,9 @@ mod tests { #[test] fn cursor_over_draggable_returns_false_for_empty_game() { use crate::layout::compute_layout; - use solitaire_core::{DrawMode, game_state::GameState}; + use solitaire_core::{DrawStockConfig, game_state::GameState}; - let game = GameState::new(42, DrawMode::DrawOne); + let game = GameState::new(42, DrawStockConfig::DrawOne); let layout = compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true); // A cursor far off-screen should never hit anything. assert!(!cursor_over_draggable( @@ -581,7 +581,7 @@ mod tests { use crate::layout::compute_layout; use solitaire_core::card::{Card, Deck, Rank, Suit}; - use solitaire_core::{DrawMode, game_state::{GameMode, GameState}}; + use solitaire_core::{DrawStockConfig, game_state::{GameMode, GameState}}; /// Builds an `App` with `MinimalPlugins` and the overlay system /// registered, plus the resources the system needs. Callers @@ -629,7 +629,7 @@ mod tests { // 5 of Spades (black) onto Tableau(2)'s 6 of Clubs (also black) // — same colour family, illegal. Tableau(2) must NOT be // highlighted. - let mut game = GameState::new_with_mode(7, DrawMode::DrawOne, GameMode::Classic); + let mut game = GameState::new_with_mode(7, DrawStockConfig::DrawOne, GameMode::Classic); set_tableau_top( &mut game, 2, diff --git a/solitaire_engine/src/daily_challenge_plugin.rs b/solitaire_engine/src/daily_challenge_plugin.rs index 6da749a..d3ee1a2 100644 --- a/solitaire_engine/src/daily_challenge_plugin.rs +++ b/solitaire_engine/src/daily_challenge_plugin.rs @@ -362,7 +362,7 @@ mod tests { use crate::progress_plugin::ProgressPlugin; use crate::table_plugin::TablePlugin; #[allow(unused_imports)] - use solitaire_core::{DrawMode, game_state::GameState}; + use solitaire_core::{DrawStockConfig, game_state::GameState}; fn headless_app() -> App { let mut app = App::new(); @@ -391,7 +391,7 @@ mod tests { // Replace the GameState with one whose seed matches the daily seed. app.world_mut().resource_mut::().0 = - GameState::new(daily_seed, DrawMode::DrawOne); + GameState::new(daily_seed, DrawStockConfig::DrawOne); app.world_mut().write_message(GameWonEvent { score: 500, @@ -419,7 +419,7 @@ mod tests { let daily_seed = app.world().resource::().seed; // Use a deliberately different seed. app.world_mut().resource_mut::().0 = - GameState::new(daily_seed.wrapping_add(7777), DrawMode::DrawOne); + GameState::new(daily_seed.wrapping_add(7777), DrawStockConfig::DrawOne); app.world_mut().write_message(GameWonEvent { score: 500, @@ -442,7 +442,7 @@ mod tests { let mut app = headless_app(); let daily_seed = app.world().resource::().seed; app.world_mut().resource_mut::().0 = - GameState::new(daily_seed, DrawMode::DrawOne); + GameState::new(daily_seed, DrawStockConfig::DrawOne); app.world_mut().write_message(GameWonEvent { score: 500, diff --git a/solitaire_engine/src/feedback_anim_plugin.rs b/solitaire_engine/src/feedback_anim_plugin.rs index 3e38495..3e832dc 100644 --- a/solitaire_engine/src/feedback_anim_plugin.rs +++ b/solitaire_engine/src/feedback_anim_plugin.rs @@ -846,13 +846,13 @@ mod tests { fn shake_anim_skipped_under_reduce_motion() { use bevy::ecs::message::Messages; use solitaire_core::Tableau; - use solitaire_core::{DrawMode, game_state::GameState}; + use solitaire_core::{DrawStockConfig, game_state::GameState}; use solitaire_data::Settings; let mut app = App::new(); app.add_plugins(MinimalPlugins) .add_plugins(FeedbackAnimPlugin); - app.insert_resource(GameStateResource(GameState::new(1, DrawMode::DrawOne))); + app.insert_resource(GameStateResource(GameState::new(1, DrawStockConfig::DrawOne))); app.insert_resource(SettingsResource(Settings { reduce_motion_mode: true, ..Settings::default() @@ -900,13 +900,13 @@ mod tests { #[test] fn foundation_flourish_skipped_under_reduce_motion() { use bevy::ecs::message::Messages; - use solitaire_core::{DrawMode, game_state::GameState}; + use solitaire_core::{DrawStockConfig, game_state::GameState}; use solitaire_data::Settings; let mut app = App::new(); app.add_plugins(MinimalPlugins) .add_plugins(FeedbackAnimPlugin); - app.insert_resource(GameStateResource(GameState::new(1, DrawMode::DrawOne))); + app.insert_resource(GameStateResource(GameState::new(1, DrawStockConfig::DrawOne))); app.insert_resource(SettingsResource(Settings { reduce_motion_mode: true, ..Settings::default() diff --git a/solitaire_engine/src/game_plugin.rs b/solitaire_engine/src/game_plugin.rs index 620128e..d75cc7d 100644 --- a/solitaire_engine/src/game_plugin.rs +++ b/solitaire_engine/src/game_plugin.rs @@ -14,7 +14,7 @@ use bevy::prelude::*; 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_core::{DrawStockConfig, game_state::{GameMode, GameState}}; use solitaire_core::{DEFAULT_SOLVE_MOVES_BUDGET, DEFAULT_SOLVE_STATES_BUDGET}; #[allow(deprecated)] use solitaire_data::latest_replay_path; @@ -157,12 +157,12 @@ impl Plugin for GamePlugin { .is_some_and(|g| g.move_count() > 0 && !g.is_won()); let (initial_state, pending_restore) = if prompt_worthy { ( - GameState::new(seed_from_system_time(), DrawMode::DrawOne), + GameState::new(seed_from_system_time(), DrawStockConfig::DrawOne), saved, ) } else { ( - saved.unwrap_or_else(|| GameState::new(seed_from_system_time(), DrawMode::DrawOne)), + saved.unwrap_or_else(|| GameState::new(seed_from_system_time(), DrawStockConfig::DrawOne)), None, ) }; @@ -388,7 +388,7 @@ 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 { +pub(crate) fn choose_winnable_seed(initial_seed: u64, draw_mode: DrawStockConfig) -> u64 { let mut seed = initial_seed; for _ in 0..SOLVER_DEAL_RETRY_CAP { match GameState::solve_fresh_deal( @@ -830,8 +830,8 @@ fn handle_draw( Vec::new() } else { let draw_count = match game.0.draw_mode() { - DrawMode::DrawOne => 1_usize, - DrawMode::DrawThree => 3_usize, + DrawStockConfig::DrawOne => 1_usize, + DrawStockConfig::DrawThree => 3_usize, }; let n = stock.len(); let take = n.min(draw_count); @@ -1324,7 +1324,7 @@ mod tests { app.insert_resource(PendingRestoredGame(None)); // Override the system-time seed with a known value. app.world_mut().resource_mut::().0 = - GameState::new(seed, DrawMode::DrawOne); + GameState::new(seed, DrawStockConfig::DrawOne); app } @@ -1540,7 +1540,7 @@ mod tests { app.insert_resource(GameStatePath(Some(path.clone()))); // Override the seed so we can verify it was written. app.world_mut().resource_mut::().0 = - GameState::new(7654, DrawMode::DrawOne); + GameState::new(7654, DrawStockConfig::DrawOne); app.world_mut().write_message(AppExit::Success); app.update(); @@ -1559,7 +1559,7 @@ mod tests { let path = tmp_gs_path("new_game_delete"); // Pre-create a saved file. - save_game_state_to(&path, &GameState::new(1, DrawMode::DrawOne)).unwrap(); + save_game_state_to(&path, &GameState::new(1, DrawStockConfig::DrawOne)).unwrap(); assert!(path.exists()); let mut app = test_app(1); @@ -1693,7 +1693,7 @@ mod tests { fn has_legal_moves_returns_true_for_fresh_game() { // A fresh deal always has a non-empty stock (24 cards), so drawing // is always a legal move regardless of the initial face-up tableau cards. - let game = GameState::new(42, DrawMode::DrawOne); + let game = GameState::new(42, DrawStockConfig::DrawOne); assert!( has_legal_moves(&game), "fresh deal must contain at least one legal move" @@ -1707,7 +1707,7 @@ mod tests { // immediately placed. The game is only stuck when both stock AND waste // are exhausted and no visible card can be moved. use solitaire_core::card::{Card, Deck, Rank, Suit}; - let mut game = GameState::new(1, DrawMode::DrawOne); + let mut game = GameState::new(1, DrawStockConfig::DrawOne); for foundation in [ Foundation::Foundation1, Foundation::Foundation2, @@ -1743,7 +1743,7 @@ mod tests { #[test] fn has_legal_moves_returns_true_when_ace_can_go_to_foundation() { use solitaire_core::card::{Card, Deck, Rank, Suit}; - let mut game = GameState::new(1, DrawMode::DrawOne); + let mut game = GameState::new(1, DrawStockConfig::DrawOne); // Empty stock and waste so draw is NOT available. game.set_test_stock_cards(Vec::new()); @@ -1787,7 +1787,7 @@ mod tests { // card of its column the previous code would return false (softlock) // even though the player can still move that run. use solitaire_core::card::{Card, Deck, Rank, Suit}; - let mut game = GameState::new(1, DrawMode::DrawOne); + let mut game = GameState::new(1, DrawStockConfig::DrawOne); game.set_test_stock_cards(Vec::new()); game.set_test_waste_cards(Vec::new()); @@ -2185,7 +2185,7 @@ mod tests { assert_eq!(loaded.seed, 7654, "seed must match the live game state"); assert_eq!( loaded.draw_mode, - DrawMode::DrawOne, + DrawStockConfig::DrawOne, "draw_mode must be captured" ); assert_eq!( @@ -2326,7 +2326,7 @@ mod tests { "with solver toggle off, the requested seed must be honoured exactly" ); // Cross-check: the dealt tableau must match GameState::new(999) byte-for-byte. - let expected = GameState::new(999, DrawMode::DrawOne); + let expected = GameState::new(999, DrawStockConfig::DrawOne); for tableau in [ Tableau::Tableau1, Tableau::Tableau2, @@ -2403,7 +2403,7 @@ mod tests { // // Seed 394 was previously Unwinnable under the old DFS; now it resolves // as Inconclusive, so the helper must accept it immediately. - let chosen = choose_winnable_seed(394, DrawMode::DrawOne); + let chosen = choose_winnable_seed(394, DrawStockConfig::DrawOne); assert_eq!( chosen, 394, "seed 394 resolves as Inconclusive; choose_winnable_seed must accept it as-is" diff --git a/solitaire_engine/src/home_plugin.rs b/solitaire_engine/src/home_plugin.rs index 01bbfc4..fd8d89f 100644 --- a/solitaire_engine/src/home_plugin.rs +++ b/solitaire_engine/src/home_plugin.rs @@ -16,7 +16,7 @@ use bevy::input::ButtonInput; use bevy::input::mouse::{MouseScrollUnit, MouseWheel}; use bevy::prelude::*; -use solitaire_core::{DrawMode, game_state::DifficultyLevel}; +use solitaire_core::{DrawStockConfig, game_state::DifficultyLevel}; use solitaire_data::save_settings_to; use crate::challenge_plugin::CHALLENGE_UNLOCK_LEVEL; @@ -432,7 +432,7 @@ fn build_home_context<'a>( zen_best: stats.map_or(0, |s| s.0.zen_best_score), challenge_best: stats.map_or(0, |s| s.0.challenge_best_score), daily_today, - draw_mode: settings.map(|s| s.0.draw_mode).unwrap_or(DrawMode::DrawOne), + draw_mode: settings.map(|s| s.0.draw_mode).unwrap_or(DrawStockConfig::DrawOne), font_res, difficulty_expanded, last_difficulty: settings.and_then(|s| s.0.last_difficulty), @@ -620,9 +620,9 @@ fn handle_home_draw_mode_buttons( return; }; let target = if want_one { - DrawMode::DrawOne + DrawStockConfig::DrawOne } else { - DrawMode::DrawThree + DrawStockConfig::DrawThree }; if settings.0.draw_mode == target { return; // already in this mode — avoid a redundant respawn. @@ -857,7 +857,7 @@ struct HomeContext<'a> { challenge_best: u32, daily_streak: u32, daily_today: Option, - draw_mode: DrawMode, + draw_mode: DrawStockConfig, font_res: Option<&'a FontResource>, /// Whether the difficulty section header is currently expanded. difficulty_expanded: bool, @@ -1038,7 +1038,7 @@ fn spawn_draw_mode_row(parent: &mut ChildSpawnerCommands, ctx: &HomeContext<'_>) ..default() }; - let active_one = matches!(ctx.draw_mode, DrawMode::DrawOne); + let active_one = matches!(ctx.draw_mode, DrawStockConfig::DrawOne); parent .spawn(Node { diff --git a/solitaire_engine/src/hud_plugin.rs b/solitaire_engine/src/hud_plugin.rs index 8fc5319..631d511 100644 --- a/solitaire_engine/src/hud_plugin.rs +++ b/solitaire_engine/src/hud_plugin.rs @@ -10,7 +10,7 @@ use bevy::prelude::*; use bevy::window::WindowResized; use solitaire_core::{Foundation, KlondikePile, Tableau}; use solitaire_core::card::Suit; -use solitaire_core::{DrawMode, game_state::GameMode}; +use solitaire_core::{DrawStockConfig, game_state::GameMode}; use crate::auto_complete_plugin::AutoCompleteState; #[cfg(not(target_arch = "wasm32"))] @@ -2284,8 +2284,8 @@ fn update_hud( if let Ok(mut t) = mode_q.single_mut() { **t = match g.mode { GameMode::Classic => match g.draw_mode() { - DrawMode::DrawOne => String::new(), - DrawMode::DrawThree => "Draw 3".to_string(), + DrawStockConfig::DrawOne => String::new(), + DrawStockConfig::DrawThree => "Draw 3".to_string(), }, GameMode::Zen => "ZEN".to_string(), GameMode::Challenge => "CHALLENGE".to_string(), @@ -2334,7 +2334,7 @@ fn update_hud( // --- Draw-cycle indicator (Draw-Three mode only) --- if let Ok(mut t) = draw_cycle_q.single_mut() { - **t = if g.is_won() || g.draw_mode() != DrawMode::DrawThree { + **t = if g.is_won() || g.draw_mode() != DrawStockConfig::DrawThree { // Hide when not in Draw-Three or after the game is won. String::new() } else { @@ -2726,7 +2726,7 @@ mod tests { use crate::game_plugin::GamePlugin; use crate::table_plugin::TablePlugin; use chrono::Local; - use solitaire_core::{DrawMode, game_state::GameState}; + use solitaire_core::{DrawStockConfig, game_state::GameState}; fn headless_app() -> App { let mut app = App::new(); @@ -2747,7 +2747,7 @@ mod tests { fn update_hud_runs_after_game_mutation_without_panic() { let mut app = headless_app(); app.world_mut().resource_mut::().0 = - GameState::new(42, DrawMode::DrawOne); + GameState::new(42, DrawStockConfig::DrawOne); app.update(); } @@ -2784,7 +2784,7 @@ mod tests { use solitaire_core::game_state::GameMode; let mut app = headless_app(); app.world_mut().resource_mut::().0 = - GameState::new_with_mode(42, DrawMode::DrawThree, GameMode::Classic); + GameState::new_with_mode(42, DrawStockConfig::DrawThree, GameMode::Classic); app.update(); assert_eq!(read_hud_text::(&mut app), "Draw 3"); } @@ -2794,7 +2794,7 @@ mod tests { use solitaire_core::game_state::GameMode; let mut app = headless_app(); app.world_mut().resource_mut::().0 = - GameState::new_with_mode(42, DrawMode::DrawOne, GameMode::Zen); + GameState::new_with_mode(42, DrawStockConfig::DrawOne, GameMode::Zen); app.update(); // Zen mode spec: "No score display" → text must be empty. assert_eq!(read_hud_text::(&mut app), ""); @@ -3037,7 +3037,7 @@ mod tests { let mut app = headless_app(); // Draw-One, no recycles yet — text must be empty. app.world_mut().resource_mut::().0 = - GameState::new(42, DrawMode::DrawOne); + GameState::new(42, DrawStockConfig::DrawOne); app.update(); assert_eq!(read_hud_text::(&mut app), ""); } @@ -3047,7 +3047,7 @@ mod tests { let mut app = headless_app(); // Draw-Three, no recycles yet — text must also be empty. app.world_mut().resource_mut::().0 = - GameState::new(42, DrawMode::DrawThree); + GameState::new(42, DrawStockConfig::DrawThree); app.update(); assert_eq!(read_hud_text::(&mut app), ""); } @@ -3055,7 +3055,7 @@ mod tests { #[test] fn recycles_hud_shows_count_draw_three() { let mut app = headless_app(); - let mut gs = GameState::new(42, DrawMode::DrawThree); + let mut gs = GameState::new(42, DrawStockConfig::DrawThree); gs.force_test_recycles(3); app.world_mut().resource_mut::().0 = gs; app.update(); @@ -3066,7 +3066,7 @@ mod tests { fn recycles_hud_shows_count_draw_one() { let mut app = headless_app(); // Draw-One with recycle_count > 0 must now show the counter too. - let mut gs = GameState::new(42, DrawMode::DrawOne); + let mut gs = GameState::new(42, DrawStockConfig::DrawOne); gs.force_test_recycles(2); app.world_mut().resource_mut::().0 = gs; app.update(); diff --git a/solitaire_engine/src/input_plugin.rs b/solitaire_engine/src/input_plugin.rs index 1e876f9..3874dfe 100644 --- a/solitaire_engine/src/input_plugin.rs +++ b/solitaire_engine/src/input_plugin.rs @@ -54,7 +54,7 @@ use crate::settings_plugin::SettingsResource; use crate::time_attack_plugin::TimeAttackResource; use crate::touch_selection_plugin::TouchSelectionState; use crate::ui_theme::{MOTION_DRAG_REJECT_SECS, STATE_SUCCESS, STATE_WARNING}; -use solitaire_core::DrawMode; +use solitaire_core::DrawStockConfig; /// System-set labels used to anchor external systems relative to the touch /// drag pipeline without duplicating the internal chain ordering. @@ -1173,7 +1173,7 @@ fn card_position( y_offset -= layout.card_size.y * step; } Vec2::new(base.x, base.y + y_offset) - } else if matches!(pile, KlondikePile::Stock) && game.draw_mode() == DrawMode::DrawThree { + } else if matches!(pile, KlondikePile::Stock) && game.draw_mode() == DrawStockConfig::DrawThree { // In Draw-Three mode the top 3 waste cards are fanned in X to match // card_plugin::card_positions(). Hit-testing must use the same offsets // so clicking the visually rightmost (top) card actually registers. @@ -1830,7 +1830,7 @@ mod tests { use super::*; use crate::layout::compute_layout; use solitaire_core::{Foundation, Tableau}; - use solitaire_core::{DrawMode, game_state::GameState}; + use solitaire_core::{DrawStockConfig, game_state::GameState}; fn clear_test_piles(game: &mut GameState) { game.set_test_stock_cards(Vec::new()); @@ -1898,7 +1898,7 @@ mod tests { #[test] fn find_draggable_picks_top_of_tableau() { - let game = GameState::new(42, DrawMode::DrawOne); + let game = GameState::new(42, DrawStockConfig::DrawOne); let layout = compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true); // In tableau 6, the visually topmost card is the last (face-up) one. @@ -1912,7 +1912,7 @@ mod tests { #[test] fn find_draggable_skips_face_down_cards() { - let game = GameState::new(42, DrawMode::DrawOne); + let game = GameState::new(42, DrawStockConfig::DrawOne); let layout = compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true); // Tableau 6 has 7 cards: 6 face-down (indices 0..5) + 1 face-up at @@ -1934,7 +1934,7 @@ mod tests { // at 0.12 — so for any column with face-down cards above the // face-up bottom card, clicking the visible card face missed the // hit-test box and only the bottom strip of the card responded. - let game = GameState::new(42, DrawMode::DrawOne); + let game = GameState::new(42, DrawStockConfig::DrawOne); let layout = compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true); // Tableau 6 starts with 6 face-down + 1 face-up. The face-up card @@ -1952,7 +1952,7 @@ mod tests { #[test] fn find_draggable_returns_run_when_picking_mid_stack() { // Manually construct a tableau with three face-up cards all stacked. - let mut game = GameState::new(1, DrawMode::DrawOne); + let mut game = GameState::new(1, DrawStockConfig::DrawOne); use solitaire_core::card::Deck as D; use solitaire_core::card::{Card, Rank, Suit}; let king = Card::new(D::Deck1, Suit::Spades, Rank::King); @@ -1979,7 +1979,7 @@ mod tests { #[test] fn find_draggable_skips_non_top_waste_card() { - let mut game = GameState::new(1, DrawMode::DrawOne); + let mut game = GameState::new(1, DrawStockConfig::DrawOne); use solitaire_core::card::Deck as D; use solitaire_core::card::{Card, Rank, Suit}; let two_spades = Card::new(D::Deck1, Suit::Spades, Rank::Two); @@ -1998,7 +1998,7 @@ mod tests { #[test] fn find_drop_target_hits_empty_tableau_pile_marker() { - let game = GameState::new(42, DrawMode::DrawOne); + let game = GameState::new(42, DrawStockConfig::DrawOne); let layout = compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true); // Move all cards out of tableau 0 so its marker is the only drop area. let mut game = game; @@ -2015,7 +2015,7 @@ mod tests { #[test] fn find_drop_target_returns_none_for_origin() { - let game = GameState::new(42, DrawMode::DrawOne); + let game = GameState::new(42, DrawStockConfig::DrawOne); let layout = compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true); let pos = layout.pile_positions[&KlondikePile::Tableau(Tableau::Tableau4)]; let target = find_drop_target( @@ -2029,7 +2029,7 @@ mod tests { #[test] fn pile_drop_rect_extends_for_tableau_with_cards() { - let game = GameState::new(42, DrawMode::DrawOne); + let game = GameState::new(42, DrawStockConfig::DrawOne); let layout = compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true); // Tableau 6 has 7 cards. let (_, size) = pile_drop_rect(&KlondikePile::Tableau(Tableau::Tableau7), &layout, &game); @@ -2046,8 +2046,8 @@ mod tests { fn find_draggable_draw_three_waste_top_card_hit_at_fanned_position() { use solitaire_core::card::Deck as D; use solitaire_core::card::{Card, Rank, Suit}; - use solitaire_core::{DrawMode, game_state::GameMode}; - let mut game = GameState::new_with_mode(1, DrawMode::DrawThree, GameMode::Classic); + use solitaire_core::{DrawStockConfig, game_state::GameMode}; + let mut game = GameState::new_with_mode(1, DrawStockConfig::DrawThree, GameMode::Classic); // Three waste cards; top (four_clubs) is rightmost in the fan. let two_spades = Card::new(D::Deck1, Suit::Spades, Rank::Two); let three_hearts = Card::new(D::Deck1, Suit::Hearts, Rank::Three); @@ -2072,7 +2072,7 @@ mod tests { #[test] fn find_draggable_returns_none_for_click_on_empty_pile() { - let mut game = GameState::new(42, DrawMode::DrawOne); + let mut game = GameState::new(42, DrawStockConfig::DrawOne); let layout = compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true); // Clear tableau 0 so it's an empty slot. game.set_test_tableau_cards(Tableau::Tableau1, Vec::new()); @@ -2086,7 +2086,7 @@ mod tests { #[test] fn pile_drop_rect_is_card_sized_for_non_tableau() { - let game = GameState::new(42, DrawMode::DrawOne); + let game = GameState::new(42, DrawStockConfig::DrawOne); let layout = compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true); for pile in [ KlondikePile::Stock, @@ -2105,7 +2105,7 @@ mod tests { fn best_destination_returns_none_when_no_legal_move() { use solitaire_core::card::Deck as D; use solitaire_core::card::{Card, Rank, Suit}; - let mut game = GameState::new(1, DrawMode::DrawOne); + let mut game = GameState::new(1, DrawStockConfig::DrawOne); // Clear everything except one card that has nowhere to go. clear_test_piles(&mut game); @@ -2123,7 +2123,7 @@ mod tests { fn best_tableau_destination_for_stack_skips_source_pile() { use solitaire_core::card::Deck as D; use solitaire_core::card::{Card, Rank, Suit}; - let mut game = GameState::new(1, DrawMode::DrawOne); + let mut game = GameState::new(1, DrawStockConfig::DrawOne); clear_test_piles(&mut game); @@ -2149,7 +2149,7 @@ mod tests { fn best_tableau_destination_for_stack_returns_none_when_no_legal_move() { use solitaire_core::card::Deck as D; use solitaire_core::card::{Card, Rank, Suit}; - let mut game = GameState::new(1, DrawMode::DrawOne); + let mut game = GameState::new(1, DrawStockConfig::DrawOne); clear_test_piles(&mut game); @@ -2178,7 +2178,7 @@ mod tests { fn find_hint_finds_ace_to_foundation() { use solitaire_core::card::Deck as D; use solitaire_core::card::{Card, Rank, Suit}; - let mut game = GameState::new(1, DrawMode::DrawOne); + let mut game = GameState::new(1, DrawStockConfig::DrawOne); // Place Ace of Clubs on top of tableau 0. clear_test_piles(&mut game); @@ -2222,7 +2222,7 @@ mod tests { fn all_hints_suggests_draw_when_no_moves_and_stock_nonempty() { use solitaire_core::card::Deck as D; use solitaire_core::card::{Card, Rank, Suit}; - let mut game = GameState::new(1, DrawMode::DrawOne); + let mut game = GameState::new(1, DrawStockConfig::DrawOne); // Remove all foundation, tableau, and waste cards so no pile-to-pile // move exists. Leave one card in the stock. @@ -2431,7 +2431,7 @@ mod tests { app.insert_resource(crate::layout::LayoutResource( crate::layout::compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true), )); - app.insert_resource(GameStateResource(GameState::new(42, DrawMode::DrawOne))); + app.insert_resource(GameStateResource(GameState::new(42, DrawStockConfig::DrawOne))); app.add_systems(Update, handle_keyboard_hint); // Simulate the H key being pressed this frame. diff --git a/solitaire_engine/src/pause_plugin.rs b/solitaire_engine/src/pause_plugin.rs index 4a38976..55a8307 100644 --- a/solitaire_engine/src/pause_plugin.rs +++ b/solitaire_engine/src/pause_plugin.rs @@ -21,7 +21,7 @@ //! active opens the overlay as normal. use bevy::prelude::*; -use solitaire_core::DrawMode; +use solitaire_core::DrawStockConfig; use solitaire_data::save_game_state_to; use crate::events::{ @@ -86,10 +86,10 @@ struct ForfeitConfirmButton; /// Returns the human-readable label for a draw mode. /// /// Used on the pause overlay draw-mode toggle button. -pub fn draw_mode_label(mode: DrawMode) -> &'static str { +pub fn draw_mode_label(mode: DrawStockConfig) -> &'static str { match mode { - DrawMode::DrawOne => "Draw 1", - DrawMode::DrawThree => "Draw 3", + DrawStockConfig::DrawOne => "Draw 1", + DrawStockConfig::DrawThree => "Draw 3", } } @@ -273,9 +273,9 @@ fn handle_pause_draw_buttons( } let Some(mut settings) = settings else { return }; let new_mode = if pressed_one { - DrawMode::DrawOne + DrawStockConfig::DrawOne } else { - DrawMode::DrawThree + DrawStockConfig::DrawThree }; if settings.0.draw_mode == new_mode { return; @@ -477,7 +477,7 @@ fn spawn_pause_screen( commands: &mut Commands, level: Option, streak: Option, - draw_mode: Option, + draw_mode: Option, font_res: Option<&FontResource>, ) { spawn_modal(commands, PauseScreen, ui_theme::Z_PAUSE, |card| { @@ -516,7 +516,7 @@ fn spawn_pause_screen( /// `Tertiary` (recessed), giving an obvious selection state at a glance. fn spawn_draw_mode_row( parent: &mut ChildSpawnerCommands, - mode: DrawMode, + mode: DrawStockConfig, font_res: Option<&FontResource>, ) { let label_font = TextFont { @@ -530,8 +530,8 @@ fn spawn_draw_mode_row( ..default() }; let (one_variant, three_variant) = match mode { - DrawMode::DrawOne => (ButtonVariant::Secondary, ButtonVariant::Tertiary), - DrawMode::DrawThree => (ButtonVariant::Tertiary, ButtonVariant::Secondary), + DrawStockConfig::DrawOne => (ButtonVariant::Secondary, ButtonVariant::Tertiary), + DrawStockConfig::DrawThree => (ButtonVariant::Tertiary, ButtonVariant::Secondary), }; parent .spawn(Node { @@ -800,20 +800,20 @@ mod tests { #[test] fn draw_mode_label_draw_one() { - assert_eq!(draw_mode_label(DrawMode::DrawOne), "Draw 1"); + assert_eq!(draw_mode_label(DrawStockConfig::DrawOne), "Draw 1"); } #[test] fn draw_mode_label_draw_three() { - assert_eq!(draw_mode_label(DrawMode::DrawThree), "Draw 3"); + assert_eq!(draw_mode_label(DrawStockConfig::DrawThree), "Draw 3"); } /// Both variants are covered so the match is exhaustive — this test would - /// fail to compile if a new DrawMode variant were added without updating + /// fail to compile if a new DrawStockConfig variant were added without updating /// `draw_mode_label`. #[test] fn draw_mode_label_covers_all_variants() { - for mode in [DrawMode::DrawOne, DrawMode::DrawThree] { + for mode in [DrawStockConfig::DrawOne, DrawStockConfig::DrawThree] { let label = draw_mode_label(mode); assert!( !label.is_empty(), @@ -842,7 +842,7 @@ mod tests { app.world_mut() .resource_mut::() .0 - .draw_mode = DrawMode::DrawOne; + .draw_mode = DrawStockConfig::DrawOne; // Set paused so handle_pause_draw_toggle acts. app.world_mut().resource_mut::().0 = true; @@ -856,7 +856,7 @@ mod tests { let mode = &app.world().resource::().0.draw_mode; assert_eq!( *mode, - DrawMode::DrawThree, + DrawStockConfig::DrawThree, "pressing Draw 3 must set mode to DrawThree" ); @@ -869,7 +869,7 @@ mod tests { let mode2 = &app.world().resource::().0.draw_mode; assert_eq!( *mode2, - DrawMode::DrawOne, + DrawStockConfig::DrawOne, "pressing Draw 1 must set mode to DrawOne" ); @@ -965,11 +965,11 @@ mod tests { /// Provides a fresh `GameStateResource` (not won) so the modal can /// open. `move_count` doesn't matter — the gate is just `!is_won`. fn forfeit_app() -> App { - use solitaire_core::{DrawMode, game_state::GameState}; + use solitaire_core::{DrawStockConfig, game_state::GameState}; let mut app = App::new(); app.add_plugins(MinimalPlugins).add_plugins(PausePlugin); app.init_resource::>(); - app.insert_resource(GameStateResource(GameState::new(1, DrawMode::DrawOne))); + app.insert_resource(GameStateResource(GameState::new(1, DrawStockConfig::DrawOne))); app.update(); app } @@ -1020,11 +1020,11 @@ mod tests { /// hotkey was received but is currently a no-op. #[test] fn forfeit_request_emits_toast_and_skips_modal_when_game_is_won() { - use solitaire_core::{DrawMode, game_state::GameState}; + use solitaire_core::{DrawStockConfig, game_state::GameState}; let mut app = App::new(); app.add_plugins(MinimalPlugins).add_plugins(PausePlugin); app.init_resource::>(); - let mut game = GameState::new(1, DrawMode::DrawOne); + let mut game = GameState::new(1, DrawStockConfig::DrawOne); game.set_test_won(true); app.insert_resource(GameStateResource(game)); app.update(); diff --git a/solitaire_engine/src/pending_hint.rs b/solitaire_engine/src/pending_hint.rs index 1e9fac0..9e6fae5 100644 --- a/solitaire_engine/src/pending_hint.rs +++ b/solitaire_engine/src/pending_hint.rs @@ -180,7 +180,7 @@ mod tests { use crate::input_plugin::HintSolverConfig; use solitaire_core::{Foundation, KlondikePile, Tableau}; use solitaire_core::card::{Card, Deck, Rank, Suit}; - use solitaire_core::{DrawMode, game_state::GameState}; + use solitaire_core::{DrawStockConfig, game_state::GameState}; /// Build a minimal Bevy app exercising only the polling system /// and the resources/messages it touches. @@ -209,7 +209,7 @@ mod tests { /// foundations hold A..Q for each suit, four Kings sit on /// tableau columns 0..3, stock and waste empty. fn near_finished_state() -> GameState { - let mut game = GameState::new(1, DrawMode::DrawOne); + let mut game = GameState::new(1, DrawStockConfig::DrawOne); game.set_test_stock_cards(Vec::new()); game.set_test_waste_cards(Vec::new()); for foundation in [ diff --git a/solitaire_engine/src/play_by_seed_plugin.rs b/solitaire_engine/src/play_by_seed_plugin.rs index 35720a1..2dff997 100644 --- a/solitaire_engine/src/play_by_seed_plugin.rs +++ b/solitaire_engine/src/play_by_seed_plugin.rs @@ -23,7 +23,7 @@ use bevy::input::ButtonInput; use bevy::prelude::*; use bevy::tasks::{AsyncComputeTaskPool, Task, futures_lite::future}; -use solitaire_core::DrawMode; +use solitaire_core::DrawStockConfig; use solitaire_core::game_state::GameState; use solitaire_core::{DEFAULT_SOLVE_MOVES_BUDGET, DEFAULT_SOLVE_STATES_BUDGET, SolveOutcome}; @@ -340,7 +340,7 @@ fn tick_debounce_and_spawn_solver_task( let draw_mode = settings .as_ref() - .map_or(DrawMode::DrawOne, |s| s.0.draw_mode); + .map_or(DrawStockConfig::DrawOne, |s| s.0.draw_mode); let task = AsyncComputeTaskPool::get().spawn(async move { GameState::solve_fresh_deal( seed, diff --git a/solitaire_engine/src/radial_menu.rs b/solitaire_engine/src/radial_menu.rs index ba02cca..31faf3f 100644 --- a/solitaire_engine/src/radial_menu.rs +++ b/solitaire_engine/src/radial_menu.rs @@ -797,7 +797,7 @@ mod tests { use crate::layout::compute_layout; use bevy::ecs::message::Messages; use solitaire_core::card::{Card as CoreCard, Deck, Rank, Suit}; - use solitaire_core::{DrawMode, game_state::GameState}; + use solitaire_core::{DrawStockConfig, game_state::GameState}; /// Build a minimal Bevy app wired with `RadialMenuPlugin` and the /// resources / messages it depends on. No window, no camera — the @@ -820,7 +820,7 @@ mod tests { /// destination — Foundation(0) — under the standard rules /// (`can_place_on_foundation` accepts the Ace on an empty foundation). fn ace_only_state() -> GameState { - let mut g = GameState::new(0, DrawMode::DrawOne); + let mut g = GameState::new(0, DrawStockConfig::DrawOne); // Wipe everything. g.set_test_stock_cards(Vec::new()); g.set_test_waste_cards(Vec::new()); @@ -854,7 +854,7 @@ mod tests { /// Place a face-down King on Tableau(0). `find_top_face_up_card_at` /// must skip it. fn face_down_only_state() -> GameState { - let mut g = GameState::new(0, DrawMode::DrawOne); + let mut g = GameState::new(0, DrawStockConfig::DrawOne); g.set_test_stock_cards(Vec::new()); g.set_test_waste_cards(Vec::new()); for foundation in [ diff --git a/solitaire_engine/src/replay_overlay/tests.rs b/solitaire_engine/src/replay_overlay/tests.rs index cde2413..e22e845 100644 --- a/solitaire_engine/src/replay_overlay/tests.rs +++ b/solitaire_engine/src/replay_overlay/tests.rs @@ -2,7 +2,7 @@ use super::*; use chrono::NaiveDate; use solitaire_core::{Foundation, KlondikePile, Tableau}; use solitaire_core::card::{Rank, Suit}; -use solitaire_core::{DrawMode, game_state::GameMode}; +use solitaire_core::{DrawStockConfig, game_state::GameMode}; use solitaire_core::klondike_adapter::{SavedKlondikePile, SavedTableau}; use solitaire_data::{Replay, ReplayMove}; @@ -13,7 +13,7 @@ use solitaire_data::{Replay, ReplayMove}; fn synthetic_replay(move_count: usize) -> Replay { Replay::new( 42, - DrawMode::DrawOne, + DrawStockConfig::DrawOne, GameMode::Classic, 120, 1_000, @@ -2314,7 +2314,7 @@ fn format_suit_glyph_all_suits() { fn format_foundations_row_empty_board() { let game = solitaire_core::game_state::GameState::new_with_mode( 42, - solitaire_core::DrawMode::DrawOne, + solitaire_core::DrawStockConfig::DrawOne, solitaire_core::game_state::GameMode::Classic, ); assert_eq!(format_foundations_row(&game), "F: -- -- -- --"); @@ -2326,7 +2326,7 @@ fn format_foundations_row_empty_board() { fn format_stock_waste_row_initial_state() { let game = solitaire_core::game_state::GameState::new_with_mode( 42, - solitaire_core::DrawMode::DrawOne, + solitaire_core::DrawStockConfig::DrawOne, solitaire_core::game_state::GameMode::Classic, ); let text = format_stock_waste_row(&game); diff --git a/solitaire_engine/src/replay_playback.rs b/solitaire_engine/src/replay_playback.rs index ecca062..3a9f180 100644 --- a/solitaire_engine/src/replay_playback.rs +++ b/solitaire_engine/src/replay_playback.rs @@ -556,7 +556,7 @@ mod tests { use bevy::time::TimeUpdateStrategy; use chrono::NaiveDate; use solitaire_core::{KlondikePile, Tableau}; - use solitaire_core::{DrawMode, game_state::GameMode}; + use solitaire_core::{DrawStockConfig, game_state::GameMode}; use solitaire_core::klondike_adapter::{SavedKlondikePile, SavedTableau}; use std::time::Duration; @@ -598,7 +598,7 @@ mod tests { fn sample_replay_three_moves() -> Replay { Replay::new( 12345, - DrawMode::DrawOne, + DrawStockConfig::DrawOne, GameMode::Classic, 60, 500, @@ -771,7 +771,7 @@ mod tests { let mut app = headless_app(); let one_move = Replay::new( 42, - DrawMode::DrawOne, + DrawStockConfig::DrawOne, GameMode::Classic, 10, 100, @@ -880,7 +880,7 @@ mod tests { fn ten_draws_replay() -> Replay { Replay::new( 7, - DrawMode::DrawOne, + DrawStockConfig::DrawOne, GameMode::Classic, 10, 100, diff --git a/solitaire_engine/src/selection_plugin.rs b/solitaire_engine/src/selection_plugin.rs index b84bd95..6eb31ee 100644 --- a/solitaire_engine/src/selection_plugin.rs +++ b/solitaire_engine/src/selection_plugin.rs @@ -935,7 +935,7 @@ mod tests { use bevy::ecs::message::Messages; use solitaire_core::card::{Card, Deck, Rank, Suit}; - use solitaire_core::{DrawMode, game_state::GameState}; + use solitaire_core::{DrawStockConfig, game_state::GameState}; /// Build a minimal app with `SelectionPlugin` only — no GamePlugin, no /// AssetServer. The `MoveRequestEvent` / `StateChangedEvent` / @@ -968,7 +968,7 @@ mod tests { /// Ace first). It cannot go to an empty tableau (only Kings). /// Empty tableaus T3..T6 only accept Kings, so they are filtered out. fn deterministic_state() -> GameState { - let mut g = GameState::new(0, DrawMode::DrawOne); + let mut g = GameState::new(0, DrawStockConfig::DrawOne); // Clear stock, waste, all tableaus. g.set_test_stock_cards(Vec::new()); g.set_test_waste_cards(Vec::new()); diff --git a/solitaire_engine/src/settings_plugin.rs b/solitaire_engine/src/settings_plugin.rs index 32dbe19..0688680 100644 --- a/solitaire_engine/src/settings_plugin.rs +++ b/solitaire_engine/src/settings_plugin.rs @@ -15,7 +15,7 @@ use bevy::input::mouse::{MouseScrollUnit, MouseWheel}; use bevy::prelude::*; use bevy::ui::{ComputedNode, UiGlobalTransform}; use bevy::window::{WindowMoved, WindowResized}; -use solitaire_core::DrawMode; +use solitaire_core::DrawStockConfig; use solitaire_data::{ AnimSpeed, REPLAY_MOVE_INTERVAL_STEP_SECS, Settings, TIME_BONUS_MULTIPLIER_STEP, TOOLTIP_DELAY_STEP_SECS, WindowGeometry, load_settings_from, save_settings_to, settings::Theme, @@ -1086,8 +1086,8 @@ fn handle_settings_buttons( } SettingsButton::ToggleDrawMode => { settings.0.draw_mode = match settings.0.draw_mode { - DrawMode::DrawOne => DrawMode::DrawThree, - DrawMode::DrawThree => DrawMode::DrawOne, + DrawStockConfig::DrawOne => DrawStockConfig::DrawThree, + DrawStockConfig::DrawThree => DrawStockConfig::DrawOne, }; persist(&path, &settings.0); changed.write(SettingsChangedEvent(settings.0.clone())); @@ -1310,10 +1310,10 @@ fn handle_sync_buttons( } } -fn draw_mode_label(mode: &DrawMode) -> String { +fn draw_mode_label(mode: &DrawStockConfig) -> String { match mode { - DrawMode::DrawOne => "Draw 1".into(), - DrawMode::DrawThree => "Draw 3".into(), + DrawStockConfig::DrawOne => "Draw 1".into(), + DrawStockConfig::DrawThree => "Draw 3".into(), } } diff --git a/solitaire_engine/src/stats_plugin.rs b/solitaire_engine/src/stats_plugin.rs index 834a8b8..edbc3c9 100644 --- a/solitaire_engine/src/stats_plugin.rs +++ b/solitaire_engine/src/stats_plugin.rs @@ -1327,7 +1327,7 @@ mod tests { app.world_mut() .resource_mut::() .0 - .set_test_draw_mode(solitaire_core::DrawMode::DrawThree); + .set_test_draw_mode(solitaire_core::DrawStockConfig::DrawThree); app.world_mut().write_message(GameWonEvent { score: 500, @@ -1952,7 +1952,7 @@ mod tests { let date = chrono::NaiveDate::from_ymd_opt(2026, 5, 8).expect("valid date"); let mut r = solitaire_data::Replay::new( 1, - solitaire_core::DrawMode::DrawOne, + solitaire_core::DrawStockConfig::DrawOne, solitaire_core::game_state::GameMode::Classic, time_seconds, 0, diff --git a/solitaire_engine/src/sync_plugin.rs b/solitaire_engine/src/sync_plugin.rs index 64f7814..68a31b9 100644 --- a/solitaire_engine/src/sync_plugin.rs +++ b/solitaire_engine/src/sync_plugin.rs @@ -604,7 +604,7 @@ mod tests { /// would silently drop the link. #[test] fn upload_result_writes_share_url_into_replay_and_persists() { - use solitaire_core::{DrawMode, game_state::GameMode}; + use solitaire_core::{DrawStockConfig, game_state::GameMode}; use solitaire_data::{ Replay, ReplayHistory, load_replay_history_from, save_replay_history_to, }; @@ -617,7 +617,7 @@ mod tests { // share_url — the upload-poll path must populate it. let initial = Replay::new( 42, - DrawMode::DrawOne, + DrawStockConfig::DrawOne, GameMode::Classic, 60, 500, diff --git a/solitaire_engine/src/time_attack_plugin.rs b/solitaire_engine/src/time_attack_plugin.rs index 97a8910..4c3c0cf 100644 --- a/solitaire_engine/src/time_attack_plugin.rs +++ b/solitaire_engine/src/time_attack_plugin.rs @@ -299,7 +299,7 @@ mod tests { use crate::game_plugin::GamePlugin; use crate::progress_plugin::ProgressPlugin; use crate::table_plugin::TablePlugin; - use solitaire_core::{DrawMode, game_state::GameState}; + use solitaire_core::{DrawStockConfig, game_state::GameState}; fn headless_app() -> App { let mut app = App::new(); @@ -430,7 +430,7 @@ mod tests { }; // The current game must be in TimeAttack mode for auto-deal to fire. app.world_mut().resource_mut::().0 = - GameState::new_with_mode(7, DrawMode::DrawOne, GameMode::TimeAttack); + GameState::new_with_mode(7, DrawStockConfig::DrawOne, GameMode::TimeAttack); app.world_mut().write_message(GameWonEvent { score: 500, @@ -454,7 +454,7 @@ mod tests { let mut app = headless_app(); // Default session is inactive. Game is TimeAttack mode — still no count. app.world_mut().resource_mut::().0 = - GameState::new_with_mode(7, DrawMode::DrawOne, GameMode::TimeAttack); + GameState::new_with_mode(7, DrawStockConfig::DrawOne, GameMode::TimeAttack); app.world_mut().write_message(GameWonEvent { score: 500, diff --git a/solitaire_engine/src/win_summary_plugin.rs b/solitaire_engine/src/win_summary_plugin.rs index 8f337c4..13fe48a 100644 --- a/solitaire_engine/src/win_summary_plugin.rs +++ b/solitaire_engine/src/win_summary_plugin.rs @@ -1205,7 +1205,7 @@ mod tests { .insert_resource(StatsResource(StatsSnapshot::default())) .insert_resource(GameStateResource(GameState::new( 0, - solitaire_core::DrawMode::DrawOne, + solitaire_core::DrawStockConfig::DrawOne, ))) .insert_resource(ProgressResource(PlayerProgress::default())); app.update(); @@ -1534,9 +1534,9 @@ mod tests { .challenge_index = 4; // Switch game mode to Challenge. { - use solitaire_core::DrawMode; + use solitaire_core::DrawStockConfig; app.world_mut().resource_mut::().0 = - GameState::new_with_mode(1, DrawMode::DrawOne, GameMode::Challenge); + GameState::new_with_mode(1, DrawStockConfig::DrawOne, GameMode::Challenge); } app.world_mut().write_message(GameWonEvent { @@ -1580,13 +1580,13 @@ mod tests { /// mode-multiplier rows. #[test] fn cache_win_data_captures_undo_count_and_mode() { - use solitaire_core::DrawMode; + use solitaire_core::DrawStockConfig; let mut app = make_app(); // Set up a Zen-mode game with 2 undos used. { let mut game = app.world_mut().resource_mut::(); - game.0 = GameState::new_with_mode(7, DrawMode::DrawOne, GameMode::Zen); + game.0 = GameState::new_with_mode(7, DrawStockConfig::DrawOne, GameMode::Zen); game.0.force_test_undos(2); } diff --git a/solitaire_wasm/src/lib.rs b/solitaire_wasm/src/lib.rs index c69e11d..05775b7 100644 --- a/solitaire_wasm/src/lib.rs +++ b/solitaire_wasm/src/lib.rs @@ -23,7 +23,7 @@ use solitaire_core::{Foundation, KlondikePile, Tableau}; use serde::{Deserialize, Serialize}; use solitaire_core::card::Suit; use solitaire_core::error::MoveError; -use solitaire_core::{DrawMode, game_state::{GameMode, GameState}}; +use solitaire_core::{DrawStockConfig, game_state::{GameMode, GameState}}; use solitaire_core::klondike_adapter::{ SavedInstruction, SavedKlondikePile, SavedKlondikePileStack, tableau_from_index, }; @@ -48,7 +48,7 @@ pub struct Replay { #[serde(default)] pub schema_version: u32, pub seed: u64, - pub draw_mode: DrawMode, + pub draw_mode: DrawStockConfig, pub mode: GameMode, pub time_seconds: u64, pub final_score: i32, @@ -332,7 +332,7 @@ pub struct DebugInvariantReport { #[derive(Debug, Clone, Serialize, PartialEq, Eq)] pub struct DebugSnapshot { pub seed: u64, - pub draw_mode: DrawMode, + pub draw_mode: DrawStockConfig, pub mode: GameMode, pub state: GameSnapshot, pub legal_moves: Vec, @@ -726,9 +726,9 @@ impl SolitaireGame { #[cfg(feature = "console_error_panic_hook")] console_error_panic_hook::set_once(); let dm = if draw_three { - DrawMode::DrawThree + DrawStockConfig::DrawThree } else { - DrawMode::DrawOne + DrawStockConfig::DrawOne }; SolitaireGame { game: GameState::new_with_mode(seed as u64, dm, GameMode::Classic), @@ -952,7 +952,7 @@ mod tests { key } - fn run_autonomous(seed: u64, draw_mode: DrawMode, max_steps: usize) -> DebugSnapshot { + fn run_autonomous(seed: u64, draw_mode: DrawStockConfig, max_steps: usize) -> DebugSnapshot { let mut game = SolitaireGame { game: GameState::new_with_mode(seed, draw_mode, GameMode::Classic), }; @@ -983,7 +983,7 @@ mod tests { #[test] fn debug_snapshot_exposes_replayable_seed_and_history() { let seed = 42_u64; - let final_snapshot = run_autonomous(seed, DrawMode::DrawOne, 1500); + let final_snapshot = run_autonomous(seed, DrawStockConfig::DrawOne, 1500); assert_eq!(final_snapshot.seed, seed); assert!( !final_snapshot.state_json.is_empty(), @@ -1000,7 +1000,7 @@ mod tests { #[test] fn replay_moves_export_is_json_compatible_and_replayable() { let seed = 7_u64; - let draw_mode = DrawMode::DrawThree; + let draw_mode = DrawStockConfig::DrawThree; let mut game = SolitaireGame { game: GameState::new_with_mode(seed, draw_mode, GameMode::Classic), }; @@ -1098,9 +1098,9 @@ mod tests { fn debug_api_autonomous_seed_batch_smoke() { for seed in 0_u64..128_u64 { let draw_mode = if seed % 2 == 0 { - DrawMode::DrawOne + DrawStockConfig::DrawOne } else { - DrawMode::DrawThree + DrawStockConfig::DrawThree }; let snapshot = run_autonomous(seed, draw_mode, 2000); assert_invariants(&snapshot, seed); @@ -1112,9 +1112,9 @@ mod tests { fn debug_api_autonomous_thousands_seed_soak() { for seed in 10_000_u64..12_000_u64 { let draw_mode = if seed % 2 == 0 { - DrawMode::DrawOne + DrawStockConfig::DrawOne } else { - DrawMode::DrawThree + DrawStockConfig::DrawThree }; let snapshot = run_autonomous(seed, draw_mode, 3000); assert_invariants(&snapshot, seed); @@ -1125,7 +1125,7 @@ mod tests { fn serialize_from_saved_round_trip() { let seed = 55_u64; let mut game = SolitaireGame { - game: GameState::new_with_mode(seed, DrawMode::DrawOne, GameMode::Classic), + game: GameState::new_with_mode(seed, DrawStockConfig::DrawOne, GameMode::Classic), }; // Advance a few moves so there is non-trivial state to round-trip. for _ in 0..20 { @@ -1160,7 +1160,7 @@ mod tests { fn undo_reverts_to_prior_state() { let seed = 99_u64; let mut game = SolitaireGame { - game: GameState::new_with_mode(seed, DrawMode::DrawOne, GameMode::Classic), + game: GameState::new_with_mode(seed, DrawStockConfig::DrawOne, GameMode::Classic), }; let before_key = board_key(&game.debug_snapshot_native().state); @@ -1198,7 +1198,7 @@ mod tests { fn draw_one_advances_waste_by_one() { let seed = 1_u64; let mut game = SolitaireGame { - game: GameState::new_with_mode(seed, DrawMode::DrawOne, GameMode::Classic), + game: GameState::new_with_mode(seed, DrawStockConfig::DrawOne, GameMode::Classic), }; let stock_before = game.game.stock_cards().len(); @@ -1224,7 +1224,7 @@ mod tests { fn draw_three_advances_waste_by_three() { let seed = 1_u64; let mut game = SolitaireGame { - game: GameState::new_with_mode(seed, DrawMode::DrawThree, GameMode::Classic), + game: GameState::new_with_mode(seed, DrawStockConfig::DrawThree, GameMode::Classic), }; let stock_before = game.game.stock_cards().len(); @@ -1254,7 +1254,7 @@ mod tests { fn debug_apply_move_json_stock_click_advances_waste() { let seed = 3_u64; let mut game = SolitaireGame { - game: GameState::new_with_mode(seed, DrawMode::DrawOne, GameMode::Classic), + game: GameState::new_with_mode(seed, DrawStockConfig::DrawOne, GameMode::Classic), }; let waste_before = game.game.waste_cards().len();