โ† All apps
๐Ÿ€„

games-mahjong

Georgianna's American Mahjong โ€” tutorial + practice + online multiplayer

Coolify

README

No README.

STATUS

# games-mahjong โ€” Status

## Joker-kong claims โ€” DONE 2026-06-16
Georgianna's first live bug report: she held a pair + Joker and the app offered
only a Pung when she could make a Kong. Fixed end-to-end across engine, AI, and
human UI.

- **Engine:** `claimOptions(player, tile)` in `turn.js` returns every legal
  claim kind for the tile โ€” `'pung'` if the player has โ‰ฅ2 naturals or โ‰ฅ1
  natural + โ‰ฅ1 joker; `'kong'` if โ‰ฅ3 naturals or โ‰ฅ1 natural and naturals+jokers
  โ‰ฅ 4. Discarded flowers and jokers are never claimable. `claimType` is now a
  thin back-compat shim (original semantics) used only by legacy tests.
- **Human UI:** `showClaimPrompt` in `game.js` now renders one button per legal
  option (Pung / Kong) rebuilt fresh on each prompt. Single-option claims look
  unchanged; two-option claims present both buttons side by side. Pass button
  unchanged.
- **AI:** `aiDecideClaim` (in `ai.js`) calls `claimOptions` then
  `claimKindForNeed(options, want)` โ€” claims kong when the target hand needs four
  of the tile, pung when it needs three, nothing otherwise. This also fixed a
  latent bug where the AI could mis-claim the wrong group size.
- `applyClaim` builds joker-kongs order-independently (final review caught that unsorted AI hands could otherwise yield a malformed 3-tile kong; fixed + regression-tested).
- Quints and sextets remain non-claimable (engine forms 3-/4-tile exposed groups
  only; extending is out of scope).
- **Verified 2026-06-16:** 37 engine tests pass (incl. `claimOptions`,
  `claimKindForNeed`, `applyClaim` joker-kong + joker-first regression, AI claim
  tests, full sim with an exposed-group-size invariant); build clean; browser
  smoke โ€” single-option Pung claim worked and hand updated; Pass path
  functional; `#claimActions` present; zero console errors.
- **Deployed 2026-06-16** โ€” Ken approved; pushed to `main` (auto-deploys to
  mahjong.kenlill.com via Coolify). Note: vendored `supabase/functions/_shared/
  engine` is stale (old claim logic); re

โ€ฆ(truncated for upload size)

DECISIONS

# Architectural decisions

Append-only running log. Each entry: context, decision, rationale, follow-ups.

---

## 2026-04-23 ยท Phase 1 โ€” Extract engine, keep single-player working

### D-001 ยท Vanilla ES modules, not a framework rewrite
The monolith is ~1200 lines of plain JS/HTML/CSS. Introducing React or Vue
would be a larger rewrite than the multiplayer work itself, and would
separate us from the original monolith we must keep working. We split into
ES modules and keep `renderTile` / `renderGame` as plain functions that
take state in and write DOM out.

### D-002 ยท Pure engine, no DOM or globals
Every module under `src/engine/` is pure. Functions take state + rng +
timestamp as arguments and return new state (or mutate the state object
they were given โ€” we're not chasing immutability, just referential
transparency). This is the hard gate: the engine must run in Deno.

### D-003 ยท Seeded RNG (mulberry32)
Using `Math.random()` inside engine code makes Edge Function behavior
non-reproducible, breaks tests, and prevents client-side speculative
rendering. Mulberry32 is ~8 lines, no dependency, and plenty good enough
for shuffle and AI randomness. The game's `state.seed` is the seed of
record; per-turn rngs are derived from `(seed ^ turn_index ^ seat)`.

### D-004 ยท Preserve original player indexing
The original monolith stores players as `[human(South), East, North, West]`
with turn order `(i+1) % 4`, which yields East โ†’ North โ†’ West โ†’ South
instead of the traditional East โ†’ South โ†’ West โ†’ North. Changing this
now would be a behavioral regression in single-player. We keep the old
indexing. If it bothers us for online play, the Edge Function can
remap seats to their canonical turn order at that boundary; we won't do it
as part of the extraction.

### D-005 ยท Mahjong validation stays `checkSimpleWin`
The monolith shipped a placeholder `matchPattern` (always false) and used
`checkSimpleWin` as the actual win check. That's the current behavior and
we match it. Real NMJL 

โ€ฆ(truncated for upload size)

MEMORY

