Table options: the host's choices when opening a table, declared by the game, recorded on the room line

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
Eric WagonerandClaude Fable 5.1 committed 2026-09-23 12:45:42 -04:00
1 parent 2ab6dc4b5f
commit 63bd154798
10 files changed
+89 -27

No files matched your search

+2 -2
View File
@@ -1,7 +1,7 @@
// The browser's side of the game server: a few JSON calls, and the seats this
// browser holds, remembered so a shared link never has to carry a token.
import type { SeatId } from '$lib/game/spec';
import type { SeatId, TableOptions } from '$lib/game/spec';
import type { RoomView } from './view';
export interface HeldSeat {
@@ -132,7 +132,7 @@ export function makeApi<State, Input>() {
type Seated = { seat: SeatId; token: string; view: RoomView<State> };
type View = RoomView<State>;
return {
create: (name: string, size: number) => call<Seated>('POST', '/api/rooms', { name, size }),
create: (name: string, size: number, options?: TableOptions) => call<Seated>('POST', '/api/rooms', { name, size, options }),
join: (roomId: string, name: string) => call<Seated>('POST', `/api/rooms/${roomId}/join`, { name }),
addBot: (roomId: string, token: string) => call<View>('POST', `/api/rooms/${roomId}/bot`, { token }),
begin: (roomId: string, token: string) => call<View>('POST', `/api/rooms/${roomId}/begin`, { token }),
+5 -5
View File
@@ -3,7 +3,7 @@
// through GameSpec; a game's own store wraps or extends this one.
import { game } from '$lib/game';
import { SPECTATOR, type SeatId } from '$lib/game/spec';
import { SPECTATOR, type SeatId, type TableOptions } from '$lib/game/spec';
import type { Input, State } from '$lib/game';
import { makeApi, rememberSeat, watchRoom, type HeldSeat } from './client';
import { markTalkSeen } from './talk';
@@ -152,15 +152,15 @@ export class Room {
}
/** Open a table on the server, seated as its host; it begins when the host says. */
static async create(name: string, size: number): Promise<Room> {
const seated = await api.create(name, size);
static async create(name: string, size: number, options?: TableOptions): Promise<Room> {
const seated = await api.create(name, size, options);
rememberSeat(seated.view.roomId, { seat: seated.seat, token: seated.token });
return new Room(seated.view, seated.token);
}
/** A room with a bot seated and the game begun, so a solo game is a ledger like any other. */
static async createWithBot(name: string): Promise<Room> {
const seated = await api.create(name, 2);
static async createWithBot(name: string, options?: TableOptions): Promise<Room> {
const seated = await api.create(name, 2, options);
rememberSeat(seated.view.roomId, { seat: seated.seat, token: seated.token });
await api.addBot(seated.view.roomId, seated.token);
return new Room(await api.begin(seated.view.roomId, seated.token), seated.token);
+3 -1
View File
@@ -1,7 +1,7 @@
// What the server tells a viewer about a room. The game's own state is
// whatever GameSpec.view returned for that viewer; nothing else leaves.
import type { Outcome, SeatId } from '../game/spec';
import type { Outcome, SeatId, TableOptions } from '../game/spec';
/** One line of table talk: from a seat, or from the Peanut Gallery when the host allows it (id SPECTATOR, signed with `name`). */
export interface ChatLine {
@@ -40,6 +40,8 @@ export interface RoomView<State> {
rematch: { to: string; by: SeatId } | null;
/** The table this one is the rematch of. */
rematchOf: string | null;
/** The host's choices for this table, every declared option filled. */
options: TableOptions;
/** The keeper of the site, and where they live, for a table that calls them. */
keeper: string;
keeperZone: string;