The automatons awaken (branch only — not for the public droplet yet)
Phase 4 begins. The automaton is a pure function in the engine — automatonCommand(view) — playing from its own redacted GameView, the same information a human seat receives: hidden hands stay hidden from the clockwork. It ranks simple damage spells, counters what hurts (full shield at 3+, reflection at 4+, blunt at 2+), discards its worst cards by a value order, BFS-pathfinds to enemy treasures and home again, refuses to path through hazards, brawls when there is nothing to steal, and always has a safe fallback; the server's drive loop steps any bot-held seat through the same runCommand path as humans, so bot commands log, persist, replay, and broadcast like anyone's. Hosts seat them pre-start with "⚙ seat an automaton" (Automaton, Automaton II, ... V); bot seats persist as tokenless join lines and restore on boot. Proven three ways: bot-vs-bot engine games conclude across seeds in 8-14 rounds (~100 commands — human-scale), a full four-automaton table finishes, and a live websocket game of human vs. automaton ended with the clockwork carrying two treasures home through a do-nothing opponent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
5802e1df05
commit
ab52628d2f
@@ -35,7 +35,9 @@ import {
|
||||
getRoom,
|
||||
joinRoom,
|
||||
loadPersistedRooms,
|
||||
addAutomaton,
|
||||
addChat,
|
||||
driveAutomatons,
|
||||
makeTransferCode,
|
||||
redactFor,
|
||||
roomCount,
|
||||
@@ -170,6 +172,7 @@ function roomInfo(room: Room) {
|
||||
hostId: room.hostId,
|
||||
started: room.state !== null,
|
||||
colors: Object.fromEntries(room.colorChoices),
|
||||
bots: [...room.bots],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -181,6 +184,17 @@ function broadcast(room: Room, makeMessage: (playerId: PlayerId) => unknown): vo
|
||||
}
|
||||
}
|
||||
|
||||
/** After a human acts, the clockwork answers; every step is broadcast. */
|
||||
function runBots(room: Room): void {
|
||||
const steps = driveAutomatons(room);
|
||||
for (const step of steps) {
|
||||
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 }));
|
||||
}
|
||||
}
|
||||
|
||||
function broadcastRoomState(room: Room): void {
|
||||
broadcast(room, () => roomInfo(room));
|
||||
if (room.state) {
|
||||
@@ -250,6 +264,16 @@ wss.on("connection", (socket) => {
|
||||
send(socket, { type: "events", events: redactFor(room.events, name) });
|
||||
}
|
||||
broadcastRoomState(room);
|
||||
runBots(room);
|
||||
break;
|
||||
}
|
||||
case "addBot": {
|
||||
const room = session.roomId ? getRoom(session.roomId) : undefined;
|
||||
if (!room || !session.playerId) return send(socket, { type: "error", message: "not in a room" });
|
||||
if (session.playerId !== room.hostId) return send(socket, { type: "error", message: "only the host seats automatons" });
|
||||
const result = addAutomaton(room);
|
||||
if ("error" in result) return send(socket, { type: "error", message: result.error });
|
||||
broadcastRoomState(room);
|
||||
break;
|
||||
}
|
||||
case "start": {
|
||||
@@ -260,6 +284,7 @@ wss.on("connection", (socket) => {
|
||||
if ("error" in result) return send(socket, { type: "error", message: result.error });
|
||||
broadcast(room, (playerId) => ({ type: "events", events: redactFor(result.events, playerId) }));
|
||||
broadcastRoomState(room);
|
||||
runBots(room);
|
||||
break;
|
||||
}
|
||||
case "command": {
|
||||
@@ -272,6 +297,7 @@ wss.on("connection", (socket) => {
|
||||
if ("error" in result) return send(socket, { type: "error", message: result.error });
|
||||
broadcast(room, (playerId) => ({ type: "events", events: redactFor(result.events, playerId) }));
|
||||
broadcast(room, (playerId) => ({ type: "state", view: viewForPlayer(room, playerId), seq: room.log.length }));
|
||||
runBots(room);
|
||||
break;
|
||||
}
|
||||
case "rollDie": {
|
||||
|
||||
Reference in New Issue
Block a user