CB-WP-0018-T02: a game log the player can read

bot::Journal -- a shared list of Applied { actor, command, events } the
driver appends to via play_journaled; play delegates with None so
nothing existing changed. BotGame.events only appears after play
returns, which is no use to a page rendered mid-game.

Phrased with record::to_step, the recorder's vocabulary, so what the
player reads is what the scenario file will say, and all 29 GroundEvent
variants now render in words instead of Debug.

A command that produced no events says 'no effect'; the mutation
dropping that branch goes red. Honest limitation recorded: the reported
SOLVE case is resolved inside the system's resolve command, which does
produce events for other seats, so it shows as a selection with no claim
following rather than an explicit 'no effect'. Making it explicit would
mean the renderer deciding why a rule did nothing -- a second
implementation of the rules, which this task's control forbids.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-03 02:15:30 +02:00
parent 57639623da
commit 7a78c58404
6 changed files with 333 additions and 15 deletions

View file

@ -573,5 +573,59 @@ mod affordances {
}
}
#[cfg(test)]
mod gamelog {
//! CB-WP-0018 T02.
use crate::doc::{document_with_log, LogLine};
use cb_kernel::PlayerId;
fn line(effects: &[&str]) -> LogLine {
LogLine {
who: "P1".into(),
what: "select_action action=SOLVE problem=1".into(),
effects: effects.iter().map(|s| (*s).to_string()).collect(),
}
}
fn page(log: &[LogLine]) -> String {
document_with_log(
&crate::testfix::view(Some(PlayerId(0))),
&[],
"/command?t=x",
Some(PlayerId(0)),
false,
log,
)
}
/// **The case the pass was reported for.** A SOLVE that cannot be
/// fulfilled produces no events, and a log built only from events
/// would render nothing for it — reproducing the silence the
/// maintainer hit when the same move did nothing three rounds
/// running.
#[test]
fn a_command_that_produced_nothing_says_so() {
let html = page(&[line(&[])]);
assert!(
html.contains("no effect"),
"a command with no events rendered as if it had done something"
);
let text = crate::text_of(&html);
assert!(text.contains("select_action action=SOLVE problem=1"));
}
#[test]
fn effects_are_listed_and_an_empty_log_says_it_is_empty() {
let text = crate::text_of(&page(&[line(&["problem 1 claimed by P1"])]));
assert!(text.contains("problem 1 claimed by P1"));
assert!(
!text.contains("no effect"),
"a command WITH effects was marked as having none"
);
assert!(crate::text_of(&page(&[])).contains("nothing has happened yet"));
}
}
#[cfg(test)]
mod testfix;