From d98580fb4c6b424f33f5ef90463ea40e9c55a3d0 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 7 Aug 2026 23:50:58 +0200 Subject: [PATCH] Discharge ground-game's engine ask 2 at every seat band MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RULED GROUND-WP-0004 (2026-08-03) asked for gr-e01 to be reinstated 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 a check at one band tests one value of the variable that moves, which is the shape GameDesign §1.4 refuses. every_seat_band_can_reach_its_threshold covers 2/3/4/5/6 and reports a row-level table with Surface separated from each hidden priority, which is engine ask 3 and §1.2's ruled shape — "12 in the file" is the wrong premise that clause was written for. Result matches ground-game's authoritative table: 6/9/12 available against thresholds 5/7/9. Mutation-proven, and the mutation is the original defect: count Surface as one of the hidden slots and it fails with "2p: available 4 < threshold 5" — the withdrawn 4/6/9 undercount, reproduced. Co-Authored-By: Claude Opus 5 --- games/ground/src/edition.rs | 69 +++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) 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