Skip to content
99ersstudio
All work
Games/Live

HBS Board Games

Three native board games — Chess, Checkers, Backgammon — on one shared HBS style canon. React Native + Expo, TypeScript strict, on-device only.

3
games
132
chess tests
shared
style canon

The problem

Most boardgame `starterkit` pitches collapse the moment you ship the second game in the series. Either the shared layer over-fits to game one and forces game two into the wrong shape (a chess-shaped engine does not want to model a doubling cube), or the shared layer is so thin that game two reinvents the timer, the haptics, the sound, the motion language, and the design tokens from scratch. We wanted three credible single-purchase apps — Chess, Checkers, Backgammon — that look and feel like they came from the same studio without being palette-swapped copies, and without each one carrying its own ad-hoc Reanimated config.

How we built it

  1. 01Wrote `HBS_STYLE_CANON.md` at the workspace root as the authoritative spec for color system, six-level elevation / shadow ladder, radii scale, spacing rhythm, motion language (`DURATION` / `EASE` / `SPRING` tokens), feedback patterns (haptic + sound + visual per game-event), accessibility rules (every `withRepeat(-1)` loop guarded by `isReducedMotion()`, ambient effects pause via `AppState`), and performance constraints (no more than 3 concurrent `withRepeat` loops on screen). Every app imports an identical `src/theme/motion.ts`.
  2. 02Kept three things per-game and only three: the rules engine, the AI, and the identity palette. Chess (`src/engine/ChessEngine.ts` + `ChessAI.ts`) is full minimax alpha-beta with piece-square tables, a 102-position weighted opening book, and 5 difficulty levels mapped to Elo 600–2000. Checkers enforces mandatory jump and multi-jump chain capture with a 3-level alpha-beta. Backgammon (`BackgammonEngine.ts`) handles bar re-entry, hit blots, bearing-off, doubles, doubling cube 1–64, and gammon×2 / backgammon×3 detection with a greedy pip-count AI.
  3. 03Centralised the multiplayer + backend story in Chess as the flagship: WebSocket client with auto-reconnect (`NetworkClient.ts`), REST `ApiClient` for users / leaderboard / teams / game results, a Node.js + PostgreSQL server with `users` / `elo_history` / `games` / `teams` / `team_members` tables, and FIDE Elo computed server-side to prevent client tampering. Checkers and Backgammon share the same `NetworkClient` shape under `packages/network/` — wiring them up is a deploy step, not a re-implementation.
  4. 04Hardened the same set of UX cross-cuts in all three: tap-to-move plus drag-and-drop on the gesture handler, slide animations for moved pieces, capture / bear-off animations, a 60 ms-cooldown event-based haptics system, `expo-av` sound effects (6–7 per game) with persisted mute toggle, an animated thinking-pulse when the AI is computing, an ambient board glow that pauses via `AppState` when the app backgrounds, and a tokenized 6-level shadow ladder. The Immer top-level `setState()` gotcha is documented in workspace memory so every store mutation goes through the right path.
  5. 05Ran a multi-pass cross-game audit before locking style-baseline-v1: deep-polish reports per game, a cross-game haptics + graphics audit with sign-off, an RC QA matrix, a parity final verdict, plus a `qa-checklist.sh` that walks all three apps in one shell run. Each game keeps its own `CLAUDE.md` / `README.md` / `ROADMAP.md` / `STORE_LISTING.md` and its own git tag, but all three sit under one workspace-level `docs/` tree so the next polish pass touches them in lockstep.

Outcome

All three games at post-MVP polish, locked at `style-baseline-v1` in March 2026. Chess on RN 0.76 / Expo 52 with 132 of 132 tests passing across 6 suites (~3 s) and a standalone APK built and device-tested. Checkers and Backgammon on RN 0.74 / Expo 51 with their own engines, AIs, sound packs, EAS profiles, and DE / EN i18n (~110 keys per app, auto-detect device language). Shared canon at `HBS_STYLE_CANON.md`, identical `motion.ts` across all three. Chess backend code exists (PostgreSQL + REST + WebSocket on port 4000) but is not yet deployed to a managed host.

Stack

React NativeExpoTypeScriptZustandImmerReanimated

React Native 0.74–0.76 · Expo 51–52 (managed workflow) · Expo Router 3.5–4.0 (file-based) · Zustand 4.5 + Immer for state · Reanimated 3.10–3.16 for animations · `expo-av` for sound · `expo-haptics` for haptics · AsyncStorage for persistence · TypeScript strict (zero compiler errors) · Jest + `jest-expo` (Chess: 132 tests / 6 suites / ~3 s) · EAS Build for store builds · Node.js + PostgreSQL backend for Chess multiplayer.

Next up

Build the Settings UI for the quality-tier picker (High / Balanced / Battery) and the high-contrast toggle — the store fields are already persisted, the setters just need to be wired via `useEffect` in each `GameScreen`. Then deploy the Chess backend to a managed PostgreSQL host so online multiplayer flips from `code exists, not deployed` to live, and connect Checkers and Backgammon to the same `NetworkClient` interface so all three share the matchmaking surface.

More case studies