From 0c5449cd1e65928d1461f80a68440b6c6a2579a0 Mon Sep 17 00:00:00 2001 From: Jason Dekarske Date: Fri, 10 Jul 2026 23:48:15 -0700 Subject: [PATCH] docs: PR-B physics, scoreboard, team1/2, multi-client UX Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- README.md | 70 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 0e13e05..9f8eb31 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## curltastic -A multiplayer 2D curling game for mobile browser. Any number of clients can join a room, pick Red or Yellow freely, and throw when it is that team's turn (solo pass-and-play or remote opponents). +A multiplayer 2D curling game for mobile browser. Any number of clients can join a room, pick **Team 1** or **Team 2** freely, and throw when it is that team's turn (solo pass-and-play or remote opponents). Default palette: Team 1 red, Team 2 yellow. - **Backend** — Rust, Axum, WebSocket, Rapier2D physics (server-authoritative, 120 Hz). - **Frontend** — TypeScript, Vite, Canvas2D, portrait-first touch UI. @@ -25,45 +25,59 @@ A multiplayer 2D curling game for mobile browser. Any number of clients can join ``` http://localhost:5173/?room=DEMO1 ``` - Use the team dropdown in the top-right to switch between Red and Yellow — this enables local pass-and-play on one device. Use the *Copy share link* button to invite an opponent on another device. - -### Mobile devices - -The frontend binds to `0.0.0.0` via `--host`. Find your machine's LAN IP and open `http://:5173/?room=CODE` on the phone. Both devices must be on the same Wi-Fi and able to reach the backend on port `3000`. The default view is zoomed in on the house; drag the sheet vertically to scroll up to the hog line. + Use the team dropdown to switch between Team 1 and Team 2 anytime. Use *Copy share link* to invite another device. ### Controls -- When it is your team's turn, drag on the sheet to place the broom (aim point) — aim is not limited to the house. -- When it is not your turn, drag to pan the ice (default framing shows the house with sidelines; pan up toward the hog line). -- Use the team dropdown to choose which team's stone you are throwing; switch any time (including mid-end for solo play). -- Use the left/right curl buttons and the weight/friction controls; friction is a local scalar. -- Tap **THROW**. Anyone identifying as the turn team may throw. -- The server runs physics for every stone and streams multi-stone paths; the client animates them on one clock so collisions move together. +- On your team's turn, drag on solid ice to place the broom (aim is **not** limited to the house). +- Otherwise, drag to pan (default framing shows house + sidelines; pan up toward the hog line). +- Team dropdown: free switch mid-game (solo: throw for Team 1, switch, throw for Team 2). +- Velocity slider (release speed, m/s), curl buttons, friction scalar **0.5–1.5** (local; multiplies ice µ(v) table). +- Tap **THROW**. Anyone joined as the current turn team may throw. +- Parallel multi-stone trajectories; rotation θ comes from physics. +- Skeuomorphic 2×8 stones-left HUD; end-of-end modal with scoreboard (auto ~5s + manual dismiss). -### Architecture +### Physics (high level) -- WebSocket JSON protocol with tagged messages. -- Server simulates each throw at 120 Hz and sends a subset of `(x, y, t)` path points at 40 Hz. -- All game state, scoring, end management, and hammer rules live on the server. -- Disconnects are tolerated: the room and turn remain in memory. +- Pure initial **velocity** (m/s), not discrete weight. +- Ice friction µ(v) table (interpolated) × local scalar; linear and angular damping share the table. +- Curl: initial |ω| = 5 rot / 14 s; lateral speed ≈ k / v_forward (clockwise → right). +- Back line and sidelines are **not** colliders — touch → out of play after sim. +- Stone ids: `{ team, n }` with n = 1…8 per team per end. +- Foot-derived radii use `FEET_TO_METERS = 0.3048`. + +### Architecture / protocol + +- WebSocket JSON, snake_case tags. +- `game_state` includes totals, hammer, turn_team, **scoreboard** (per end: hammer, team1 pts, team2 pts), **stones_remaining**, stones. +- `trajectories` message: `{ stones: [{ stone_id, rotation, team, trajectory: [[x,y,theta], ...] }] }` sampled at 40 Hz from 120 Hz sim (`t = index / 40`). +- No room-full limit; no `end_scored` message (scoreboard replaces it). ### E2E tests -With the backend and frontend dev server running: +With the backend running (`cargo run --release` on :3000): ```bash cd e2e -npm install -g ws # or npm install ws locally in the project -node e2e_test.cjs # room lifecycle, throw, trajectory -node e2e_score.cjs # alternate turns / stones in play -node e2e_persistence.cjs # multi-throw stone persistence -node e2e_multi_client.cjs # 3 clients share state (no room-full) -node e2e_end_score.cjs # full end → end_scored + next end -node collision_trajectory_qa.cjs # multi-stone trajectory on collision +npm install -g ws # or npm install ws +node e2e_test.cjs +node e2e_score.cjs +node e2e_persistence.cjs +node e2e_multi_client.cjs +node e2e_end_score.cjs # full end → scoreboard entry + next end +node collision_trajectory_qa.cjs +node load_test.cjs # optional concurrent rooms ``` -### Limitations / known simplifications +Unit tests: `cd backend && cargo test`, `cd frontend && npm test`. + +### Branches (this work) + +- **`jasonlooked`** — PR-A refactor: multi-client, multi-path animation, free broom, camera, FEET_TO_METERS. +- **`pr2-features`** — PR-B features: physics rewrite, team1/2 wire, scoreboard, HUD, modal (branched from PR-A tip). + +### Limitations - No accounts, persistence, anti-cheat, replay log, or turn timer. -- Curl is fixed as a function of release speed (more curl at lower speed); sweeping is not implemented. -- Stones that pass the back line or leave the sheet are removed from play. +- Sweeping not implemented; friction/curl not shared-room state. +- Stones past back/sideline/hog rules are removed from play after simulation.