Engineering docs
Hit for Six — the cricket wall (contract v1.7)
Last updated Oct 20, 2018 · 8 sections
A floor-to-ceiling portrait LED wall in a mall with a kiosk (a tablet) beside it. Shoppers scan the wall's QR and get a 4-digit code on their phone (and on WhatsApp when configured). They queue at the kiosk in person, enter the code, and pick one of the 10 sponsor packs at a time; the wall plays each ball and the pack flips to reveal 0 / 1 / 2 / 4 / 6 / OUT. The super over ends at 6 balls or the first out. Highest runs of the day tops the leaderboard. (v1.7.1 — this replaced the v1.7 phone picker and its notifications: no push, no phone-side timing, the queue is physical.)
This document is the design the code follows. Protocol detail is in
API_CONTRACT.md §v1.7; the engine is web/public/cricket.html; the phone page
is web/app/play/[screen]; the backend is app/services/cricket.py +
app/api/cricket.py; the shell plan is §6.
1. Why it works, and the two rules that make it fair
- The screen is the stage and the phone is the controller. Stadium "big screen" games draw crowds because everyone can see whose turn it is.
- The ad canvas is the stadium: the pack tiles, the boundary boards, the batter's helmet / jersey / bat, the scoreboard sponsor, the "Brite Super Six" sting.
- Picks are blind. Every innings gets a fresh shuffle of the config's
bag(default 4×0, 5×1, 4×2, 3×4, 2×6, 2×OUT over 20 packs). Values are sealed server-side when the player locks their picks and only revealed by the wall. Nothing the player can see tells them where the sixes are. - The server scores. The wall reports an innings, but runs are re-derived from the sealed balls before any prize is minted. A tampered wall cannot inflate a score.
Expected score with the default bag ≈ 9 runs over 6 balls; max 36; ~45 % of
innings end in an OUT before the sixth ball. Tune bag per campaign.
2. The turn, timed
| step | who | seconds |
|---|---|---|
| intro card / walk-in clip | wall | 2.6 (or clip) |
| per ball: pack flips 1.6 → ball bowled 0.9 → clip or procedural shot 3–9 → score 0.5 | wall | 6–12 |
| result card with QR | wall | 7 |
≈ 50–70 s per innings → 50–70 innings an hour on one wall. Players pick while
they wait, so a turn is only the watching part. For peak hours set balls to
3 in the config.
3. Queue rules (v1.7.1)
- A ticket (
queued) is a session with a 4-digit code, good for 30 min. - The kiosk claims a code →
playing. One innings per wall; a second claim while someone bats is refused with their name, so the person in the queue just waits. - The kiosk sends one pick at a time; the server reveals the sealed value and the wall plays it; the next pick unlocks only after the wall reports it has shown the ball. An innings idle for 90 s closes as it stands.
- No notifications anywhere. The phone mirrors the innings and ends with the scorecard + claim link.
3a. Queue rules (v1.7, phone picker — retired)
queued→ready(called) →playing→done.- A player is only called once their picks are sealed; players without picks keep their place but cannot be promoted.
- Called players have
turn_timeout_s(default 25) to tap Play. No tap →skipped; the phone shows "missed your turn, join again". - A started innings the wall never reports is
expiredafter 180 s. - The wall polls every 2 s; phones poll every 2 s while live. Push (WhatsApp / Web Push) is a later upgrade; the phone buzzes with the Vibration API when called.
- One player per wall at a time.
MAX_QUEUE= 200.
4. Prizes, names and sharing
- Prize bands by runs (
prizes[].min_runs), highest band first, walking down when a band'sdaily_budgetis spent. The 0-run band is what everyone gets. - A prize is minted through the spin ledger (a
Spinrow withsymbol_key = "cricket"+ aWinToken), so the wallet, OTP, coupons, redeem and metrics from v1.5 are unchanged. The phone's result screen links to/w/claim/{token}. - Names are moderated before insert (
app/services/moderation.py): normalise (accents, leet, repeats) → URL / phone rejection → block list (English + Hindi / Telugu / Tamil / Urdu in Latin script;MODERATION_EXTRA_WORDSenv extends it). Strict on purpose: a false reject costs one retry. - No photos on the wall in v1. Players pick an emoji avatar. The share
card (
GET /v1/play/sessions/{id}/card.png) is a server-rendered PNG with name, runs, balls, rank, venue — shared through the phone's native share sheet. - Rule 3 holds: a session stores a display name and an emoji only. Rows feed the day's leaderboard; a cleanup job can purge rows older than 7 days.
5. Assets a sponsor uploads (portal → Creatives → Cricket wall)
| key | required | where it shows |
|---|---|---|
logo | yes | header, "Scoreboard by", callout sting |
hero | yes | portal card / attract |
pack | yes | the 20 tiles on the wall and the picker on every phone |
board | no | scrolling boundary-board strip |
scoreboard_logo, helmet_logo, jersey_logo, bat_sticker | no | the player card (helmet / chest / bat anchors) |
batter, stadium | no | override the template's batter cutout / stadium still |
clip_six, clip_four, clip_out | no | stadium clips per ball (fall back to the procedural shot) |
clip_dot, clip_single, clip_double, clip_walkin | no | more clips |
Clips carry no logos; the wall overlays the sponsor's. Demo set: Brite
(fictional detergent) in web/public/cricket/brite/; clips generated with
Seedance from one start frame so the batter stays consistent.
6. Native player shell (step 4 — plan)
The browser player is enough for the pilot. For a wall that must run for weeks, a thin native shell wraps the same HTML5 engines:
- Flutter (the repo already has
mobile/), targets Android boxes and Windows mini PCs on HDMI; Linux for free. - Boots on power-on, full screen, watchdog that restarts the web view on a hang
(no
gody:statsfor 120 s), reload at a slot boundary after an update. - Downloads and caches the whole content pack (clips, packs, engines) on schedule refresh; plays the ad loop and attract mode fully offline.
- Offline queue of plays / events exactly as the browser player (
storage.tssemantics), drained inbatch_seqorder. - Polls the cricket queue only; when the API is unreachable the wall shows the attract mode with "come back in a moment" and the phone says the screen is offline.
- Remote: version + logs in
GET /v1/ops/fleet, "reload" and "update" commands via the heartbeat body.
Not started: the Flutter SDK is not installed on the build machine. Estimate 2 weeks once the pilot proves the game.
7. Open items
- Web Push / WhatsApp nudge when a player is called.
- Per-day leaderboard prize (top of the board at closing) — needs an ops action.
- Purge job for
cricket_sessionsolder than 7 days. - Portal preview of the wall in ops approvals plays the local simulator
(
cricket.htmlwithout a host) — fine for approval, says so in the UI.