diff --git a/games/ground/src/edition.rs b/games/ground/src/edition.rs index 296ba16..97c3818 100644 --- a/games/ground/src/edition.rs +++ b/games/ground/src/edition.rs @@ -586,6 +586,75 @@ mod card_text_tests { println!("{report}"); } + /// **The import check ground-game's final ruling asked for**, at + /// every seat band (`RULED GROUND-WP-0004`, 2026-08-03, engine ask 2): + /// + /// > rewrite `gr-e01-threshold-unreachable-2p` / invert `gd0001` as + /// > an **import check**: sum(point_value dealt) >= threshold for + /// > every seat band. + /// + /// **Only the 2p half was done.** `gr-e01-threshold-reachable-2p` + /// covers the tightest band, and the ruling said *every* band. The + /// bands are where the deal depth changes (`k` = 2 / 3 / 4), so a + /// check at one of them tests one value of the variable that moves — + /// which is the shape GameDesign §1.4 exists to refuse. + /// + /// **Reported as a row-level table, never a sum** (§1.2, and + /// ground-game's engine ask 3): Surface separated from each hidden + /// priority, because *"12 in the file"* is the wrong premise this + /// clause was written for. + #[test] + fn every_seat_band_can_reach_its_threshold() { + // GR-E01 dataset 0.1, mirroring `GroundState::threshold`. + let threshold = |players: u8| -> u32 { + match players { + 0..=2 => 5, + 3..=4 => 7, + _ => 9, + } + }; + let mut table = String::from("\nseats | k | Surface | hidden | available | threshold\n"); + for players in [2u8, 3, 4, 5, 6] { + let k = hidden_depth(players).expect("seat band"); + let dealt = deal("SCN_01", players).expect("deal"); + let surface: u32 = dealt + .iter() + .filter(|p| p.surface) + .map(|p| u32::from(p.value)) + .sum(); + let hidden: Vec = dealt + .iter() + .filter(|p| !p.surface) + .map(|p| format!("{}:{}", p.priority, p.value)) + .collect(); + let hidden_total: u32 = dealt + .iter() + .filter(|p| !p.surface) + .map(|p| u32::from(p.value)) + .sum(); + let available = surface + hidden_total; + table.push_str(&format!( + "{players:>5} | {k} | {surface:>7} | {} | {available:>9} | {}\n", + hidden.join(" "), + threshold(players), + )); + assert!( + available >= threshold(players), + "{players}p: available {available} < threshold {}; \ + GR-E01 is unreachable at this band{table}", + threshold(players), + ); + // Surface is never one of the hidden slots — the exact + // undercount that produced the withdrawn 4/6/9. + assert_eq!( + hidden.len(), + usize::from(k), + "{players}p: expected {k} hidden Problems beside Surface{table}" + ); + } + println!("{table}"); + } + /// **F24** (CB-WP-0037 T01). The draw pile is a Rust literal. /// /// `solution_deck()` builds 6 of each suit from an array and never