Tournament engine (pools, single/double elimination, standings with proper tiebreaks, withdrawals, two-court queue with ETAs and alerts), a single-process Node server with SQLite via node:sqlite and a WebSocket live board, organizer desk, QR landing page that follows the tournament phase, Dockerfile and compose with cloudflared, and documentation for deploying and running a day. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01MB7nCCAscYsb3zzkT6LHZi
42 lines
2.2 KiB
JavaScript
42 lines
2.2 KiB
JavaScript
// Creates a demo tournament in the database so a fresh install has something to look at.
|
|
// Run with the server STOPPED (the server keeps state in memory and writes it back):
|
|
// DB_PATH=./data/courtside.sqlite node scripts/seed-demo.js
|
|
import { fileURLToPath } from 'node:url';
|
|
import { dirname, join } from 'node:path';
|
|
import { Store } from '../server/store.js';
|
|
import { Registry } from '../server/state.js';
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
const DB_PATH = process.env.DB_PATH ?? join(here, '..', 'data', 'courtside.sqlite');
|
|
const store = new Store(DB_PATH);
|
|
const registry = new Registry(store);
|
|
|
|
if (registry.get('demo-labor-day-2s')) { console.log('Demo tournament already exists at /t/demo-labor-day-2s'); process.exit(0); }
|
|
|
|
const { t, engine } = registry.create({ slug: 'demo-labor-day-2s', name: 'Demo Labor Day 2s', date: '2026-09-07', courtCount: 2, rules: { teamSize: 2, pointsTo: 21, winBy: 2, cap: 25 }, notes: 'Demo data. Scores are made up.' });
|
|
|
|
const names = ['Net Gains', 'Block Party', 'Sunburnt', 'Dig It', 'Kiss My Ace', 'Setting Ducks', 'The Spikers', 'Sandbaggers', 'Serves You Right', 'Bump Chumps', 'Ace Holes', 'Beach Please'];
|
|
for (const n of names) t.registerTeam({ name: n, players: 2, captain: 'Captain' });
|
|
t.closeRegistration();
|
|
t.generatePools(4);
|
|
let clock = Date.now() - 90 * 60_000;
|
|
engine.now = () => clock; t.now = engine.now;
|
|
engine.start();
|
|
// play the first seven matches so there are standings, then leave two live with a running score
|
|
let seed = 11;
|
|
const rnd = () => ((seed = (seed * 1664525 + 1013904223) >>> 0) / 2 ** 32);
|
|
for (let i = 0; i < 7; i++) {
|
|
const c = engine.courts.find(c => c.matchId);
|
|
clock += (12 + rnd() * 6) * 60_000;
|
|
const hi = 21, lo = Math.floor(8 + rnd() * 12);
|
|
engine.recordResult(c.matchId, [rnd() < 0.5 ? [hi, lo] : [lo, hi]]);
|
|
}
|
|
const live = engine.courts.filter(c => c.matchId).map(c => t.match(c.matchId));
|
|
if (live[0]) live[0].live = [14, 11];
|
|
if (live[1]) live[1].live = [7, 9];
|
|
t.banner = 'Demo tournament — scores are simulated';
|
|
engine.now = () => Date.now(); t.now = engine.now;
|
|
registry.persist(t.slug);
|
|
store.close();
|
|
console.log(`Seeded demo at /t/${t.slug}. Team codes: ${[...t.teams.values()].slice(0, 3).map(x => `${x.name}=${x.code}`).join(', ')}`);
|