brackt/app/routes.ts
Chris Parsons 8c3663e01b
Add PDC World Darts Championship simulator with Elo-based bracket (#248)
Fixes #123

* Add PDC World Darts Championship simulator (128-player bracket)

- New DartsSimulator: 128-player single-elimination, 7 rounds with
  PDC-accurate best-of-sets formats (bo3/bo5/bo5/bo7/bo7/bo11/bo13).
  ELO_DIVISOR=500 via set-level Bernoulli model. Two simulation paths:
  Path A (bracket drawn) simulates from actual DB matches; Path B
  (pre-bracket) seeds top 32 by world ranking in fixed positions and
  randomly draws the remaining 96 per simulation run (50,000 iterations).
- darts_bracket added to simulatorTypeEnum and simulator registry.
- world_ranking nullable integer column added to participant_expected_values
  (migration 0067); batchSaveSourceElos now accepts and persists it.
- Admin route /admin/sports-seasons/:id/darts-elo: bulk import with format
  "Player Name, 2099, 1" (name, Elo, optional world ranking), fuzzy name
  matching, auto-runs simulation and updates EV/snapshots on save.
- DARTS_128 bracket template added (scoring starts at Quarterfinals).
- 30 unit tests: math helpers, bracket seeding structure, Path A/B integration.

https://claude.ai/code/session_01WZ6FjMmC2eBeZ1564M9Ruz

* Review fixes: pre-compute hot-loop invariants, import normalizeName

- Pre-compute seededSlots and getSeededMatchOrder(32) before the 50k
  simulation loop — was being recomputed every iteration
- Pad unseeded pool to 96 once before the loop instead of inside it
- Inline bracket-building in the hot loop using pre-computed seededSlots;
  removes the per-iteration call to buildR1Bracket/getSeededMatchOrder
- Remove dead SEEDED_R1_PAIRS constant (was never referenced)
- Import normalizeName from ~/lib/fuzzy-match instead of redefining it locally

https://claude.ai/code/session_01WZ6FjMmC2eBeZ1564M9Ruz

* Fix lint failures: unused vars, eqeqeq, toSorted

- Remove unused seededSet/unseededSet variables in test
- Replace != null with !== null && !== undefined (eqeqeq rule)
- Replace .sort() with .toSorted() in simulator and route (unicorn/no-array-sort)

https://claude.ai/code/session_01WZ6FjMmC2eBeZ1564M9Ruz

* Fix TS2552: restore seededSet declaration removed during lint fix

https://claude.ai/code/session_01WZ6FjMmC2eBeZ1564M9Ruz

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-31 15:37:28 -07:00

129 lines
4.9 KiB
TypeScript

import { type RouteConfig, index, route } from "@react-router/dev/routes";
export default [
index("routes/home.tsx"),
route("i/:inviteCode", "routes/i.$inviteCode.tsx"),
route("leagues/new", "routes/leagues/new.tsx"),
route("leagues/:leagueId", "routes/leagues/$leagueId.tsx"),
route("leagues/:leagueId/settings", "routes/leagues/$leagueId.settings.tsx"),
route(
"leagues/:leagueId/upcoming-events",
"routes/leagues/$leagueId.upcoming-events.tsx"
),
route(
"leagues/:leagueId/sports-seasons/:sportsSeasonId",
"routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.tsx"
),
route(
"leagues/:leagueId/draft/:seasonId",
"routes/leagues/$leagueId.draft.$seasonId.tsx"
),
route(
"leagues/:leagueId/draft-board/:seasonId",
"routes/leagues/$leagueId.draft-board.$seasonId.tsx"
),
route(
"leagues/:leagueId/standings/:seasonId",
"routes/leagues/$leagueId.standings.$seasonId.tsx"
),
route(
"leagues/:leagueId/standings/:seasonId/teams/:teamId",
"routes/leagues/$leagueId.standings.$seasonId.teams.$teamId.tsx"
),
route("teams/:teamId/settings", "routes/teams/$teamId.settings.tsx"),
route("api/webhooks/clerk", "routes/api/webhooks/clerk.ts"),
route("api/queue/add", "routes/api/queue.add.ts"),
route("api/queue/remove", "routes/api/queue.remove.ts"),
route("api/queue/clear", "routes/api/queue.clear.ts"),
route("api/queue/reorder", "routes/api/queue.reorder.ts"),
route("api/draft/make-pick", "routes/api/draft.make-pick.ts"),
route("api/draft/start", "routes/api/draft.start.ts"),
route("api/draft/pause", "routes/api/draft.pause.ts"),
route("api/draft/resume", "routes/api/draft.resume.ts"),
route("api/draft/force-autopick", "routes/api/draft.force-autopick.ts"),
route("api/draft/force-manual-pick", "routes/api/draft.force-manual-pick.ts"),
route("api/draft/replace-pick", "routes/api/draft.replace-pick.ts"),
route("api/draft/rollback", "routes/api/draft.rollback.ts"),
route("api/draft/adjust-time-bank", "routes/api/draft.adjust-time-bank.ts"),
route("api/autodraft/update", "routes/api/autodraft.update.ts"),
route("api/seasons/:seasonId/draft", "routes/api/seasons.$seasonId.draft.ts"),
route("user-profile", "routes/user-profile.tsx"),
route("how-to-play", "routes/how-to-play.tsx"),
route("rules", "routes/rules.tsx"),
route("support", "routes/support.tsx"),
route("upcoming-events", "routes/upcoming-events.tsx"),
route("test-socket", "routes/test-socket.tsx"),
// Admin routes
route("admin", "routes/admin.tsx", [
index("routes/admin._index.tsx"),
route("sports", "routes/admin.sports.tsx"),
route("sports/new", "routes/admin.sports.new.tsx"),
route("sports/:id", "routes/admin.sports.$id.tsx"),
route("sports-seasons", "routes/admin.sports-seasons.tsx"),
route("sports-seasons/new", "routes/admin.sports-seasons.new.tsx"),
route("sports-seasons/:id", "routes/admin.sports-seasons.$id.tsx"),
route(
"sports-seasons/:id/participants",
"routes/admin.sports-seasons.$id.participants.tsx"
),
route(
"sports-seasons/:id/events",
"routes/admin.sports-seasons.$id.events.tsx"
),
route(
"sports-seasons/:id/events/:eventId",
"routes/admin.sports-seasons.$id.events.$eventId.tsx"
),
route(
"sports-seasons/:id/events/:eventId/bracket",
"routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx"
),
route(
"sports-seasons/:id/expected-values",
"routes/admin.sports-seasons.$id.expected-values.tsx"
),
route(
"sports-seasons/:id/futures-odds",
"routes/admin.sports-seasons.$id.futures-odds.tsx"
),
route(
"sports-seasons/:id/elo-ratings",
"routes/admin.sports-seasons.$id.elo-ratings.tsx"
),
route(
"sports-seasons/:id/surface-elo",
"routes/admin.sports-seasons.$id.surface-elo.tsx"
),
route(
"sports-seasons/:id/darts-elo",
"routes/admin.sports-seasons.$id.darts-elo.tsx"
),
route(
"sports-seasons/:id/golf-skills",
"routes/admin.sports-seasons.$id.golf-skills.tsx"
),
route(
"sports-seasons/:id/standings",
"routes/admin.sports-seasons.$id.standings.tsx"
),
route(
"sports-seasons/:id/regular-standings",
"routes/admin.sports-seasons.$id.regular-standings.tsx"
),
route(
"sports-seasons/:id/simulate",
"routes/admin.sports-seasons.$id.simulate.tsx"
),
route("participants", "routes/admin.participants.tsx"),
route("templates", "routes/admin.templates.tsx"),
route("templates/new", "routes/admin.templates.new.tsx"),
route("templates/:id", "routes/admin.templates.$id.tsx"),
route("data-sync", "routes/admin.data-sync.tsx"),
route("standings-snapshots", "routes/admin.standings-snapshots.tsx"),
]),
route(
"api/admin/export-sports-data",
"routes/api.admin.export-sports-data.ts"
),
] satisfies RouteConfig;