A duel server: rooms, seat tokens, ledgers, and resolution when all have moved

Plain HTTP for actions and a websocket that only says "fetch again". A
room is its append-only ledger replayed from the top; the engine is
deterministic given the seed and the recorded inputs. Each seat sees a
view filtered to what the rules let it know. Bot seats are filled at
resolution time.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Eric WagonerandClaude Fable 5.1 committed 2026-09-22 16:54:29 -04:00
1 parent 95a1915cae
commit 8366c9d7ff
11 files changed
+1024 -4

No files matched your search

+16
View File
@@ -0,0 +1,16 @@
// What one wizard is allowed to know. Gestures made while they were blind, or
// while the other wizard was invisible, are unknown to them; the log lines that
// describe those gestures are withheld too. Spells cast are always visible, as
// the rules say.
import { visibleHistory, type GameState, type WizardId } from './state';
export function viewFor(state: GameState, viewer: WizardId): GameState {
const view = structuredClone(state);
for (const id of view.seats) {
if (id === viewer) continue;
view.wizards[id].history = visibleHistory(state, viewer, id);
}
view.log = view.log.filter((l) => !l.hiddenFrom?.includes(viewer));
return view;
}