2062bd06f3
Adds a gen_seeds binary to solitaire_assetgen that brute-searches seeds for hands solvable in ≤250 moves, then writes the list. The 75 new seeds (0xCAFEBABE prefix) are appended to CHALLENGE_SEEDS in solitaire_data::challenge. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
170 lines
4.7 KiB
Rust
170 lines
4.7 KiB
Rust
//! Static seed list for Challenge mode + helpers.
|
|
//!
|
|
//! Challenge mode walks a fixed sequence of hard-but-winnable seeds. The
|
|
//! player advances by winning a deal in `GameMode::Challenge`. The
|
|
//! `challenge_index` cursor is stored per-player in `PlayerProgress`.
|
|
//!
|
|
//! Seeds wrap modulo `CHALLENGE_SEEDS.len()` so a sufficiently dedicated
|
|
//! player never runs out of challenges.
|
|
|
|
/// Curated Challenge-mode seeds. Order is stable across versions; add new
|
|
/// seeds at the end.
|
|
pub const CHALLENGE_SEEDS: &[u64] = &[
|
|
// Round 1 (original 5)
|
|
0xDEAD_BEEF_CAFE_F00D,
|
|
0xC0DE_FACE_8BAD_F00D,
|
|
0xFEE1_DEAD_DEAD_BEEF,
|
|
0xBAAD_F00D_BAAD_F00D,
|
|
0x1337_C0DE_4242_BABE,
|
|
// Round 2
|
|
0xACED_CAFE_B0BA_1234,
|
|
0x5A1D_F00D_BEEF_CAFE,
|
|
0xF01D_BABE_CAFE_BEEF,
|
|
0xD00D_1E42_ABCD_EF01,
|
|
0x7EA5_ED01_2345_6789,
|
|
// Round 3
|
|
0xC1A5_51C0_F00D_CAFE,
|
|
0xBA5E_BA11_DEAD_BEEF,
|
|
0xFACE_B00C_C0DE_1010,
|
|
0x5EED_CAFE_5EED_CAFE,
|
|
0xABBA_ABBA_CAFE_BABE,
|
|
// Round 4
|
|
0x1234_5678_9ABC_DEF0,
|
|
0xFEDC_BA98_7654_3210,
|
|
0x0011_2233_4455_6677,
|
|
0x8899_AABB_CCDD_EEFF,
|
|
0x1111_2222_3333_4444,
|
|
// Round 5
|
|
0x5555_6666_7777_8888,
|
|
0x9999_AAAA_BBBB_CCCC,
|
|
0xDDDD_EEEE_FFFF_0000,
|
|
0x0101_0101_0101_0101,
|
|
0xA1B2_C3D4_E5F6_0718,
|
|
// Generated by solitaire_assetgen::gen_seeds (start=0xCAFEBABE00000000, count=75, date=2026-05-09)
|
|
0xCAFE_BABE_0000_0000,
|
|
0xCAFE_BABE_0000_0002,
|
|
0xCAFE_BABE_0000_0004,
|
|
0xCAFE_BABE_0000_0008,
|
|
0xCAFE_BABE_0000_000B,
|
|
0xCAFE_BABE_0000_000D,
|
|
0xCAFE_BABE_0000_000E,
|
|
0xCAFE_BABE_0000_0010,
|
|
0xCAFE_BABE_0000_0011,
|
|
0xCAFE_BABE_0000_0014,
|
|
0xCAFE_BABE_0000_0016,
|
|
0xCAFE_BABE_0000_0019,
|
|
0xCAFE_BABE_0000_001A,
|
|
0xCAFE_BABE_0000_001F,
|
|
0xCAFE_BABE_0000_0020,
|
|
0xCAFE_BABE_0000_0021,
|
|
0xCAFE_BABE_0000_0024,
|
|
0xCAFE_BABE_0000_0025,
|
|
0xCAFE_BABE_0000_0027,
|
|
0xCAFE_BABE_0000_002B,
|
|
0xCAFE_BABE_0000_002D,
|
|
0xCAFE_BABE_0000_0030,
|
|
0xCAFE_BABE_0000_0034,
|
|
0xCAFE_BABE_0000_0036,
|
|
0xCAFE_BABE_0000_003A,
|
|
0xCAFE_BABE_0000_003B,
|
|
0xCAFE_BABE_0000_003D,
|
|
0xCAFE_BABE_0000_0042,
|
|
0xCAFE_BABE_0000_0043,
|
|
0xCAFE_BABE_0000_0044,
|
|
0xCAFE_BABE_0000_004C,
|
|
0xCAFE_BABE_0000_004D,
|
|
0xCAFE_BABE_0000_004F,
|
|
0xCAFE_BABE_0000_0050,
|
|
0xCAFE_BABE_0000_0051,
|
|
0xCAFE_BABE_0000_0054,
|
|
0xCAFE_BABE_0000_0055,
|
|
0xCAFE_BABE_0000_0056,
|
|
0xCAFE_BABE_0000_0059,
|
|
0xCAFE_BABE_0000_005B,
|
|
0xCAFE_BABE_0000_005C,
|
|
0xCAFE_BABE_0000_005E,
|
|
0xCAFE_BABE_0000_0060,
|
|
0xCAFE_BABE_0000_0062,
|
|
0xCAFE_BABE_0000_0064,
|
|
0xCAFE_BABE_0000_0067,
|
|
0xCAFE_BABE_0000_0069,
|
|
0xCAFE_BABE_0000_006A,
|
|
0xCAFE_BABE_0000_006B,
|
|
0xCAFE_BABE_0000_006C,
|
|
0xCAFE_BABE_0000_006D,
|
|
0xCAFE_BABE_0000_006E,
|
|
0xCAFE_BABE_0000_006F,
|
|
0xCAFE_BABE_0000_0072,
|
|
0xCAFE_BABE_0000_0073,
|
|
0xCAFE_BABE_0000_0074,
|
|
0xCAFE_BABE_0000_0079,
|
|
0xCAFE_BABE_0000_007A,
|
|
0xCAFE_BABE_0000_007D,
|
|
0xCAFE_BABE_0000_007E,
|
|
0xCAFE_BABE_0000_007F,
|
|
0xCAFE_BABE_0000_0082,
|
|
0xCAFE_BABE_0000_0083,
|
|
0xCAFE_BABE_0000_0084,
|
|
0xCAFE_BABE_0000_0085,
|
|
0xCAFE_BABE_0000_0089,
|
|
0xCAFE_BABE_0000_008A,
|
|
0xCAFE_BABE_0000_008D,
|
|
0xCAFE_BABE_0000_008E,
|
|
0xCAFE_BABE_0000_0090,
|
|
0xCAFE_BABE_0000_0094,
|
|
0xCAFE_BABE_0000_0095,
|
|
0xCAFE_BABE_0000_0098,
|
|
0xCAFE_BABE_0000_0099,
|
|
0xCAFE_BABE_0000_009F,
|
|
];
|
|
|
|
/// Resolve a `challenge_index` to its corresponding seed, wrapping when
|
|
/// the index exceeds the seed-list length. Returns `None` if the seed list
|
|
/// is empty (defensive — `CHALLENGE_SEEDS` is non-empty by construction).
|
|
pub fn challenge_seed_for(index: u32) -> Option<u64> {
|
|
if CHALLENGE_SEEDS.is_empty() {
|
|
return None;
|
|
}
|
|
Some(CHALLENGE_SEEDS[(index as usize) % CHALLENGE_SEEDS.len()])
|
|
}
|
|
|
|
/// Total number of currently-defined challenges. Useful for displaying
|
|
/// "Challenge {n + 1} of {total}" in UI.
|
|
pub fn challenge_count() -> u32 {
|
|
CHALLENGE_SEEDS.len() as u32
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn challenge_seed_for_0_is_first_seed() {
|
|
assert_eq!(challenge_seed_for(0), Some(CHALLENGE_SEEDS[0]));
|
|
}
|
|
|
|
#[test]
|
|
fn challenge_seed_wraps_past_end() {
|
|
let len = CHALLENGE_SEEDS.len() as u32;
|
|
assert_eq!(
|
|
challenge_seed_for(len),
|
|
Some(CHALLENGE_SEEDS[0]),
|
|
"wraps to seed 0 when index == len"
|
|
);
|
|
assert_eq!(
|
|
challenge_seed_for(len + 2),
|
|
Some(CHALLENGE_SEEDS[2]),
|
|
"wraps modulo len"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn all_challenge_seeds_are_unique() {
|
|
let mut seeds: Vec<u64> = CHALLENGE_SEEDS.to_vec();
|
|
seeds.sort();
|
|
let len_before = seeds.len();
|
|
seeds.dedup();
|
|
assert_eq!(seeds.len(), len_before);
|
|
}
|
|
}
|