This tutorial rebuilds theDocumentation Index
Fetch the complete documentation index at: https://openturn.io/docs/llms.txt
Use this file to discover all available pages before exploring further.
examples/games/tic-tac-toe example end to end. You will finish with three packages in a Bun workspace:
- a
gamepackage that exports the authored tic-tac-toe definition, - an
appbrowser package that renders it with React, - and an optional Bun
clipackage for terminal play.
game package is worker-safe, meaning it does not import any Node, Bun, browser, or DOM APIs. That same package will also drop into a hosted multiplayer worker later (see tutorial: tic-tac-toe multiplayer) without any change.
In short: game/src/index.ts exports game and match. The React app binds them with createOpenturnBindings. The CLI uses createLocalSession directly.
Reference code: examples/games/tic-tac-toe. If you would rather just edit a working copy, that’s the source to copy.
This tutorial assumes a Bun workspace at the repo root with a
package.json containing "workspaces": ["packages/*", "tic-tac-toe/*"] (or similar). If you are starting from a fresh checkout of the openturn repo, the workspace is already set up; otherwise add a top-level package.json with "workspaces" before running bun install.1. Create the workspace packages
game/package.json
game/src/index.ts
"play"), a round-robin turn policy, and two views.
The three helpers (placeMark, getWinner, isBoardFull) are pure TypeScript — nothing openturn-specific. Drop them into the same file:
2. Render it with React
The browser package lives in a folder calledapp/. Inside it, the openturn entry files (game.ts, page.tsx, openturn.ts) live in an inner app/ folder — that is the convention the CLI and deploy pipeline look for. So a fully qualified path looks like app/app/page.tsx (browser package app, openturn-app folder app, file page.tsx). Your own React components can live anywhere; this tutorial puts them in app/src/.
app/package.json (excerpt)
app/app/game.ts
app/app/openturn.ts
app/app/page.tsx
app/src/components/TicTacToeExperience.tsx
createOpenturnBindings(ticTacToe, { runtime: "local", match }) builds typed bindings, declares the deployment shape (in-process), and seeds the initial match. Capacity (playerIDs, minPlayers) lives on the game itself; the per-session match carries just the seated subset. useMatch() returns a mode-discriminated view; in local mode, view.state exposes snapshot, dispatch, reset, and a per-player getPlayerView helper. The dispatch map exposes every move by name: dispatch.placeMark(playerID, args) sends the event through the reducer.
3. Run it
From the root of the monorepo:http://localhost:3000. Click cells. Fill a row. The game ends and result.winner pops into the snapshot.
4. Add a Bun CLI (optional)
cli/src/index.ts
createLocalSession is the same reducer React uses, just without the store wrapper. applyEvent is the low-level dispatch primitive.
Run it:
What you learned
- A game definition is a pure value you can share across a worker package, a React app, and a Bun CLI.
- Gamekit moves compile to core transitions;
move.invalid,move.endTurn, andmove.finishare the outcomes you will use most. createOpenturnBindingsgives you typed React bindings with no boilerplate.- The CLI uses the raw
createLocalSessionbecause it does not need React.
What to do next
- Tutorial: tic-tac-toe multiplayer reuses this same game package and hosts it.
- Tutorial: tic-tac-toe replay captures matches to JSON and builds a playback UI.
- Tutorial: tic-tac-toe with core rewrites the same game without gamekit sugar.