尚未收集仓库信息。
尚未检测到更新。GitHub 发布版本后会显示在这里。
制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗The rip is pulling swimmers past the buoys. Two lifeguard crews of two race to bring them in. Each crew has a Driver, who steers a rescue jet ski but can only see a small circle around it, and a Navigator, who works a drone (sweeps that reveal sandbars, rocks and jellyfish, plus up to two hazard marks on the Driver's screen) and the beach radio (scans that say how warm each square is, and a pin for their best guess). Both crews race for the same swimmer on the same map. Whoever reaches them first scores, and the next swimmer appears for both. Most rescues in 120 seconds wins.
Built in ~24 hours for HowdyHack 2026 by four developers.
Play: https://shoreline-game.vercel.app. You need one laptop and four phones on any network.
Run locally:
npm install
cp .env.example .env.local # fill in NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY
npm run dev:lan # phones on the same Wi-Fi open http://<laptop-ip>:3000
Without Supabase keys the app runs in a clearly labelled single-device mock mode. /versus/preview shows every screen with scripted data.
Check everything: npm run check (typecheck + lint + tests + production build).
phones (4) host laptop phones
───────── ─────────── ──────
PlayerAction ──► Supabase ──► VersusHostSession ──► versusEngine.step() ──► per-seat views ──► Supabase ──► render
(steer, sweep, Realtime (the ONLY simulation) two deterministic (views.ts filters Realtime (props in,
mark, scan) team worlds, one what each seat actions out)
shared survivor may see)
src/game/engine/): no clock, no Math.random, no input mutation. The same seed and inputs always produce the same match, which keeps the tests simple. Each crew has its own world (physics, sonar, radio), and resolveRescue keeps one shared survivor in sync across both.src/game/engine/views.ts). Phones never get the swimmer's position, except a drone contact when a sweep catches them and the Driver's close-up view. The laptop's big screen shows it live.src/game/engine/formations.ts): walls with gaps and horseshoes built from ordinary hazards. Each formation is kept only if a flood fill proves both docks can still reach the swimmer, and later swimmers only spawn where the docks can reach.applyMatchAction in src/game/engine/versus.ts). Tapping a mark removes it, a third mark is refused, and the radio pin never counts.node:test) across the engine, protocol, sessions, audio cues and UI helpers. All pass in npm run check.Also in the build: beach-themed UI; host music plus per-phone sound effects and haptics; a countdown synced to its audio; and an end-of-match beach landing where the rescued swimmers step off one by one.
| Path | What |
|---|---|
src/game/engine/ | Pure game engine: co-op core, versus rules (versus.ts), formations, survivors, views |
src/game/versus/contract.ts | Shared 2v2 types, tuning and the VersusEngine interface |
src/lib/realtime/, src/lib/game-client/ | Supabase transport, protocol, host and phone sessions |
src/lib/audio/ | Host music and phone sound effects |
src/app/versus/ | Host (/versus/host/[code]), phone (/versus/room/[code]) and preview routes |
src/components/ | Driver, Navigator, big-screen and shared UI |
The original four-role co-op mode (Captain, Sonar, Weather, Radio) is still in the code as legacy. Its routes redirect to 2v2, so it isn't reachable from the site.
Next.js 16, React 19, TypeScript (strict), Tailwind CSS v4, Supabase Realtime (broadcast + presence, no database), deployed on Vercel. Setup details: docs/DEPLOY.md. Rules for contributors and coding agents: AGENTS.md.