Credibility pass: the splice marks sanded from the rev-11 batch

The extracted rebuildRoom loop reflowed to the file's indent and its
doc comments unstacked; liftIdiotIfSatisfied moves below idiotBlocked
so each keeps its own comment; the pickup's duplicated owner lookup
folds to one; the cloned grab-refusal chain hoists to grabRefused; a
one-off #8a7a5c rejoins the #8a7a5e palette; an inert overflow-x line
and a duplicate import go; span 6 and the two cache strategies say why;
the reversed walking-dead test moves beside its biting sibling. Also:
the replay harness now honors lobby kicks, which VVZU (five bots
seated, two kicked) was the first production ledger to exercise.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
This commit is contained in:
Eric Wagoner
2026-08-30 13:58:02 -04:00
co-authored by Claude Fable 5
parent e83d70116f
commit a1fdb37c54
8 changed files with 96 additions and 87 deletions
+9 -10
View File
@@ -102,8 +102,8 @@
}
/** Board zoom: 1 = fitted to the viewport; higher magnifies for crowded
* squares, with the viewport scrolling. The svg's width is always set
* explicitly (fit × zoom) from measurement — auto-sizing left stale
* dimensions behind when the first-person pane closed. */
* explicitly (fit × zoom) from measurement — auto-sizing leaves stale
* dimensions behind when the first-person pane closes. */
let boardViewportEl = $state<HTMLDivElement | null>(null);
let boardZoom = $state(1);
function zoomBoard(dir: number) {
@@ -1079,16 +1079,14 @@
const weakened = view.sustained.some((e) => e.cardId === "weakness" && e.targetId === view.you);
const safed = view.squareContents[here]?.kind === "safe" &&
view.squareContents[here]!.createdBy !== view.you && !view.openSafes.includes(here);
if (treasuresHere.length > 0 && !autoWillGrab && !me.carriedTreasureId &&
!view.turn.actionsEnded && !weakened && !view.gluedCells[here] && !safed &&
here !== cellKey(me.home)) {
const grabRefused = !!me.carriedTreasureId || view.turn.actionsEnded || weakened ||
!!view.gluedCells[here] || safed || here === cellKey(me.home);
if (treasuresHere.length > 0 && !autoWillGrab && !grabRefused) {
reasons.push(`a treasure lies at your feet, un-grabbed`);
}
// Auto-grab about to pocket your OWN chest: carrying it does not
// score and bares it to capture, so that walk-off asks first.
if (autoWillGrab && !me.carriedTreasureId && !view.turn.actionsEnded &&
!weakened && !view.gluedCells[here] && !safed && here !== cellKey(me.home) &&
treasuresHere[0]!.owner === view.you) {
if (autoWillGrab && !grabRefused && treasuresHere[0]!.owner === view.you) {
reasons.push("your OWN treasure lies underfoot — ending the turn grabs it and carries it along");
}
// The draw picker is sticky: a 0 or 1 chosen turns ago quietly starves.
@@ -3053,6 +3051,8 @@
.game.hand-left .table-edge > * { grid-column: 2; min-width: 0; }
.game.hand-left .table-edge > .hand {
grid-column: 1;
/* One implicit row per table-edge sibling; the strip deals at
* most six, and the hand must span them all to hold the column. */
grid-row: 1 / span 6;
display: grid;
grid-template-columns: repeat(2, 8.2rem);
@@ -3063,7 +3063,6 @@
top: 0.5rem;
max-height: calc(100dvh - 5rem);
overflow-y: auto;
overflow-x: visible;
padding: 0.3rem 0.3rem 0.9rem;
}
.game.hand-left .board-zone { grid-column: 2; grid-row: 1; }
@@ -3120,7 +3119,7 @@
.zoom-btn {
width: 1.7rem;
height: 1.7rem;
border: 1px solid #8a7a5c;
border: 1px solid #8a7a5e;
border-radius: 4px;
background: rgba(233, 225, 203, 0.9);
color: #43331f;