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>
162 lines
5.8 KiB
Rust
162 lines
5.8 KiB
Rust
//! A populated `GroundView`, built for the coverage gate.
|
|
//!
|
|
//! Mid-DARVO, mid-GROUND, scored — **not** a fresh deal. Most of the
|
|
//! fields this gate exists to cover are empty at deal time, and a coverage
|
|
//! test run against a state where the fields are absent is the same lie in
|
|
//! a different costume (CB-WP-0011 T01).
|
|
//!
|
|
//! Built directly rather than projected from a `GroundState`. The
|
|
//! projection's own hiding rules are already asserted by
|
|
//! `games_ground::view` and by `cb-play`'s
|
|
//! `the_inspector_never_widens_the_projection`; re-asserting them here
|
|
//! would be a duplicated fact that drifts. What *this* crate must not do
|
|
//! is invent a hand it was never given, and that is what the renderer test
|
|
//! checks — with `hand: None` supplied deliberately.
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
use cb_kernel::PlayerId;
|
|
use games_ground::view::{GroundView, OutcomeView, PlayerView, ProblemView, SelectionView};
|
|
use games_ground::{
|
|
Action, Coalition, DarvoStage, DarvoTarget, GroundChoice, GroundMode, Pair, Relation,
|
|
RoundStep, ScoringMode, Selection, SolutionCard, Suit, SupportResponse,
|
|
};
|
|
|
|
pub fn seats() -> (PlayerId, PlayerId, PlayerId) {
|
|
(PlayerId(0), PlayerId(1), PlayerId(2))
|
|
}
|
|
|
|
/// The fixture, seen by `viewer` (`None` for a spectator).
|
|
pub fn view(viewer: Option<PlayerId>) -> GroundView {
|
|
let (p1, p2, p3) = seats();
|
|
let card = |suit| SolutionCard { suit };
|
|
|
|
let mut players = BTreeMap::new();
|
|
for (id, stress, protection, darvo, ready, lifted, blame) in [
|
|
(p1, 5u8, 2u8, DarvoStage::Reverse, true, true, vec![p3]),
|
|
(p2, 1, 0, DarvoStage::Off, false, false, vec![]),
|
|
(p3, 0, 0, DarvoStage::Deny, true, false, vec![]),
|
|
] {
|
|
let own = viewer == Some(id);
|
|
players.insert(
|
|
id,
|
|
PlayerView {
|
|
stress,
|
|
freedom_ready: ready,
|
|
freedom_gate_lifted: lifted,
|
|
darvo,
|
|
protection,
|
|
blame_from: blame,
|
|
hand: own.then(|| vec![card(Suit::Clarify), card(Suit::Boundary)]),
|
|
hand_size: 2,
|
|
},
|
|
);
|
|
}
|
|
|
|
let mut problems = BTreeMap::new();
|
|
problems.insert(1, ProblemView::FaceDown);
|
|
// CB-WP-0043: a third Problem so all three H2 scopes are drawn —
|
|
// global (1), personal (6) and bond (7). Two would leave one
|
|
// placement rule unexercised and the coverage probe unsatisfiable.
|
|
problems.insert(6, ProblemView::FaceDown);
|
|
problems.insert(
|
|
7,
|
|
ProblemView::FaceUp {
|
|
suit: Suit::Change,
|
|
value: 6,
|
|
denied: true,
|
|
claimed_by: Some(p2),
|
|
protected_this_round: true,
|
|
},
|
|
);
|
|
|
|
GroundView {
|
|
viewer,
|
|
round: 3,
|
|
lead: p2,
|
|
step: RoundStep::Select,
|
|
mode: ScoringMode::BondedCoalitions,
|
|
scenario: "SCN_03".into(),
|
|
players,
|
|
relations: BTreeMap::from([
|
|
(Pair::new(p1, p2), Relation::Rivalry),
|
|
(Pair::new(p2, p3), Relation::Bond),
|
|
]),
|
|
problems,
|
|
variant: games_ground::Variant::Baseline,
|
|
focus: BTreeMap::from([(p1, p3)]),
|
|
selections: BTreeMap::from([
|
|
(
|
|
p1,
|
|
SelectionView::Shown(Selection {
|
|
action: Action::Attack,
|
|
target: Some(p2),
|
|
problem: Some(7),
|
|
}),
|
|
),
|
|
(p2, SelectionView::Hidden),
|
|
// CB-WP-0034: P3 offered P2 their Support, revealed. The
|
|
// fixture now carries the case the report was about, so the
|
|
// coverage probe for `support_responses` can name a SEAT
|
|
// rather than a response with nobody attached to it.
|
|
(
|
|
p3,
|
|
SelectionView::Shown(Selection {
|
|
action: Action::Support,
|
|
target: Some(p2),
|
|
problem: None,
|
|
}),
|
|
),
|
|
]),
|
|
// CB-WP-0043: the fixture carries H2 markers, so the scoped
|
|
// layout is exercised — a fixture with none would leave the new
|
|
// placement untested and the row layout would always be taken.
|
|
problem_markers: BTreeMap::from([
|
|
(
|
|
1,
|
|
games_ground::view::ProblemMarker {
|
|
owner: None,
|
|
scope: Some(games_ground::edition::StressScope::Global),
|
|
},
|
|
),
|
|
(
|
|
6,
|
|
games_ground::view::ProblemMarker {
|
|
owner: Some(p1),
|
|
scope: Some(games_ground::edition::StressScope::Personal),
|
|
},
|
|
),
|
|
(
|
|
7,
|
|
games_ground::view::ProblemMarker {
|
|
owner: Some(p2),
|
|
scope: Some(games_ground::edition::StressScope::Bond),
|
|
},
|
|
),
|
|
]),
|
|
ground_modes: BTreeMap::from([(p3, GroundMode::Gr)]),
|
|
ground_choices: BTreeMap::from([(p3, GroundChoice::ProtectProblem { problem: 7 })]),
|
|
support_responses: BTreeMap::from([(p2, SupportResponse::AcceptBond)]),
|
|
darvo_targets: BTreeMap::from([(
|
|
p3,
|
|
DarvoTarget {
|
|
problem: Some(1),
|
|
player: Some(p2),
|
|
},
|
|
)]),
|
|
solution_deck_len: 17,
|
|
solution_discard: vec![card(Suit::Repair), card(Suit::Change)],
|
|
outcome: Some(OutcomeView {
|
|
total: 9,
|
|
threshold: 12,
|
|
group_success: false,
|
|
personal: BTreeMap::from([(p1, 3), (p2, -1), (p3, 0)]),
|
|
coalitions: vec![Coalition {
|
|
members: vec![p2, p3],
|
|
score: 4,
|
|
}],
|
|
mastery: Some(1),
|
|
winners: vec![p1],
|
|
}),
|
|
}
|
|
}
|