Files
bkvargyasandClaude Fable 5.1 62efa4d127 Draw the bracket as a real tree with connectors; fit-to-screen and list views for phones
Matches are positioned per round with SVG connector lines between feeder and
next match, winners in bold, live matches tagged with their court, empty slots
labelled with what fills them (W R1.4, L R2.1), and the viewer's team highlighted
along its path. Double elimination renders winners, losers and finals trees.
Narrow screens default to a scaled fit-to-width view; a round-by-round list is
also available. Seed script gains --stage=bracket|double. Release 0.3.0.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MB7nCCAscYsb3zzkT6LHZi
2026-09-03 21:41:35 +00:00

64 lines
3.5 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
// Options: --stage=pool (default, mid pool play) | --stage=bracket (single elim in progress)
// | --stage=double (double elim in progress)
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 args = Object.fromEntries(process.argv.slice(2).map(a => a.replace(/^--/, '').split('=')));
const STAGE = args.stage ?? 'pool';
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]]);
}
if (STAGE !== 'pool') {
// finish pool play, build the bracket, decide a few bracket matches
while (engine.courts.some(c => c.matchId) && t.matches.filter(m => m.stage === 'pool' && m.status === 'pending').length + engine.courts.filter(c => c.matchId).length > 0) {
const c = engine.courts.find(c => c.matchId); if (!c) break;
clock += (12 + rnd() * 6) * 60_000;
const lo = Math.floor(8 + rnd() * 12);
engine.recordResult(c.matchId, [rnd() < 0.5 ? [21, lo] : [lo, 21]]);
}
const seeded = t.advanceFromPools({ perPool: 2, wildcards: 2 });
t.generateBracket(seeded, { type: STAGE === 'double' ? 'double' : 'single', thirdPlace: STAGE !== 'double' });
engine.tick();
for (let i = 0; i < (STAGE === 'double' ? 5 : 3); i++) {
const c = engine.courts.find(c => c.matchId); if (!c) break;
clock += (12 + rnd() * 6) * 60_000;
const lo = Math.floor(8 + rnd() * 12);
engine.recordResult(c.matchId, [rnd() < 0.5 ? [21, lo] : [lo, 21]]);
}
}
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(', ')}`);