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
|
- **The tally** sits in the about panel: what the ledgers add up to
|
||||||
(tables, games begun and finished, names seated, turns, time at the
|
(tables, games begun and finished, names seated, turns, time at the
|
||||||
table, games with a bot, talk), counted on demand from the files and
|
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
|
- **Table options** are the host's choices when opening a table (a
|
||||||
variant, a side, a length): the game declares them in `GameSpec.options`,
|
variant, a side, a length): the game declares them in `GameSpec.options`,
|
||||||
the hall renders them as selects, the room line records them, and
|
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.
|
`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
|
`parting(press, viewer)` gives the bots' last words once the game is
|
||||||
over. Without `botPress`, private talk to a bot is refused.
|
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
|
- `preferences`, `options`: the preferences panel's settings and the
|
||||||
host's table choices (above).
|
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
|
// 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
|
// 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
|
// 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 { cleanOptions, type GameSpec, type SeatId } from '../../src/lib/game/spec';
|
||||||
import type { Tally } from '../../src/lib/net/view';
|
import type { Tally } from '../../src/lib/net/view';
|
||||||
@@ -56,6 +57,7 @@ export class Tallies<State, Input> {
|
|||||||
t.gamesBegun += 1;
|
t.gamesBegun += 1;
|
||||||
if (hasBot) t.gamesWithBot += 1;
|
if (hasBot) t.gamesWithBot += 1;
|
||||||
if (lines[0].createdAt < firstStart) firstStart = lines[0].createdAt;
|
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 {
|
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);
|
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 {
|
} catch {
|
||||||
|
|||||||
@@ -139,6 +139,8 @@ export interface GameSpec<State, Input> {
|
|||||||
nameOf(state: State, seat: SeatId): string;
|
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. */
|
/** 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>;
|
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