Have more questions? Join our Discord

Upgrade a game

Follow these steps to move a framework v1 or v2 game to this framework. upgrade_framework answers this section when it moves a game here.

This is a rewrite of the game logic and the client, not an edit. Port one rule at a time and keep the old files open to read from until the last step.

Use what Boardweaver provides

Boardweaver now provides features that a framework v1 or v2 game had to build for itself. When the old game has its own version of one of these features, use Boardweaver's version. Never port the game's own version. Delete its code and its state, and wire the game to the feature in the table below.

The old game built its own Use this instead
Undo: an undo stack, saved snapshots, Immer patches or an undo intent table.allow_undo() in the game logic, and the Undo button from useUndo() in the client (Changing the state)
A move made in parts: selection state, tentative moves or arrange-then-commit in the client A decision of several steps: choice offers, then a confirm offer. The client shows the steps so far with useDraft() (Offers, intents and decisions)
Match history: a move log, a last-move banner, a replay, or a list of moves kept in metaData table.log(line) on each step (Changing the state). Boardweaver shows each step to the other players with its log lines, gives Back, Forward and Skip to now, and replays a finished match. The client reads the lines with useLastChange() and the board's place with usePlayback() (The React client)
Move animations from a diff of the old and new state The movements that Table's piece methods record. Animate useLastChange() movements, and set the time per step with useStepDuration()
Hidden hand order: shuffling ids, decoy pieces or sorting tricks .collection() on the space, .hiding() on the piece and .revealing() on the offer (Hidden information)
Random numbers: a seed or a generator kept in the state The rng argument of setup and apply_action (Randomness and determinism)
Bots: extra seats added in preGameInitialization, or bot moves picked in the client or on a timer A Bot in the game logic. The lobby seats it (Bots)
Player names and avatars kept in the state usePlayers()
A queue of moves on their way, or an offline banner usePending(), and useFailures() for refused moves
An end screen built into the board Boardweaver's own end screen, or a GameOver export with useMatchEnd() (The React client)
Rules, sound, full screen or leave buttons drawn by the client Nothing: Boardweaver draws them in the bar under the board

What is new

Framework v1 or v2 This framework
Game logic in TypeScript, in /src/game.ts Game logic in Rust, in /src/lib.rs and the modules it declares
gameConfig and preGameInitialization setup
getAvailableActions or getSelectableItems, and getButtons offers: one Offer per legal step, each an action, an intent and a label
applyActions changes the state in place apply_action changes the state only through Table, and refuses a move with Err
getPlayerScores, scoreLabels, isGameOver scores, score_labels, is_game_over
PieceDef and SpaceDef classes, each with a kind Piece::new(public_kind) and Space::new(space_id, kind) values
A piece has a pieceId A piece has no id: it is named by its space and its index there
static constants of a kind A const or a data file in /src/data/
publicState, privateState public_state, private_state, the piece's owner, and private_kind for a hidden face
metaData, activePlayerIds meta_data, active_player_ids
v1: Boardweaver draws from render(). v2: useGameState, useAvailableActions and match.click useBoard, useOffers and useAct in /src/frontend.tsx

These carry over unchanged: uploaded art (the client reads it with useImage(key)), /theme.json, the rules pages and the marketing page.

1. Read the new contract

Read How a game works. Call get_example with tic-tac-toe-rust and tic-tac-toe-rust-client.

Done when you can point to each of the six game functions in the example, and to the place where its client takes an offer.

2. Map the state

Write down how each def, bucket and map of the old game becomes a space, a piece or a map of the new state. A piece the old code found by pieceId needs another way to be found: its space and index, or a field in public_state. Decide who owns each private part. Leave out every field that only served a feature in Use what Boardweaver provides, such as an undo stack, a move log or a random seed.

References: The state, Hidden information.

Done when every field the old game read or wrote has a place in the new state.

3. Port the game logic

Write /src/lib.rs with the six game functions and export_game!. Port the old rules one at a time. Write a #[test] for each rule as you port it. Draw randomness only from the rng argument.

References: The game functions, Changing the state, Offers, intents and decisions, Randomness and determinism, Testing a game.

Done when validate_code answers no Rust error, its tests counts a passed test for each rule, a start_game session plays to the end at each seat count, and the game logic has no own version of a feature in Use what Boardweaver provides.

4. Rewrite the client

Rewrite /src/frontend.tsx against boardweaver/react of this framework. Draw from useBoard(), make only the offers of useOffers() clickable, and take them with useAct().

References: The React client, boardweaver/react reference.

Done when validate_code answers no error that names /src/frontend.tsx, and the client has no own version of a feature in Use what Boardweaver provides.

5. Delete the old game logic

Delete /src/game.ts, /src/pieces/, /src/spaces/ and every other TypeScript file that only the old game logic imported.

Done when list_files shows no TypeScript game logic and validate_code answers valid: true.

6. Commit

Call commit. The commit builds against this framework and pins the game to it.

Done when commit succeeds.

Tips

  • Port the rules, not the code. TypeScript that mutates a piece found by id does not translate line by line: rewrite each rule against Table.
  • A move that took one v2 action and picked several values is several steps here, one value each. Mark the last step confirm.
  • Mark every offer whose result depends on hidden information with .revealing().
  • A PlayerId is opaque. Never assume seats are 1, 2, 3.
  • To stay on the old framework, call upgrade_framework with the game's committed major before you commit. It removes the pending upgrade, and your file changes stay.