//! 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-06-04) 0xD1FF_0000_0000_0009, 0xD1FF_0000_0000_0087, 0xD1FF_0000_0000_00EB, 0xD1FF_0000_0000_017F, 0xD1FF_0000_0000_01CE, 0xD1FF_0000_0000_020F, 0xD1FF_0000_0000_0251, 0xD1FF_0000_0000_0275, 0xD1FF_0000_0000_029C, 0xD1FF_0000_0000_02BD, 0xD1FF_0000_0000_02ED, 0xD1FF_0000_0000_038F, 0xD1FF_0000_0000_03C9, 0xD1FF_0000_0000_0415, 0xD1FF_0000_0000_045F, 0xD1FF_0000_0000_04C4, 0xD1FF_0000_0000_04CC, 0xD1FF_0000_0000_04EE, 0xD1FF_0000_0000_0631, 0xD1FF_0000_0000_0651, 0xD1FF_0000_0000_0689, 0xD1FF_0000_0000_0735, 0xD1FF_0000_0000_0748, 0xD1FF_0000_0000_0801, 0xD1FF_0000_0000_0820, 0xD1FF_0000_0000_08F9, 0xD1FF_0000_0000_091C, 0xD1FF_0000_0000_0937, 0xD1FF_0000_0000_09A6, 0xD1FF_0000_0000_09C3, 0xD1FF_0000_0000_09DD, 0xD1FF_0000_0000_0BD9, 0xD1FF_0000_0000_0BEC, 0xD1FF_0000_0000_0BF2, 0xD1FF_0000_0000_0C1B, 0xD1FF_0000_0000_0C26, 0xD1FF_0000_0000_0C36, 0xD1FF_0000_0000_0C4B, 0xD1FF_0000_0000_0C78, 0xD1FF_0000_0000_0CBC, ]; /// 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-06-04) 0xD1FF_0000_0000_0012, 0xD1FF_0000_0000_002C, 0xD1FF_0000_0000_004B, 0xD1FF_0000_0000_0052, 0xD1FF_0000_0000_0058, 0xD1FF_0000_0000_005E, 0xD1FF_0000_0000_0063, 0xD1FF_0000_0000_0099, 0xD1FF_0000_0000_00A9, 0xD1FF_0000_0000_00AF, 0xD1FF_0000_0000_00BB, 0xD1FF_0000_0000_00D1, 0xD1FF_0000_0000_00E3, 0xD1FF_0000_0000_0108, 0xD1FF_0000_0000_010D, 0xD1FF_0000_0000_0110, 0xD1FF_0000_0000_012F, 0xD1FF_0000_0000_0139, 0xD1FF_0000_0000_013C, 0xD1FF_0000_0000_0148, 0xD1FF_0000_0000_015E, 0xD1FF_0000_0000_016A, 0xD1FF_0000_0000_016F, 0xD1FF_0000_0000_0179, 0xD1FF_0000_0000_019E, 0xD1FF_0000_0000_01A8, 0xD1FF_0000_0000_01AB, 0xD1FF_0000_0000_01B5, 0xD1FF_0000_0000_01B8, 0xD1FF_0000_0000_01D3, 0xD1FF_0000_0000_01EE, 0xD1FF_0000_0000_01F3, 0xD1FF_0000_0000_0202, 0xD1FF_0000_0000_0203, 0xD1FF_0000_0000_021E, 0xD1FF_0000_0000_022C, 0xD1FF_0000_0000_022D, 0xD1FF_0000_0000_0233, 0xD1FF_0000_0000_0245, 0xD1FF_0000_0000_024E, ]; /// 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-06-04) 0xD1FF_0000_0000_0006, 0xD1FF_0000_0000_0008, 0xD1FF_0000_0000_000F, 0xD1FF_0000_0000_0011, 0xD1FF_0000_0000_0022, 0xD1FF_0000_0000_0023, 0xD1FF_0000_0000_002A, 0xD1FF_0000_0000_002D, 0xD1FF_0000_0000_0040, 0xD1FF_0000_0000_0042, 0xD1FF_0000_0000_0050, 0xD1FF_0000_0000_005B, 0xD1FF_0000_0000_005D, 0xD1FF_0000_0000_0067, 0xD1FF_0000_0000_0069, 0xD1FF_0000_0000_006E, 0xD1FF_0000_0000_0072, 0xD1FF_0000_0000_0079, 0xD1FF_0000_0000_007C, 0xD1FF_0000_0000_0080, 0xD1FF_0000_0000_0081, 0xD1FF_0000_0000_0083, 0xD1FF_0000_0000_0091, 0xD1FF_0000_0000_009B, 0xD1FF_0000_0000_00A1, 0xD1FF_0000_0000_00B1, 0xD1FF_0000_0000_00C3, 0xD1FF_0000_0000_00D6, 0xD1FF_0000_0000_00DD, 0xD1FF_0000_0000_00E8, 0xD1FF_0000_0000_00F2, 0xD1FF_0000_0000_0101, 0xD1FF_0000_0000_010F, 0xD1FF_0000_0000_0113, 0xD1FF_0000_0000_0118, 0xD1FF_0000_0000_0119, 0xD1FF_0000_0000_012D, 0xD1FF_0000_0000_0133, 0xD1FF_0000_0000_0144, 0xD1FF_0000_0000_0147, ]; /// 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-06-04) 0xD1FF_0000_0000_0000, 0xD1FF_0000_0000_0002, 0xD1FF_0000_0000_000A, 0xD1FF_0000_0000_0013, 0xD1FF_0000_0000_0017, 0xD1FF_0000_0000_001C, 0xD1FF_0000_0000_001F, 0xD1FF_0000_0000_0021, 0xD1FF_0000_0000_0024, 0xD1FF_0000_0000_0029, 0xD1FF_0000_0000_002E, 0xD1FF_0000_0000_0035, 0xD1FF_0000_0000_0045, 0xD1FF_0000_0000_0048, 0xD1FF_0000_0000_0049, 0xD1FF_0000_0000_004F, 0xD1FF_0000_0000_0062, 0xD1FF_0000_0000_006D, 0xD1FF_0000_0000_0074, 0xD1FF_0000_0000_0076, 0xD1FF_0000_0000_0082, 0xD1FF_0000_0000_008F, 0xD1FF_0000_0000_0090, 0xD1FF_0000_0000_0097, 0xD1FF_0000_0000_009A, 0xD1FF_0000_0000_009F, 0xD1FF_0000_0000_00A5, 0xD1FF_0000_0000_00A8, 0xD1FF_0000_0000_00AD, 0xD1FF_0000_0000_00AE, 0xD1FF_0000_0000_00B8, 0xD1FF_0000_0000_00B9, 0xD1FF_0000_0000_00BC, 0xD1FF_0000_0000_00C5, 0xD1FF_0000_0000_00CA, 0xD1FF_0000_0000_00CE, 0xD1FF_0000_0000_00DE, 0xD1FF_0000_0000_00ED, 0xD1FF_0000_0000_00EE, 0xD1FF_0000_0000_00EF, ]; /// 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-06-04) 0xD1FF_0000_0000_003C, 0xD1FF_0000_0000_0047, 0xD1FF_0000_0000_005A, 0xD1FF_0000_0000_009C, 0xD1FF_0000_0000_00D2, 0xD1FF_0000_0000_00F4, 0xD1FF_0000_0000_00F6, 0xD1FF_0000_0000_0104, 0xD1FF_0000_0000_0106, 0xD1FF_0000_0000_0111, 0xD1FF_0000_0000_0112, 0xD1FF_0000_0000_0116, 0xD1FF_0000_0000_0117, 0xD1FF_0000_0000_011A, 0xD1FF_0000_0000_0123, 0xD1FF_0000_0000_012B, 0xD1FF_0000_0000_012E, 0xD1FF_0000_0000_0135, 0xD1FF_0000_0000_013A, 0xD1FF_0000_0000_013B, 0xD1FF_0000_0000_0141, 0xD1FF_0000_0000_014A, 0xD1FF_0000_0000_014B, 0xD1FF_0000_0000_014E, 0xD1FF_0000_0000_0150, 0xD1FF_0000_0000_0155, 0xD1FF_0000_0000_0157, 0xD1FF_0000_0000_0158, 0xD1FF_0000_0000_0159, 0xD1FF_0000_0000_015A, 0xD1FF_0000_0000_015C, 0xD1FF_0000_0000_015D, 0xD1FF_0000_0000_015F, 0xD1FF_0000_0000_0166, 0xD1FF_0000_0000_0173, 0xD1FF_0000_0000_0174, 0xD1FF_0000_0000_0178, 0xD1FF_0000_0000_017D, 0xD1FF_0000_0000_0182, 0xD1FF_0000_0000_0187, ]; // --------------------------------------------------------------------------- // 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" ); } } }