Skip to main content
Use @openturn/core directly when you want full control over the state graph or when gamekit’s move-first shape does not fit.

Skeleton

Declare events

defineEvent<T>() declares an event with a typed payload; defineEvent() (no type argument) is a no-payload event.

Declare states

Each state is a node of the game graph:
  • activePlayers gates who can dispatch.
  • label, control, metadata surface in snapshot.derived for tooling.

Declare transitions

Each transition branch declares from, event, and resolve:
Resolver returns:
  • null | false | undefined — branch does not match.
  • rejectTransition(code, details) — event is invalid; reject it.
  • { G?, enqueue?, result?, turn? } — branch matches; commit.
Every (from, event) pair can have multiple branches as long as exactly one matches at any time.

Queue internal events

Need a chain reaction after committing? return { G: next, enqueue: [{ kind: "resolveEffects", payload: ... }] }. See reducers and queued events.

Selectors and views

Same as gamekit:

Why bother when gamekit exists

  • You need more than one event per move.
  • Your state graph has nodes that are not phase-shaped.
  • You want to render the graph yourself and need stable transition labels.
  • You are writing a library that mixes authored games (gamekit would force you to pick moves up front).