Skip to content

Parametric Tournament Structures

Status: shipped 2026-07-13 · Full ADR: .indusk/planning/archive/parametric-tournament-structures/adr.md

What was decided

A tournament's blind schedule and prize structure are open-ended parametric formulas, not fixed lists — computed on demand, tournament-owned, identical across every table of a tournament.

  • BlindsblindStructure + blindsAtLevel(n): six knobs (start, growth factor, level duration, ante start/size/growth), BB = 2×SB, every derived level snapped to a clean casino denomination (roundToNiceBlind). Escalation never runs out: the old getCurrentBlinds throw and both blindLevels.length - 1 caps are gone. Values plateau at MAX_BLIND_SMALL (1e12) — a safety cap, not a schedule cap.
  • PayoutspayoutStructure + payoutsForFieldSize(fieldSize): paid places = clamp(max(minPaidPlaces, round(fieldSize × paidPlacesPercent/100)), 1, fieldSize), weights w(p) = p^(−topHeaviness) normalized, floored to bps, dust → 1st — Σ == 10000 exactly at every field size. Defaults anchored to real-world SnG convention (9-max ≈ 50/30/20, 6-max ≈ 65/35, HU exactly 100) as trajectory tests.
  • Sealed fieldSize (the MTT keystone) — pool = (entryFee − rakeFee) × fieldSize, where fieldSize is the sealed entrant count (SnG: the lobby filling; MTT late-reg just moves the seal). No pool or payout path reads seats capacity.
  • Deadline-based durable level clock — the DBOS workflow's level clock is an absolute deadline (playStart + Σ durations); hand boundaries re-park on the SAME deadline. The prior recv-timeout-restarts shape meant a busy table never escalated.
  • Open-ended wire shapetotalLevels is number|null (null = parametric, no last level), tournamentSchedule is a preview + continues: true.

The compat window (D3)

Both sng schemas accept the parametric structure AND the legacy list (at least one), preferring the structure. resolveBlinds/resolvePayouts are THE lookups everywhere. Legacy blindLevels[] extrapolates open-endedly past its end (flat when the tail is deliberately flat); legacy prizeStructure is honored verbatim forever. Migration 0088 backfills legacy blinds → parametric (live tournaments excluded; per-row exception skip). Deliberately NO payout backfill: a legacy prize list is hand-tuned intent with no failure mode — converting it would change advertised splits. Distinguishing legacy-as-debt (blinds) from legacy-as-intent (payouts) is the reusable judgment.

Key tradeoffs accepted

  • A fund-adjacent settings-schema migration + a dual-shape window (removed once the compat window closes).
  • The parametric curve approximates hand-tuned SnG payouts (anchored at the defaults); exact splits remain expressible via the legacy list until explicit overrides land.
  • A wire-shape change (totalLevels nullable + continues) absorbed by every consumer in the same phase (monorepo deploys atomically).

See also

  • Guide: SnG Tournaments — the settings reference with both structures.
  • Changelog: 2026-07-13 entry — the plan-close story including the live-smoke haul (registration-reconciler bug, harness drifts).