A game may count its own lines from every game begun, not only those finished
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
403232087d
commit
d97d709212
+5
-2
@@ -99,7 +99,8 @@ README.
|
||||
- **The tally** sits in the about panel: what the ledgers add up to
|
||||
(tables, games begun and finished, names seated, turns, time at the
|
||||
table, games with a bot, talk), counted on demand from the files and
|
||||
cached a minute. A game adds its own lines through `GameSpec.tally`.
|
||||
cached a minute. A game adds its own lines through `GameSpec.tally` and
|
||||
`GameSpec.tallyBegun`.
|
||||
- **Table options** are the host's choices when opening a table (a
|
||||
variant, a side, a length): the game declares them in `GameSpec.options`,
|
||||
the hall renders them as selects, the room line records them, and
|
||||
@@ -157,7 +158,9 @@ if it were never there.
|
||||
`botInput` receives the same press, so a bot can keep what was agreed.
|
||||
`parting(press, viewer)` gives the bots' last words once the game is
|
||||
over. Without `botPress`, private talk to a bot is refused.
|
||||
- `tally(state)`: the game's own lines for the tally, from a finished game.
|
||||
- `tally(state)`: the game's own lines for the tally, from a finished game;
|
||||
`tallyBegun(options)`: lines from every game begun, finished or not, by
|
||||
its table's options (games begun on each variant).
|
||||
- `preferences`, `options`: the preferences panel's settings and the
|
||||
host's table choices (above).
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { mkdtempSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
import { game, type Input, type State } from '../../src/lib/game';
|
||||
import type { GameSpec } from '../../src/lib/game/spec';
|
||||
import { Rooms } from './rooms';
|
||||
import { Store } from './store';
|
||||
import { Tallies } from './tally';
|
||||
|
||||
let dir: string;
|
||||
beforeEach(() => {
|
||||
dir = mkdtempSync(join(tmpdir(), 'tally-'));
|
||||
});
|
||||
afterEach(() => {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
describe('the tally', () => {
|
||||
it("adds a game's own lines from every game begun, finished or not", () => {
|
||||
const counted: GameSpec<State, Input> = { ...game, tallyBegun: () => ({ 'games of High Card begun': 1 }) };
|
||||
const rooms = new Rooms(counted, new Store(dir));
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const host = rooms.create('Ann', 2);
|
||||
rooms.addBot(host.room, host.token);
|
||||
rooms.begin(host.room, host.token);
|
||||
}
|
||||
rooms.create('Bo', 2);
|
||||
const t = new Tallies(counted, new Store(dir)).get();
|
||||
expect(t).toMatchObject({ tablesOpened: 3, gamesBegun: 2, gamesFinished: 0 });
|
||||
expect(t.byGame).toEqual({ 'games of High Card begun': 2 });
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,8 @@
|
||||
// The tally: what the ledgers add up to, for the hall's about panel. Read
|
||||
// from the files on demand and cached for a minute, so a curious visitor
|
||||
// costs nothing and a busy hall costs one scan a minute. The counts are
|
||||
// the kit's; a game may add its own lines through GameSpec.tally.
|
||||
// the kit's; a game may add its own lines through GameSpec.tally (from a
|
||||
// finished game) and GameSpec.tallyBegun (from any game begun).
|
||||
|
||||
import { cleanOptions, type GameSpec, type SeatId } from '../../src/lib/game/spec';
|
||||
import type { Tally } from '../../src/lib/net/view';
|
||||
@@ -56,6 +57,7 @@ export class Tallies<State, Input> {
|
||||
t.gamesBegun += 1;
|
||||
if (hasBot) t.gamesWithBot += 1;
|
||||
if (lines[0].createdAt < firstStart) firstStart = lines[0].createdAt;
|
||||
for (const [k, v] of Object.entries(this.game.tallyBegun?.(cleanOptions(this.game, lines[0].options)) ?? {})) t.byGame[k] = (t.byGame[k] ?? 0) + v;
|
||||
try {
|
||||
state = this.game.create(Object.fromEntries(seats.map((s) => [s.id, s.name])), line.seed, line.rules, cleanOptions(this.game, lines[0].options), line.rankings);
|
||||
} catch {
|
||||
|
||||
@@ -139,6 +139,8 @@ export interface GameSpec<State, Input> {
|
||||
nameOf(state: State, seat: SeatId): string;
|
||||
/** The game's own lines for the hall's tally, counted from a finished game: a label and how many it adds. */
|
||||
tally?(state: State): Record<string, number>;
|
||||
/** The game's own lines counted from every game begun, finished or not, by its table's options (games begun on each variant). */
|
||||
tallyBegun?(options: TableOptions): Record<string, number>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user