## 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). - **Backend** — Rust, Axum, WebSocket, Rapier2D physics (server-authoritative, 120 Hz). - **Frontend** — TypeScript, Vite, Canvas2D, portrait-first touch UI. ### Run locally 1. Install Rust via [rustup](https://rustup.rs/) (if not already installed). 2. Start the backend: ```bash cd backend cargo run --release # listens on 0.0.0.0:3000 ``` 3. Start the frontend dev server: ```bash cd frontend npm install npm run dev # opens on 0.0.0.0:5173 by default ``` 4. Open one or more browser tabs to the same room URL, e.g.: ``` 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. ### 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. ### Architecture - 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. ### E2E tests With the backend and frontend dev server running: ```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 ``` ### Limitations / known simplifications - 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.