Game-family-specific logic. Each plugin handles all variants within its family through configuration, not code branching.
A plugin encapsulates the rules for a game family: legal move generation, capture mechanics, win/draw detection, and turn flow. The engine has 13 built-in plugins covering chess, go, draughts, reversi, mancala, backgammon, morris, hex, halma, shogi, xiangqi, race games, and Big 2.
Plugins receive configuration (from frontmatter) and produce behaviour. The same chess plugin handles standard, Fischer Random, Capablanca, and 72 other variants through different config parameters.
A plugin is a factory function that returns a plugin object:
export function createMyPlugin(config = {}, context = {}) {
return {
sliceName: 'myGame',
init(pluginConfig, { provide, request }) {
// Set up initial state, register capabilities
return { /* initial state */ }
},
getLegalMoves(state, topology, playerIndex) {
// Return array of legal moves
return []
},
applyMove(state, move, topology) {
// Apply move, return new state
// Return { state, continueTurn: true } for multi-action turns
return { state: newState }
},
checkWin(state, topology) {
// Return winner index, 'draw', or null
return null
},
}
}
All plugins implement from the same set of 11 hooks. No game-specific hooks exist.
| Hook | Purpose |
|---|---|
init | Initialise state, register capabilities |
getLegalMoves | Generate all legal moves for current player |
applyMove | Execute a move, return new state |
checkWin | Detect win, draw, or ongoing |
validateMove | Check if a specific move is legal |
getScore | Numeric evaluation for AI |
serialize | State to notation string |
deserialize | Notation string to state |
getMetadata | Piece lists, captured pieces, etc. |
onTurnEnd | Post-turn cleanup (timers, clocks) |
onGameEnd | Final state processing |
Each plugin owns a named state slice (its sliceName). Any plugin can read any slice, but only the owner can write to it. State must be JSON-serialisable.
The continueTurn mechanism allows compound turns: Morris mill removal, Backgammon multi-dice moves, Draughts forced capture chains.
Plugins register at packages/plugins/registry.js:
import { register, get, has, createFactory } from 'packages/plugins/registry.js'
// Register a custom plugin
register('myGame', { factory: createMyPlugin })
// Retrieve a factory
const factory = createFactory('chess')
const plugin = factory(variantConfig, context)
All 13 built-ins auto-register when packages/plugins/index.js is imported.
packages/plugins/my-game/ with src/my-game-plugin.js and index.jsindex.jspackages/plugins/index.jspackages/plugins/my-game/__tests__/register('myGame', { factory }) at startup without modifying any files.