Drop7 Research
src/core/typescript/headless.test.tsTypeScript110 lines · 2.8 KB
import assert from "node:assert/strict";
import test from "node:test";
import {
  headlessDisc,
  runHeadlessGame,
  runHeadlessTournament,
} from "./headless.ts";

const FIXED_SEARCH = {
  maxDepth: 3,
  maxWork: 1_500,
} as const;

test("the next-disc sequence is deterministic and keyed by move number", () => {
  const first = Array.from({ length: 20 }, (_, move) =>
    headlessDisc(42, move),
  );
  const second = Array.from({ length: 20 }, (_, move) =>
    headlessDisc(42, move),
  );
  const other = Array.from({ length: 20 }, (_, move) =>
    headlessDisc(43, move),
  );

  assert.deepEqual(first, second);
  assert.notDeepEqual(first, other);
  assert.ok(first.every((disc) => disc >= 1 && disc <= 7));
});

test("a fixed-work headless game reproduces every move and score", () => {
  const options = {
    seed: 17,
    heuristicProfile: "combined" as const,
    search: FIXED_SEARCH,
    maxMoves: 8,
    trace: true,
  };
  const first = runHeadlessGame(options);
  const second = runHeadlessGame(options);

  assert.deepEqual(first.trace, second.trace);
  assert.equal(first.score, second.score);
  assert.equal(first.moves, second.moves);
  assert.equal(first.searchWork, second.searchWork);
  assert.equal(first.censored, true);
});

test("different policies receive the same paired future-disc sequence", () => {
  const legacy = runHeadlessGame({
    seed: 91,
    heuristicProfile: "legacy",
    search: FIXED_SEARCH,
    maxMoves: 6,
    trace: true,
  });
  const combined = runHeadlessGame({
    seed: 91,
    heuristicProfile: "combined",
    search: FIXED_SEARCH,
    maxMoves: 6,
    trace: true,
  });

  assert.deepEqual(
    legacy.trace?.map((move) => move.disc),
    combined.trace?.map((move) => move.disc),
  );
});

test("tournaments report paired deltas and censored games", () => {
  const tournament = runHeadlessTournament({
    profiles: ["legacy", "combined"],
    seeds: [3, 4],
    search: FIXED_SEARCH,
    maxMoves: 3,
  });

  assert.equal(tournament.games.length, 4);
  assert.equal(tournament.summaries.length, 2);
  assert.equal(tournament.summaries[0].pairedDelta95, null);
  assert.equal(tournament.summaries[0].pairedGames, 0);
  assert.equal(tournament.summaries[0].ties, 0);
  assert.equal(tournament.summaries[0].censoredGames, 2);
  assert.equal(
    tournament.summaries[1].wins +
      tournament.summaries[1].ties +
      tournament.summaries[1].losses,
    0,
  );
});

test("tournaments validate and deduplicate uint32 seeds", () => {
  assert.throws(
    () =>
      runHeadlessGame({
        seed: -1,
        search: FIXED_SEARCH,
        maxMoves: 1,
      }),
    /uint32/,
  );

  const tournament = runHeadlessTournament({
    profiles: ["combined"],
    seeds: [12, 12],
    search: FIXED_SEARCH,
    maxMoves: 1,
  });
  assert.equal(tournament.games.length, 1);
});