//! Pre-verified seed catalogs for each [`DifficultyLevel`] tier. //! //! Each slice contains seeds that are provably winnable in Draw-One mode and //! that required a specific solver-budget range to solve — the **smallest** //! budget that returns `Winnable` determines the tier. See //! `solitaire_assetgen/src/bin/gen_difficulty_seeds.rs` for the generator. //! //! # Tiers and budget boundaries //! //! | Tier | move_budget | state_budget | //! |-------------|-------------|--------------| //! | Easy | 1 000 | 1 000 | //! | Medium | 5 000 | 5 000 | //! | Hard | 25 000 | 25 000 | //! | Expert | 100 000 | 100 000 | //! | Grandmaster | 200 000 | 200 000 | //! //! [`DifficultyLevel::Random`] has no catalog — the engine picks a system-time //! seed and skips verification. use solitaire_core::game_state::DifficultyLevel; // --------------------------------------------------------------------------- // Catalogs (populated by gen_difficulty_seeds) // --------------------------------------------------------------------------- /// 40 seeds proven winnable within the Easy budget (≤ 1 000 states). pub const EASY_SEEDS: &[u64] = &[ // Generated by solitaire_assetgen::gen_difficulty_seeds (tier=Easy, date=2026-05-09) 0xD1FF_0000_0000_0001, 0xD1FF_0000_0000_0002, 0xD1FF_0000_0000_0007, 0xD1FF_0000_0000_0008, 0xD1FF_0000_0000_0009, 0xD1FF_0000_0000_000E, 0xD1FF_0000_0000_0013, 0xD1FF_0000_0000_0015, 0xD1FF_0000_0000_0018, 0xD1FF_0000_0000_001D, 0xD1FF_0000_0000_0021, 0xD1FF_0000_0000_0022, 0xD1FF_0000_0000_0026, 0xD1FF_0000_0000_002C, 0xD1FF_0000_0000_002E, 0xD1FF_0000_0000_002F, 0xD1FF_0000_0000_0035, 0xD1FF_0000_0000_0036, 0xD1FF_0000_0000_003C, 0xD1FF_0000_0000_0045, 0xD1FF_0000_0000_0046, 0xD1FF_0000_0000_0048, 0xD1FF_0000_0000_0049, 0xD1FF_0000_0000_004D, 0xD1FF_0000_0000_004F, 0xD1FF_0000_0000_0050, 0xD1FF_0000_0000_0051, 0xD1FF_0000_0000_0053, 0xD1FF_0000_0000_0054, 0xD1FF_0000_0000_0057, 0xD1FF_0000_0000_0058, 0xD1FF_0000_0000_005A, 0xD1FF_0000_0000_005B, 0xD1FF_0000_0000_005C, 0xD1FF_0000_0000_005D, 0xD1FF_0000_0000_005F, 0xD1FF_0000_0000_0061, 0xD1FF_0000_0000_0062, 0xD1FF_0000_0000_0063, 0xD1FF_0000_0000_0069, ]; /// 40 seeds proven winnable within the Medium budget (≤ 5 000 states). pub const MEDIUM_SEEDS: &[u64] = &[ // Generated by solitaire_assetgen::gen_difficulty_seeds (tier=Medium, date=2026-05-09) 0xD1FF_0000_0000_0000, 0xD1FF_0000_0000_0012, 0xD1FF_0000_0000_0016, 0xD1FF_0000_0000_001B, 0xD1FF_0000_0000_001C, 0xD1FF_0000_0000_0020, 0xD1FF_0000_0000_002A, 0xD1FF_0000_0000_0034, 0xD1FF_0000_0000_003A, 0xD1FF_0000_0000_0041, 0xD1FF_0000_0000_0043, 0xD1FF_0000_0000_0060, 0xD1FF_0000_0000_006A, 0xD1FF_0000_0000_006C, 0xD1FF_0000_0000_006E, 0xD1FF_0000_0000_006F, 0xD1FF_0000_0000_0071, 0xD1FF_0000_0000_0072, 0xD1FF_0000_0000_0075, 0xD1FF_0000_0000_0076, 0xD1FF_0000_0000_007B, 0xD1FF_0000_0000_007E, 0xD1FF_0000_0000_0081, 0xD1FF_0000_0000_0083, 0xD1FF_0000_0000_0084, 0xD1FF_0000_0000_0087, 0xD1FF_0000_0000_0090, 0xD1FF_0000_0000_0092, 0xD1FF_0000_0000_0093, 0xD1FF_0000_0000_0098, 0xD1FF_0000_0000_0099, 0xD1FF_0000_0000_009A, 0xD1FF_0000_0000_009E, 0xD1FF_0000_0000_00A5, 0xD1FF_0000_0000_00A8, 0xD1FF_0000_0000_00AA, 0xD1FF_0000_0000_00AB, 0xD1FF_0000_0000_00AE, 0xD1FF_0000_0000_00AF, 0xD1FF_0000_0000_00B0, ]; /// 40 seeds proven winnable within the Hard budget (≤ 25 000 states). pub const HARD_SEEDS: &[u64] = &[ // Generated by solitaire_assetgen::gen_difficulty_seeds (tier=Hard, date=2026-05-09) 0xD1FF_0000_0000_001F, 0xD1FF_0000_0000_0024, 0xD1FF_0000_0000_0025, 0xD1FF_0000_0000_0031, 0xD1FF_0000_0000_0032, 0xD1FF_0000_0000_003E, 0xD1FF_0000_0000_004A, 0xD1FF_0000_0000_006D, 0xD1FF_0000_0000_0079, 0xD1FF_0000_0000_007C, 0xD1FF_0000_0000_0080, 0xD1FF_0000_0000_008A, 0xD1FF_0000_0000_0097, 0xD1FF_0000_0000_00B1, 0xD1FF_0000_0000_00B2, 0xD1FF_0000_0000_00B3, 0xD1FF_0000_0000_00B5, 0xD1FF_0000_0000_00B7, 0xD1FF_0000_0000_00B8, 0xD1FF_0000_0000_00B9, 0xD1FF_0000_0000_00BA, 0xD1FF_0000_0000_00BB, 0xD1FF_0000_0000_00BC, 0xD1FF_0000_0000_00BD, 0xD1FF_0000_0000_00C2, 0xD1FF_0000_0000_00C3, 0xD1FF_0000_0000_00C5, 0xD1FF_0000_0000_00CC, 0xD1FF_0000_0000_00CE, 0xD1FF_0000_0000_00D1, 0xD1FF_0000_0000_00D2, 0xD1FF_0000_0000_00D6, 0xD1FF_0000_0000_00D7, 0xD1FF_0000_0000_00DC, 0xD1FF_0000_0000_00DF, 0xD1FF_0000_0000_00E0, 0xD1FF_0000_0000_00E1, 0xD1FF_0000_0000_00E4, 0xD1FF_0000_0000_00E6, 0xD1FF_0000_0000_00E7, ]; /// 40 seeds proven winnable within the Expert budget (≤ 100 000 states). pub const EXPERT_SEEDS: &[u64] = &[ // Generated by solitaire_assetgen::gen_difficulty_seeds (tier=Expert, date=2026-05-09) 0xD1FF_0000_0000_0006, 0xD1FF_0000_0000_000B, 0xD1FF_0000_0000_0019, 0xD1FF_0000_0000_0082, 0xD1FF_0000_0000_00CB, 0xD1FF_0000_0000_00D5, 0xD1FF_0000_0000_00D8, 0xD1FF_0000_0000_00E8, 0xD1FF_0000_0000_00EA, 0xD1FF_0000_0000_00EB, 0xD1FF_0000_0000_00EC, 0xD1FF_0000_0000_00ED, 0xD1FF_0000_0000_00F2, 0xD1FF_0000_0000_00F3, 0xD1FF_0000_0000_00F4, 0xD1FF_0000_0000_00FE, 0xD1FF_0000_0000_00FF, 0xD1FF_0000_0000_0102, 0xD1FF_0000_0000_0103, 0xD1FF_0000_0000_0104, 0xD1FF_0000_0000_0105, 0xD1FF_0000_0000_0106, 0xD1FF_0000_0000_0109, 0xD1FF_0000_0000_010B, 0xD1FF_0000_0000_010C, 0xD1FF_0000_0000_0110, 0xD1FF_0000_0000_0113, 0xD1FF_0000_0000_0114, 0xD1FF_0000_0000_011B, 0xD1FF_0000_0000_011C, 0xD1FF_0000_0000_011E, 0xD1FF_0000_0000_0120, 0xD1FF_0000_0000_0121, 0xD1FF_0000_0000_0122, 0xD1FF_0000_0000_0123, 0xD1FF_0000_0000_0124, 0xD1FF_0000_0000_0126, 0xD1FF_0000_0000_012B, 0xD1FF_0000_0000_012C, 0xD1FF_0000_0000_012E, ]; /// 40 seeds proven winnable only within the Grandmaster budget (≤ 200 000 states). pub const GRANDMASTER_SEEDS: &[u64] = &[ // Generated by solitaire_assetgen::gen_difficulty_seeds (tier=Grandmaster, date=2026-05-09) 0xD1FF_0000_0000_0027, 0xD1FF_0000_0000_00A0, 0xD1FF_0000_0000_00C4, 0xD1FF_0000_0000_00D4, 0xD1FF_0000_0000_00DE, 0xD1FF_0000_0000_00F9, 0xD1FF_0000_0000_0107, 0xD1FF_0000_0000_0108, 0xD1FF_0000_0000_0130, 0xD1FF_0000_0000_0132, 0xD1FF_0000_0000_0133, 0xD1FF_0000_0000_0134, 0xD1FF_0000_0000_0135, 0xD1FF_0000_0000_0137, 0xD1FF_0000_0000_0139, 0xD1FF_0000_0000_013A, 0xD1FF_0000_0000_013D, 0xD1FF_0000_0000_013F, 0xD1FF_0000_0000_0140, 0xD1FF_0000_0000_0141, 0xD1FF_0000_0000_0142, 0xD1FF_0000_0000_0143, 0xD1FF_0000_0000_0145, 0xD1FF_0000_0000_0146, 0xD1FF_0000_0000_014A, 0xD1FF_0000_0000_014B, 0xD1FF_0000_0000_014C, 0xD1FF_0000_0000_014D, 0xD1FF_0000_0000_014F, 0xD1FF_0000_0000_0150, 0xD1FF_0000_0000_0151, 0xD1FF_0000_0000_0152, 0xD1FF_0000_0000_0153, 0xD1FF_0000_0000_0157, 0xD1FF_0000_0000_0158, 0xD1FF_0000_0000_015B, 0xD1FF_0000_0000_015C, 0xD1FF_0000_0000_015E, 0xD1FF_0000_0000_0162, 0xD1FF_0000_0000_0164, ]; // --------------------------------------------------------------------------- // Public API // --------------------------------------------------------------------------- /// Type alias for the catalog lookup return: a static slice or `None` for `Random`. pub type DifficultySeeds = Option<&'static [u64]>; /// Return the seed catalog for `level`, or `None` for `Random` (caller must /// use a system-time seed instead). pub fn seeds_for(level: DifficultyLevel) -> DifficultySeeds { match level { DifficultyLevel::Easy => Some(EASY_SEEDS), DifficultyLevel::Medium => Some(MEDIUM_SEEDS), DifficultyLevel::Hard => Some(HARD_SEEDS), DifficultyLevel::Expert => Some(EXPERT_SEEDS), DifficultyLevel::Grandmaster => Some(GRANDMASTER_SEEDS), DifficultyLevel::Random => None, } } // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- #[cfg(test)] mod tests { use super::*; #[test] fn all_difficulty_seeds_are_unique() { let all: Vec = [ EASY_SEEDS, MEDIUM_SEEDS, HARD_SEEDS, EXPERT_SEEDS, GRANDMASTER_SEEDS, ] .iter() .flat_map(|s| s.iter().copied()) .collect(); let mut sorted = all.clone(); sorted.sort_unstable(); let before = sorted.len(); sorted.dedup(); assert_eq!(sorted.len(), before, "duplicate seeds found across difficulty tiers"); } #[test] fn seeds_for_random_returns_none() { assert!(seeds_for(DifficultyLevel::Random).is_none()); } #[test] fn seeds_for_non_random_returns_some() { for level in [ DifficultyLevel::Easy, DifficultyLevel::Medium, DifficultyLevel::Hard, DifficultyLevel::Expert, DifficultyLevel::Grandmaster, ] { assert!( seeds_for(level).is_some(), "{level:?} should return Some catalog" ); } } }