# games-mahjong โ€” Memory

## What this is
American Mahjong tutorial + practice-vs-AI + online multiplayer, built for
Ken's **wife** Georgianna (NOT his sister โ€” early docs had this wrong).
Vanilla JS + Vite (NO React in the game itself),
Supabase (`nidovyzcsgmcqovukzvy`) for multiplayer via Edge Functions (Deno).
Engine is pure/dom-free in `src/engine/` and is synced into Edge Functions by
`npm run sync-engine`. Tests: `npm test` (node:test, engine only).

## Hosting
- Live at **https://mahjong.kenlill.com** on Ken's self-hosted sherpa-server
  (Coolify; `D:\dev\sherpa-server` project). Pushes to `main` auto-deploy via
  GitHub webhook โ€” **a push IS a production deploy**.
- Edge Functions deploy separately: `npm run deploy-functions` (Supabase CLI)
  or the Supabase MCP `deploy_edge_function` tool (used 2026-06-11 โ€” works,
  pass `ask_kenbot/index.ts` + `_shared/cors.ts` with entrypoint
  `ask_kenbot/index.ts` to preserve the `../_shared/` import).
- Old Render service `georgianna-mahjong` is suspended; render.yaml remains
  in the repo but is inactive.

## Standing facts
- GitHub repo (private) is source of truth; local restored 2026-06-08 to
  `D:\Personal\games-mahjong`.
- `.env.local` is gitignored and was lost in the re-clone; recreated
  2026-06-11 with the **publishable** key (`sb_publishable_โ€ฆ`). Values come
  from the Supabase MCP (`get_project_url` / `get_publishable_keys`).
- The app has TWO Anthropic-backed Edge Functions: `ask_claude` (legacy
  single-shot help, still deployed, no longer wired to UI) and `ask_kenbot`
  (KenBot chat with history, `claude-opus-4-8`). `ANTHROPIC_API_KEY` is set
  in Supabase Edge Function secrets and shared by both.
- **KenBot fully integrated (chat + voice), Ken-approved 2026-06-11** โ€”
  React island pattern, help-screen-only, replaces the old AI Help panel.
  Voice = `kenbot_tts` Edge Function (ElevenLabs proxy, voice ID defaulted
  in code); `ELEVENLABS_API_KEY` secret is set (Ken approved setting it from
  sherpa-kenbo

โ€ฆ(truncated for upload size)

CLAUDE.md

# Georgianna's Mahjong โ€” Project Guide

American Mahjong tutorial, practice vs AI, and online multiplayer. Built for
Ken's wife Georgianna for her first game night.

## Live

- **App:** https://mahjong.kenlill.com (Coolify on sherpa-server โ€” see `D:\dev\sherpa-server`)
- **Repo:** https://github.com/klill6506/games-mahjong (private)
- **Supabase project:** `nidovyzcsgmcqovukzvy` โ€” https://supabase.com/dashboard/project/nidovyzcsgmcqovukzvy
- **Old hosting:** Render service `georgianna-mahjong` is suspended; `render.yaml` remains but is inactive

## Running locally

```bash
npm install
npm run dev        # vite dev server on http://localhost:5173
npm test           # engine unit tests (node:test, no framework)
npm run build      # production build โ†’ dist/
npm run preview    # serve dist/ locally
```

Single-player ("Practice vs AI") works offline with no Supabase credentials.
Multiplayer is wired in Phase 4+ and requires env vars below.

## Where things live

```
index.html                   # shell with every screen div
src/
  main.js                    # entry โ€” wires screens and events
  ui/
    styles.css               # all CSS (lifted from the original monolith)
    render.js                # renderTile + renderGame, called with explicit state
  engine/                    # pure, dom-free, importable from Deno
    constants.js             # SUITS, WINDS, DRAGONS, PATTERNS, seat layout
    rng.js                   # mulberry32 seeded RNG
    tiles.js                 # createDeck, shuffle, tileKey, sortHand
    deal.js                  # newGameState (post-deal, pre-Charleston)
    charleston.js            # applyCharlestonPass, maybeEndCharleston
    turn.js                  # applyDraw, applyDiscard, applyClaim, applyMahjong
    ai.js                    # aiPickDiscard, aiDecideClaim, aiCharlestonPick
    patterns.js              # findBestPattern (used only for hints)
    engine.test.js           # node --test
  screens/
    menu.js, setup.js        # (wiring 

โ€ฆ(truncated for upload size)

Diary mentions

No recent diary mentions.