CB-WP-0047: all four boards, and every mode named on the page
Some checks failed
ci / check (push) Failing after 3s
Some checks failed
ci / check (push) Failing after 3s
The modes were already implemented; nothing had ever COMPARED them. The scenarios were not implemented at all: edition::deal has taken a scenario_id since it was written and the only caller passed the literal "SCN_01", so 15 of 20 Problem cards had never been dealt by anything. The seam was the whole mechanism and it sat unused, with nothing red because nothing asked. Scenario is now state (serde default SCN_01, so all 26 recordings replay unchanged), selected by preset `scn-03-4p` with `standard-Np` still meaning SCN_01, and by --scenario/SCENARIO= accepting ids, numbers or titles, validated against the edition rather than a pattern. The threshold now comes off the Scenario card, closing F25's hardcoded 5/7/9. The first version of that control was worthless and mutation said so: all four scenarios print 5/7/9, so reverting to the bands left it green. Split threshold_from() so it can be handed a card that disagrees. The header read `scoring CommonProblem` where the Mode card is titled COMMON PROBLEM, PERSONAL EDGE -- the defect CB-WP-0034 deleted from the move buttons, still standing on the line that says what winning means. The coverage probe was matching that Debug output and went red when it was fixed: third instance (CB-WP-0024, CB-WP-0034). Page now carries the premise, the mode's rules text, and the tiebreak. scenario-panel plays 4x3x3. Findings: SCN_01 and SCN_02 are the same board (identical cells, pinned by a characterisation test); SCN_04 is the hard board at 2p (52% vs 67/73%, the only deck needing two Repair); and group success is EXACTLY equal across all three modes in all 36 cells, because greedy never reads state.mode -- filed F27, the two competitive modes are scoring lenses over cooperative play. F28: SHARED GROUND's mastery subtracts penalties from the claimed COUNT where the mode card's shared score is claimed VALUE. Raised, not fixed; scoring is ground-game's to rule on. Also fixes design.py reporting a backticked path as no reproduction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
1d3f1bfe60
commit
d30938b259
19 changed files with 1351 additions and 109 deletions
|
|
@ -474,6 +474,11 @@ pub fn darvo_stages() -> Result<Vec<DarvoStageText>, String> {
|
|||
pub struct ScenarioText {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
/// What the game is ABOUT, in the edition's words (CB-WP-0047).
|
||||
///
|
||||
/// Vendored and unread until the four scenarios became selectable:
|
||||
/// with one scenario hardcoded there was nothing to tell apart.
|
||||
pub premise: String,
|
||||
pub surface_problem_id: String,
|
||||
pub hidden_problem_ids: Vec<String>,
|
||||
/// GR-E01 by seat band, as the EDITION states it. The engine has its
|
||||
|
|
@ -494,6 +499,7 @@ pub fn scenarios() -> Result<Vec<ScenarioText>, String> {
|
|||
out.push(ScenarioText {
|
||||
id: t.get(row, "scenario_id")?.to_string(),
|
||||
title: t.get(row, "title")?.to_string(),
|
||||
premise: t.get(row, "premise")?.to_string(),
|
||||
surface_problem_id: t.get(row, "surface_problem_id")?.to_string(),
|
||||
hidden_problem_ids: t
|
||||
.get(row, "hidden_problem_ids")?
|
||||
|
|
@ -1007,6 +1013,91 @@ mod card_text_tests {
|
|||
///
|
||||
/// Same shape as F24's solution deck. `inert`, role `default`: green
|
||||
/// because they agree, red the moment either side moves.
|
||||
/// **All four scenarios deal, and they are not the same board**
|
||||
/// (CB-WP-0047).
|
||||
///
|
||||
/// `setup` passed the literal `"SCN_01"`, so 15 of the 20 Problem
|
||||
/// cards had never been dealt by anything. This deals every scenario
|
||||
/// at every seat band and holds the deal to the Scenario card.
|
||||
#[test]
|
||||
fn every_scenario_deals_the_board_its_card_states() {
|
||||
for s in scenarios().expect("Scenarios.csv") {
|
||||
for (players, k) in [(2u8, 2usize), (4, 3), (6, 4)] {
|
||||
let dealt =
|
||||
deal(&s.id, players).unwrap_or_else(|e| panic!("{} at {players}p: {e}", s.id));
|
||||
assert_eq!(
|
||||
dealt.len(),
|
||||
k + 1,
|
||||
"{} at {players}p: Surface + {k} hidden is {} cards",
|
||||
s.id,
|
||||
k + 1
|
||||
);
|
||||
assert_eq!(
|
||||
dealt.iter().filter(|p| p.surface).count(),
|
||||
1,
|
||||
"{}: exactly one Surface Problem is dealt face up",
|
||||
s.id
|
||||
);
|
||||
// The card states the available total; the deal must be
|
||||
// able to reach the threshold or the board is unwinnable.
|
||||
let available: u32 = dealt.iter().map(|p| u32::from(p.value)).sum();
|
||||
let threshold = match players {
|
||||
0..=2 => s.thresholds.0,
|
||||
3..=4 => s.thresholds.1,
|
||||
_ => s.thresholds.2,
|
||||
};
|
||||
assert!(
|
||||
available >= threshold,
|
||||
"{} at {players}p: {available} points available against a \
|
||||
threshold of {threshold} — the group cannot win in principle",
|
||||
s.id
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// **Two of the four scenarios are the same board** (CB-WP-0047).
|
||||
///
|
||||
/// SCN_01 and SCN_02 have identical suit-and-value profiles at every
|
||||
/// priority, so they play identically and differ only in prose. Not
|
||||
/// a defect — a designed reskin is a legitimate choice — but it is a
|
||||
/// fact about what "four scenarios" buys, and measuring them as four
|
||||
/// independent boards would be measuring two of them twice.
|
||||
///
|
||||
/// This is a **characterisation** test: it pins what is true today so
|
||||
/// that a change to either deck is a decision rather than a drift.
|
||||
#[test]
|
||||
fn which_scenarios_are_mechanically_distinct() {
|
||||
let profile = |id: &str| -> Vec<(u8, String, u8)> {
|
||||
let mut v: Vec<_> = problems_of(id)
|
||||
.expect(id)
|
||||
.into_iter()
|
||||
.map(|p| (p.priority, format!("{:?}", p.suit), p.value))
|
||||
.collect();
|
||||
v.sort();
|
||||
v
|
||||
};
|
||||
assert_eq!(
|
||||
profile("SCN_01"),
|
||||
profile("SCN_02"),
|
||||
"SCN_01 and SCN_02 diverged — they were identical boards; \
|
||||
if this is intended, the panel now measures four real boards"
|
||||
);
|
||||
for pair in [
|
||||
("SCN_01", "SCN_03"),
|
||||
("SCN_01", "SCN_04"),
|
||||
("SCN_03", "SCN_04"),
|
||||
] {
|
||||
assert_ne!(
|
||||
profile(pair.0),
|
||||
profile(pair.1),
|
||||
"{} and {} became the same board",
|
||||
pair.0,
|
||||
pair.1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_engine_agrees_with_the_editions_own_numbers() {
|
||||
for s in scenarios().expect("Scenarios.csv") {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue