๐
games-mahjong
Georgianna's American Mahjong โ tutorial + practice + online multiplayer
- Primary: https://mahjong.kenlill.com
- GitHub: https://github.com/klill6506/games-mahjong
- Local:
D:\Personal\games-mahjong
Coolify
- App:
games-mahjong - Status: live
- URL: https://mahjong.kenlill.com
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.