The clockwork plays at a watchable pace
One command per beat — 1.5 seconds, the replay's 1x cadence — each broadcast with its board state as it lands, starting a beat after the human's own action settles. The pump is per-room, re-entrancy guarded, and stops the moment the maze wants a human (or the game ends). Watching an automaton stalk your treasure across the maze is now a spectacle instead of a teleport. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
ab52628d2f
commit
3842307bf0
@@ -37,7 +37,7 @@ import {
|
||||
loadPersistedRooms,
|
||||
addAutomaton,
|
||||
addChat,
|
||||
driveAutomatons,
|
||||
driveOneAutomaton,
|
||||
makeTransferCode,
|
||||
redactFor,
|
||||
roomCount,
|
||||
@@ -184,15 +184,26 @@ function broadcast(room: Room, makeMessage: (playerId: PlayerId) => unknown): vo
|
||||
}
|
||||
}
|
||||
|
||||
/** After a human acts, the clockwork answers; every step is broadcast. */
|
||||
/**
|
||||
* The clockwork plays at a watchable pace: one command every beat, each
|
||||
* broadcast as it lands, until the maze wants a human again.
|
||||
*/
|
||||
const BOT_STEP_MS = 1500;
|
||||
const pumping = new Set<string>();
|
||||
function runBots(room: Room): void {
|
||||
const steps = driveAutomatons(room);
|
||||
for (const step of steps) {
|
||||
if (pumping.has(room.id)) return;
|
||||
pumping.add(room.id);
|
||||
const tick = () => {
|
||||
const step = driveOneAutomaton(room);
|
||||
if (!step) {
|
||||
pumping.delete(room.id);
|
||||
return;
|
||||
}
|
||||
broadcast(room, (playerId) => ({ type: "events", events: redactFor(step.events, playerId) }));
|
||||
}
|
||||
if (steps.length > 0) {
|
||||
broadcast(room, (playerId) => ({ type: "state", view: viewForPlayer(room, playerId), seq: room.log.length }));
|
||||
}
|
||||
setTimeout(tick, BOT_STEP_MS);
|
||||
};
|
||||
setTimeout(tick, 800); // a beat after the human's own action settles
|
||||
}
|
||||
|
||||
function broadcastRoomState(room: Room): void {
|
||||
|
||||
@@ -262,29 +262,25 @@ function actingSeat(room: Room): PlayerId | null {
|
||||
}
|
||||
|
||||
/**
|
||||
* Let the automatons play until the maze wants a human again. Returns the
|
||||
* event batches produced, one per command, for per-step broadcasting.
|
||||
* One automaton command, if the maze is waiting on clockwork. Null when a
|
||||
* human holds the floor (or the game is over, or the clockwork is wedged).
|
||||
*/
|
||||
export function driveAutomatons(room: Room): { seat: PlayerId; events: GameEvent[] }[] {
|
||||
const out: { seat: PlayerId; events: GameEvent[] }[] = [];
|
||||
for (let i = 0; i < 300; i++) {
|
||||
const seat = actingSeat(room);
|
||||
if (!seat || !room.bots.has(seat)) break;
|
||||
const view = viewFor(room.state!, seat);
|
||||
const cmd = automatonCommand(view) ?? automatonFallback(view);
|
||||
let r = runCommand(room, seat, cmd);
|
||||
export function driveOneAutomaton(room: Room): { seat: PlayerId; events: GameEvent[] } | null {
|
||||
const seat = actingSeat(room);
|
||||
if (!seat || !room.bots.has(seat)) return null;
|
||||
const view = viewFor(room.state!, seat);
|
||||
const cmd = automatonCommand(view) ?? automatonFallback(view);
|
||||
let r = runCommand(room, seat, cmd);
|
||||
if ("error" in r) {
|
||||
r = runCommand(room, seat, automatonFallback(view));
|
||||
if ("error" in r) r = runCommand(room, seat, { type: "endTurn", draw: 0 });
|
||||
if ("error" in r) r = runCommand(room, seat, { type: "pass" });
|
||||
if ("error" in r) {
|
||||
r = runCommand(room, seat, automatonFallback(view));
|
||||
if ("error" in r) r = runCommand(room, seat, { type: "endTurn", draw: 0 });
|
||||
if ("error" in r) r = runCommand(room, seat, { type: "pass" });
|
||||
if ("error" in r) {
|
||||
console.error(`automaton ${seat} wedged in ${room.id}: ${r.error}`);
|
||||
break;
|
||||
}
|
||||
console.error(`automaton ${seat} wedged in ${room.id}: ${r.error}`);
|
||||
return null;
|
||||
}
|
||||
out.push({ seat, events: r.events });
|
||||
}
|
||||
return out;
|
||||
return { seat, events: r.events };
|
||||
}
|
||||
|
||||
export interface GameSummary {
|
||||
|
||||
Reference in New Issue
Block a user