Read the board
API
| Hook | Contract |
|---|---|
useBoard() |
State | null: the seat's view with its draft and its decision on its way applied. |
useMatch() |
{ ready: boolean; status: "waiting" | "in_progress" | "finished" | "locked" | null; viewer: PlayerId | null; isGameOver: boolean; online: boolean }. ready is false until the match, the seat's saved draft and the first page of the steps the seat has not seen have arrived. |
useSelf() |
{ playerId: PlayerId; isActive: boolean } | null. null for a spectator. |
usePlayers() |
PlayerProfile[]: { id: number; name: string; avatarUrl?: string | null; bot?: { id: string; difficulty?: "easy" | "standard" | "hard" }; abandoned?: boolean }, in the order of the state's players. id is the seat's player id. name is the person's username or the bot's name in the player's language, and bot is set only on a bot's seat. abandoned is true on a seat whose player left the match. A player leaving ends the match, so the match can be over while useMatch().isGameOver is still false. |
useScores() |
Scores: Record<string, { publicPoints: number[]; privatePoints: number[] }>, keyed by player id as a string. |
Drawing the board
Draw the board from useBoard(), the seat's view with its draft applied, and make only the offers of useOffers() clickable:
import { useMemo } from "react";
import { useAct, useOffers, useBoard, type Action } from "boardweaver/react";
export default function App() {
const board = useBoard();
const offers = useOffers();
const act = useAct();
const offerBySpace = useMemo(() => {
const bySpace = new Map<string, Action>();
for (const { action } of offers) {
if (action.type === "SpaceClick") bySpace.set(action.spaceId, action);
}
return bySpace;
}, [offers]);
if (!board) return null;
return (
<div role="grid">
{board.spaces.map((space) => {
const offer = offerBySpace.get(space.spaceId);
return (
<button key={space.spaceId} disabled={!offer} onClick={() => offer && act(offer)}>
{space.pieces?.[0]?.publicKind ?? ""}
</button>
);
})}
</div>
);
}
- The board is JSON in camelCase:
spaces[].spaceId,pieces[].publicKind,publicState,metaData. Parse a map with the schema of its type fromboardweaver/game, as Client types shows. - Pieces have no ids. Use the space id and the index as React keys.
- A piece click names the piece by its index in this board:
{ type: "PieceClick", spaceId, index }. useOffers()is empty for a spectator, on another seat's turn, and while a decision is on its way.
Scores
useScores() is keyed by the player id as a string: scores[String(playerId)].publicPoints. Each seat sees every seat's publicPoints and only its own privatePoints. The game logic names no score columns: name each column in the client, with a message from your catalog, such as in GameOver.
Spectators
A spectator's client renders the same component. useSelf() is null, useMatch().viewer is null, and there are no offers. A spectator starts at now, and sees each player's steps one at a time, as a seated player does.