Six of seven perceptual defects fixed; item 1 already passed. T01, at the maintainer's instruction: a legal target restyles its EXISTING border rather than drawing a new box. outline + outline-offset drew a second rectangle, which an SVG viewport clips (the missing top and left edges) and which made a seat's highlight card-sized. A border already in the layout cannot move the layout. T02: the ghost was a textContent copy of the card, which is why the line break collapsed and it read as a second card, and why showing the explanation destroyed the label. It is now a pill, the explanation is appended beside the label, and the left-behind element is dimmed and dashed. The stub grew innerHTML so a test can assert BOTH are present -- it could previously only see that something was displayed. T03: NOT reproduced and recorded as not reproduced. The likeliest cause is which element the browser reports -- for touch and pen the pointer is captured to the pointerdown target, making every drop look like a drop-on-itself, which is the other half of the report. elementFromPoint is correct under both explanations. Separately the refusal was written in element ids on the one surface a player reads when something goes wrong; it now speaks the game's words and a test forbids id leakage. T04: seat selections rendered as Debug. The coverage gate then failed my first fix for dropping a field when target and problem were both set -- the aggregate does not produce that shape and the gate was right not to care. T05: the headline reads from group_success. 'Play again' is real, and its first version was useless: run_game bound a fresh listener per game, so a second game moved to a new port and left the tab pointing at a dead one. One listener per session now, and the test asserts the second game is a DIFFERENT deal. Chaos d8=8 fired the first override at the new rate and drew S, changing nothing -- one half of window 2's retirement condition. CB-WP-0019 settled at $38.54/117 against $34.80/107. Eight for eight, and the first under 20%. make all exits 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
5e06a7d01e
commit
bf24affa84
8 changed files with 584 additions and 55 deletions
|
|
@ -21,6 +21,7 @@ use games_ground::{GroundCommand, GroundState};
|
|||
use crate::inspect::{render, seat_name};
|
||||
use std::io::{BufRead, Write};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Config {
|
||||
pub seed: u64,
|
||||
pub players: u8,
|
||||
|
|
@ -220,14 +221,39 @@ pub fn play<R: BufRead, W: Write>(config: &Config, input: R, out: W) -> Result<S
|
|||
// borrow them, so ownership stays here and the game runs one frame in.
|
||||
let input = std::cell::RefCell::new(input);
|
||||
let out = std::cell::RefCell::new(out);
|
||||
run_game(config, &input, &out)
|
||||
|
||||
// CB-WP-0020 T05: a browser session can deal a second game. The seed
|
||||
// advances, because replaying the identical deal is not "again".
|
||||
// CB-WP-0020 T05: ONE listener for the session, not one per game.
|
||||
// The first version bound a fresh port inside `run_game`, so "play
|
||||
// again" moved the game to a new URL and the player's tab was left
|
||||
// pointing at a dead port. Caught by `play_again_deals_a_second_game`.
|
||||
let server = match config.serve {
|
||||
Some(port) => {
|
||||
let s = std::rc::Rc::new(crate::hotseat::Server::bind(port)?);
|
||||
let _ = writeln!(out.borrow_mut(), " open {}", s.url());
|
||||
let _ = out.borrow_mut().flush();
|
||||
Some(s)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let mut cfg: Config = config.clone();
|
||||
loop {
|
||||
let (summary, choice) = run_game(&cfg, &input, &out, server.clone())?;
|
||||
if choice != crate::hotseat::EndChoice::Again {
|
||||
return Ok(summary);
|
||||
}
|
||||
cfg.seed = cfg.seed.wrapping_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn run_game<'a, R: BufRead + 'a, W: Write + 'a>(
|
||||
config: &Config,
|
||||
input: &'a std::cell::RefCell<R>,
|
||||
out: &'a std::cell::RefCell<W>,
|
||||
) -> Result<Summary, String> {
|
||||
server: Option<std::rc::Rc<crate::hotseat::Server>>,
|
||||
) -> Result<(Summary, crate::hotseat::EndChoice), String> {
|
||||
let setup = Setup {
|
||||
players: config.players,
|
||||
preset: format!("standard-{}p", config.players),
|
||||
|
|
@ -243,15 +269,6 @@ fn run_game<'a, R: BufRead + 'a, W: Write + 'a>(
|
|||
// ADR-0007: a browser seat and a CLI seat are both just a Policy, so
|
||||
// the driver cannot tell them apart — which is the property that lets
|
||||
// a browser game replay as a scenario like any other.
|
||||
let server = match config.serve {
|
||||
Some(port) => {
|
||||
let s = std::rc::Rc::new(crate::hotseat::Server::bind(port)?);
|
||||
let _ = writeln!(out.borrow_mut(), " open {}", s.url());
|
||||
let _ = out.borrow_mut().flush();
|
||||
Some(s)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let mut policies: Vec<Box<dyn Policy + 'a>> = Vec::new();
|
||||
for seat in 0..config.players {
|
||||
if config.human_seats.contains(&seat) {
|
||||
|
|
@ -322,10 +339,11 @@ fn run_game<'a, R: BufRead + 'a, W: Write + 'a>(
|
|||
// CB-WP-0018 T01: the browser sees the end of its own game. Measured
|
||||
// before this: a game ended at 5 rounds / 30 commands, the result went
|
||||
// to stdout, and the page's post-`ok` reload got Connection refused.
|
||||
let mut end_choice = crate::hotseat::EndChoice::Closed;
|
||||
if let Some(srv) = &server {
|
||||
let ended = game.state.project(Viewer::Spectator);
|
||||
let msg = format!("{} commands, hash {}", game.commands, &end_hash[..12]);
|
||||
let _ = srv.serve_end(Some(&ended), &msg, END_LINGER);
|
||||
end_choice = srv.serve_end(Some(&ended), &msg, END_LINGER)?;
|
||||
}
|
||||
|
||||
let scenario = games_ground::record::to_scenario(
|
||||
|
|
@ -361,11 +379,14 @@ fn run_game<'a, R: BufRead + 'a, W: Write + 'a>(
|
|||
}
|
||||
};
|
||||
|
||||
Ok(Summary {
|
||||
rounds: game.rounds,
|
||||
end_state_hash: end_hash,
|
||||
scenario,
|
||||
bundle,
|
||||
recorded,
|
||||
})
|
||||
Ok((
|
||||
Summary {
|
||||
rounds: game.rounds,
|
||||
end_state_hash: end_hash,
|
||||
scenario,
|
||||
bundle,
|
||||
recorded,
|
||||
},
|
||||
end_choice,
|
||||
))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue