curltastic/README.md
Jason Dekarske 94c6949426 more changes
2026-07-11 10:13:48 -07:00

85 lines
3.6 KiB
Markdown
Raw 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.

## curltastic
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.
### 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 to switch between Team 1 and Team 2 anytime. Use *Copy share link* to invite another device.
### Controls
- 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.51.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).
### Physics (high level)
- 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 continuous model (clockwise → right).
- Stonestone contacts are **nearly elastic** (`STONE_RESTITUTION = 0.9`) so takeouts launch both rocks along the impact line instead of plastic-sticking.
- 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 running (`cargo run --release` on :3000):
```bash
cd e2e
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
```
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.
- Sweeping not implemented; friction/curl not shared-room state.
- Stones past back/sideline/hog rules are removed from play after simulation.