Skip to main content

Documentation Index

Fetch the complete documentation index at: https://openturn.io/docs/llms.txt

Use this file to discover all available pages before exploring further.

Because createLocalSession is just a reducer with a subscribe, you can drive it from a Bun script, a Node process, or any environment that can do stdin. This is useful for:
  • Rapid prototyping of game rules before you build a UI.
  • Smoke tests and AI players.
  • Keeping a CLI variant alongside your React app (see the examples/games/tic-tac-toe/cli and examples/games/pig-dice/cli packages).

Minimal CLI

// cli/src/index.ts
import { stdout } from "node:process";
import { createLocalSession } from "@openturn/core";
import { ticTacToe } from "@my/tic-tac-toe-game";

const session = createLocalSession(ticTacToe, { match: { players: ticTacToe.playerIDs } });
const reader = Bun.stdin.stream().pipeThrough(new TextDecoderStream()).getReader();

async function prompt(text: string) {
  stdout.write(text);
  const { value } = await reader.read();
  return value?.trim() ?? null;
}

while (true) {
  const snap = session.getState();
  printBoard(snap.G.board);
  if (snap.meta.result !== null) {
    console.log(snap.meta.result);
    break;
  }
  const player = snap.derived.activePlayers[0]!;
  const line = await prompt(`Player ${player} (row col): `);
  if (line === null || line === "q") break;
  const [row, col] = line.split(" ").map(Number);
  const result = session.applyEvent(player, "placeMark", { row, col });
  if (!result.ok) console.log("rejected:", result.error);
}

cli/package.json (excerpt)

{
  "name": "@my/tic-tac-toe-cli",
  "openturn": { "runtime": "bun" },
  "scripts": { "demo": "bun run src/index.ts" },
  "dependencies": {
    "@openturn/core": "workspace:*",
    "@my/tic-tac-toe-game": "workspace:*"
  }
}
Mark the package openturn.runtime: "bun" so the runtime checker knows you are allowed to use Node builtins and Bun globals.

Capture a replay from the CLI

Pair the CLI with @openturn/replay to record matches:
import { createSavedReplayFromSession, serializeSavedReplay } from "@openturn/replay";

const session = createLocalSession(ticTacToe, {
  match: { players: ticTacToe.playerIDs },
  seed: "cli-run",
});
// ... play the match ...

const envelope = createSavedReplayFromSession({
  gameID: "example/tic-tac-toe",
  playerID: "0",
  session,
});
await Bun.write("replay.json", serializeSavedReplay(envelope));
See how-to: capture replays.

AI player sketch

An AI driver is a loop that reads session.getState(), picks an event, and calls applyEvent:
while (session.getState().meta.result === null) {
  const snap = session.getState();
  const player = snap.derived.activePlayers[0]!;
  const move = pickMove(snap, player);     // your logic
  session.applyEvent(player, move.name, move.args);
}
Because the reducer is deterministic, fixing the RNG seed and action order makes AI evaluation reproducible.