Files
loto/web/docs/project-overview-pdr.md

148 lines
8.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Lô Tô — Project Overview & PDR
## What is Lô Tô?
This app replicates **Lô tô hội chợ** (Vietnamese fairground / carnival lô tô),
specifically the **Tân Tân** style most familiar from southern Vietnam class
reunions and Tết gatherings. Inspiration: TN1 class reunions (2014–2017)
where players ran build of physical bingo cards.
It is **not** the British Bingo 90 / Italian Tombola 3×9 / 15-number ticket.
That format is intentionally out of scope.
## Game Variant & Scope
### In scope (Lô tô hội chợ Tân Tân)
- **Card**: 9 rows × 9 columns. Exactly **5 numbers per row** AND exactly
**5 numbers per column** (45 numbers per card; 36 blanks).
- **Visual layout**: card displayed as 3 stacked 3×9 mini-cards with
traditional separator labels — *Tân Tân*, *An khang thịnh vượng*,
*Tân Tân tốt nhất* — matching the physical Tân Tân BAMBOORAFT paper
sheet (tall non-square cells on mobile, wider on sm+; condensed bold black numbers on white,
purple empty-cell background by default — Excel Standard Purple `#7030A0`).
- **Column ranges**: col 0 = 1–9, col 1 = 10–19, …, col 7 = 70–79,
col 8 = 80–90.
- **Within a column**: numbers placed top-to-bottom in **ascending** order.
- **Visual rhythm (soft)**: the generator avoids rows with 3 consecutive
filled columns whenever possible (rejection-sampled per card). Hard
invariants (5 per row + 5 per col) always win if the constraint can't
be satisfied.
- **Number pool**: 1–90.
- **Win condition**: 1 row complete = **"Kinh!"**. After winning, the player
may keep playing further rows (game does not end).
- **Waiting state**: when a row needs only 1 number, app announces **"Chờ N"**.
- **Host (quản trò)** draws numbers from a shuffled 1–90 deck, tracks them on
a master board with circular tokens (light-blue ring and cream fill when
called, dim when uncalled, red ring + scale on the most
recent draw), and may also play their own card.
- **Settings**: gear icon in headers opens a modal with 5 fieldsets:
- Giao diện: theme switcher (auto / light / dark; auto mirrors OS pref)
- Chế độ quản trò: toggle to show MasterPanel on `/`
- Tự động xổ: enable auto-call; speed slider (1–10 seconds)
- Âm thanh: voice toggles for master + player + Vietnamese voice picker
(every `vi-*` edge-tts voice; default Hoài Mỹ)
- Màu ô trống: color picker + 10 Excel preset swatches
Persisted to `loto_settings`.
### Out of scope
- 3×9 / 15-number Bingo 90 / Tombola tickets (European format).
- Two-line and full-house win tiers.
- Custom number ranges (1–75 American bingo, etc.).
- Stake / pot / payout logic.
- Multiplayer real-time sync.
## Core Mechanics
- **Players**: Generate a randomized 9×9 card. The generator guarantees 5 per
row and 5 per column (constraint-aware picker, not loose weighted random).
Numbers within each column ascend top-to-bottom. Click cells to mark them.
- **Host**: Draws numbers randomly from a shuffled 1–90 deck, shows the
current number, and tracks all called numbers on an **11×9 master board
aligned by ones-digit** (col 0 = 1–9, col 8 = 80–90; 1 / 11 / 21 / … / 81
share row 1, etc.). Each called cell shows its 1-based **draw order** as a
small superscript so the host can quickly verify a "Kinh!" claim by reading
the order across the winning row.
- **Bingo**: When a row is complete, a celebration popup shows "Kinh!" with
confetti. Player keeps playing afterward — no game-end gate.
- **Waiting**: "Chờ N" toast when a row is one number away.
## Tech Stack
- **Framework**: SvelteKit 3 with Svelte 5 (runes mode)
- **Runtime**: Svelte 5 runes ($state, $derived, $effect, $props)
- **Styling**: Tailwind CSS 4 (utility-first, animations)
- **Persistence**: localStorage (no backend)
- **Audio**: pre-generated MP3 clips bundled under `static/audio/{voiceId}/`. Built once by `scripts/generate-audio.py` using free `edge-tts` (Microsoft Neural). Runtime plays via plain HTML5 Audio — no TTS API at runtime.
- **Deploy**: GitHub Pages at `/loto` (canonical, via GitHub Actions)
- **Dev Profile**: code-server compatible via `/absproxy/{port}` basePath + HMR proxy config
## Architecture Overview
Single page (`/`):
- Always renders the player card (generate, mark, bingo popup, "Chờ N" toasts).
- When `settings.masterMode === true`, mounts MasterPanel below the player card — host controls, draw history, and the host's own player card.
State is entirely client-side. Each card / panel instance uses a unique localStorage prefix (`"loto"` for the player card, `"loto_master"` for host draw state, `"loto_master_card"` for the host's own card).
## Deployment
- **Production**: GitHub Pages at `https://tiennm99.github.io/loto/` (canonical, basePath `/loto`). Deployed by the `deploy-pages` job in `.github/workflows/ci.yml` on push to `main`.
- **Development**: `npm run dev` (local), `npm run dev:codeserver` (code-server via proxy).
- **Build**: `npm run build:gh` generates static export to `build/` directory with the `/loto` basePath.
## Key Acceptance Criteria
- [x] Player card is 9×9 with **exactly 5 per row and 5 per column**.
- [x] Numbers within each column are **ascending top-to-bottom**.
- [x] Player can click cells to toggle crossed state.
- [x] Player can clear all marks on the current card without regenerating it (confirm prompt when marks exist).
- [x] No row has 3 consecutive filled columns (soft constraint, rejection-sampled).
- [x] Master speaks the called number aloud in Vietnamese (bundled MP3, no runtime TTS API).
- [x] Player hears "Chờ N" when a row is one away and "Kinh" on bingo.
- [x] Voice picker in settings — switch among every Vietnamese edge-tts voice.
- [x] Player numbers comfortably readable on a 360 px viewport.
- [x] Cell tap on mobile gives haptic + active-press + animated cross-out feedback.
- [x] Master "Số vừa xổ" hero is the page focal point and auto-scrolls into view on each draw.
- [x] Cold first paint shows a faded preview card + welcome line, not a gray placeholder.
- [x] Bingo celebration tiers: 3rd+ row triggers a CSS confetti rain on top of the popup.
- [x] Bingo popup triggers when row is complete, shows row number and "Kinh!" message.
- [x] Player may keep marking after a Kinh — no game-end lock.
- [x] Toast notifications show "Chờ X" before bingo (one number remaining).
- [x] Host can draw numbers and see them on the **11×9 last-digit-aligned master board**.
- [x] Master board shows **draw order** on each called cell for Kinh verification.
- [x] Host has their own player card (isolated by localStorage prefix).
- [x] Offline persistence via localStorage (grid and crossed state).
- [x] Dark mode support (Tailwind dark classes).
- [x] Mobile-responsive (base + sm breakpoints).
- [x] HMR works on code-server via proxy.
- [x] Player card rendered as 3 stacked Tân Tân mini-cards with separator labels.
- [x] Settings modal with 4 fieldsets (theme, master mode, auto-call, color); applies to both grids; persisted.
- [x] Theme switcher (auto / light / dark) with explicit dark class selector on `<html>`.
- [x] Master mode toggle to show MasterPanel on player page.
- [x] Auto-call timer (1–10s speed) with start/stop button.
- [x] Mobile-responsive layout (aspect-square sm:aspect-[3/5] cells, text scaling).
- [x] MasterPanel extracted; mounted conditionally on `/` when master mode is on.
- [x] PageFooter with tagline + link.
## Visual Language
- **Wordmark gradient**: rose → amber → rose, italic + drop-shadow — vintage carnival marquee feel.
- **Player accent**: indigo → purple (primary brand, positive action).
- **Host accent**: orange (subordinate to wordmark; not gradient).
- **Completed rows**: sky blue (success indicator).
- **Waiting toast**: amber (attention, ephemeral).
- **Emojis**: 🎉 ✨ 🎊 🥳 ❤️ (celebration, joy). Confetti rain on 3rd+ bingo per card.
- **Haptic**: 10 ms vibration on cell tap (mobile only, where supported).
## Future Considerations (Not Committed)
- Undo last crossed cell
- Sound effects on bingo
- Theme switcher
- PWA install
- Multiplayer sync (real-time via WebSocket)
- i18n beyond Vietnamese
Last reviewed: 2026-05-09 (scope locked: Lô tô hội chợ Tân Tân + theme/master/auto-call)
Last synced: 2026-05-09 (deploy target switched to GitHub Pages)