# Shark Game Backend — FastAPI + SQLite + Auth + Draft + Scoring ## Database tables - users (id, email, phone, display_name, password_hash, created_at) - leagues (id, name, invite_code, status [draft/active/closed], season_start/end, created_by) - league_members (id, league_id, user_id, draft_order, is_commissioner) - regions (id, name, emoji, description, prev_year stats) - draft_picks (id, league_id, user_id, region_id, round, pick_number) — unique on (league_id, region_id) - scores (id, region_id, event_type, description, source_url, points 2/5/10, event_date, verified) - user_scores (user_id, league_id, total_points) — materialized, recalculated on new score events ## Seed data: 32 coastal regions The game ships with 32 pre-seeded regions with real-world lat/lng and historical stats (prev_year_sightings, prev_year_bites, prev_year_fatalities): - **IDs 1–12** (original): Florida East Coast, Florida Gulf Coast, Caribbean, California South, California North, Hawaii, Carolina Coast, Texas Gulf, Australia East/West, South Africa, Brazil Coast - **IDs 13–32** (expanded): New England Coast, New York Bight, Mid-Atlantic, Georgia Coast, Louisiana Gulf, Mexico Pacific, Mexico Gulf, Central America, South America Pacific, Mediterranean, West/East Africa, Middle East, India West/East, Southeast Asia, Japan, New Zealand, Pacific Islands, UK & Ireland **Seed strategy:** `INSERT OR IGNORE` — the regions table is populated once on first startup via `seed_regions()`. The `REGIONS_SEED` list in server.py is the single source of truth. When adding new regions, append tuples to the list and increment the total. The seed guard (`if existing == 0`) ensures existing databases are not touched. The frontend `MAP_REGIONS` array in `draft-room.html` mirrors the backend list with matching IDs, lat/lng for Leaflet markers, and a `stats:{s,b,f}` field on new regions (IDs 13+). Original regions (1–12) intentionally lack the `stats` field to avoid breaking the existing code path. ## Key patterns - **Snake draft:** Round 1 order 1→N, Round 2 N→1, Round 3 1→N - **Turn enforcement:** Check draft_order matches next_pick_number - **Commissioner-only:** Draft start endpoint validates is_commissioner - **Scoring:** sighting=2, bite=5, fatality=10. Recalculate all user_scores on new event - **Region assignment:** Region IDs 1-32 are pre-seeded. Event endpoint expects region_id, validates it exists. - **Frontend serving:** FastAPI catch-all route at the end serves static files + falls back to index.html