The demo bot draws from the seed, the round and the seat
This commit is contained in:
@@ -16,10 +16,17 @@ describe('High Card', () => {
|
||||
expect(game.needsInput(s, 'A')).toBe(false);
|
||||
});
|
||||
|
||||
it('replays to the same bot picks from the same seed', () => {
|
||||
it('draws the same bot picks from the same seed, and different ones as rounds pass', () => {
|
||||
const a = createGame({ A: 'Ann', B: 'Bo' }, 42);
|
||||
const b = createGame({ A: 'Ann', B: 'Bo' }, 42);
|
||||
expect(game.botInput(a, 'B')).toEqual(game.botInput(b, 'B'));
|
||||
const picks = new Set<number>();
|
||||
let s = a;
|
||||
for (let i = 0; i < 12; i++) {
|
||||
picks.add(game.botInput(s, 'B').pick);
|
||||
s = resolveRound(s, { A: { pick: 1 }, B: game.botInput(s, 'B') });
|
||||
}
|
||||
expect(picks.size).toBeGreaterThan(1);
|
||||
});
|
||||
|
||||
it('stamps the rules revision', () => {
|
||||
|
||||
@@ -33,12 +33,11 @@ export interface State {
|
||||
|
||||
export type Input = { pick: number };
|
||||
|
||||
/** Mulberry32: a small seeded generator, so a game replays to the same bot picks. */
|
||||
function next(state: State): number {
|
||||
let t = (state.rng += 0x6d2b79f5) >>> 0;
|
||||
/** Mulberry32 on a seed: a small generator, so a game replays to the same bot picks. */
|
||||
function random(seed: number): number {
|
||||
let t = (seed + 0x6d2b79f5) >>> 0;
|
||||
t = Math.imul(t ^ (t >>> 15), t | 1);
|
||||
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
||||
state.rng = state.rng >>> 0;
|
||||
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
||||
}
|
||||
|
||||
@@ -86,7 +85,8 @@ export const game: GameSpec<State, Input> = {
|
||||
turn: (state) => state.rounds.length + 1,
|
||||
// Every finished round is public; nothing is hidden, so the gallery sees what a seat sees.
|
||||
view: (state, viewer) => (viewer === SPECTATOR ? structuredClone(state) : structuredClone(state)),
|
||||
botInput: (state) => ({ pick: 1 + Math.floor(next(structuredClone(state)) * HIGHEST) }),
|
||||
// A function of the state alone: the seed, the round and the seat, so a replay draws the same pick.
|
||||
botInput: (state, seat) => ({ pick: 1 + Math.floor(random(state.rng + state.rounds.length * 7919 + state.seats.indexOf(seat)) * HIGHEST) }),
|
||||
cleanInput: (raw) => {
|
||||
const pick = Number((raw as { pick?: unknown })?.pick);
|
||||
return { pick: Number.isInteger(pick) && pick >= 1 && pick <= HIGHEST ? pick : 1 };
|
||||
|
||||
Reference in New Issue
Block a user