Skip to content

Balance Parameters

All tuning parameters live in src/shared/balance.js. A regression test enforces that every value here matches the source — if the values diverge the test suite will fail loudly.

Changing a parameter

A balance parameter change requires two commits: a code change to src/shared/balance.js and a documentation commit (docs(balance): ...) updating docs/game/balance.md. Silent edits are considered a bug.


Move classification

These thresholds determine how a move is labelled based on the win-percentage loss it caused.

ParameterDefaultDescription
BLUNDER_WIN_PTS30Win% loss ≥ 30 → Blunder (??)
MISTAKE_WIN_PTS20Win% loss ≥ 20 → Mistake (?)
INACCURACY_WIN_PTS10Win% loss ≥ 10 → Inaccuracy (?!)
GREAT_CP_MAX25Centipawn loss < 25 AND no win% loss → Great (!)
GOOD_CP_MAX50Centipawn loss 25–49 AND no win% loss → Good
NEAR_MISS_WIN_PTS3Moves within 3 win% of the best move are included in acceptedMovesJson

Moves below the inaccuracy threshold are OK if centipawn loss ≥ 50, Good if < 50, Great if < 25, Best if cp_loss ≤ 0.


Puzzle extraction

ParameterDefaultDescription
FINDABILITY_MIN0.04Minimum Maia probability of finding the best move. Below this → tagged engine_only (shown in review, not added to drill queue)
POLICY_TEMPERATURE1.0Temperature used for Maia policy probes. 1.0 = unmodified probability distribution
PUZZLES_PER_GAME_MAX6Maximum drillable puzzles extracted per game, ranked by instructiveness

FSRS drill scheduling

ParameterDefaultDescription
DRILL_BATCH10Maximum cards served per drill session
DUE_SOFT_CAP40Above this many due cards, the queue sort changes: opening cards first, then by instructiveness × overdue factor
TARGET_RETENTION0.90FSRS target recall probability at the scheduled review date
RATING_FAST_MS6000Correct on first try in < 6 s → Easy
RATING_SLOW_MS25000Correct in > 25 s → Hard
GRADUATE_REPS5Minimum review count for graduation eligibility
GRADUATE_INTERVAL_D180Minimum scheduled interval in days for graduation
SUSPECT_RECALL_MS2000Correct in < 2 s on the first spaced review → suspect_recall flag

Rating inference (the server assigns the rating; the player never picks it):

ConditionRating
Wrong, or hint usedAgain
Correct followup wrongHard
Correct, time > 25 sHard
Correct, time ≤ 25 sGood
Correct, time < 6 s, first tryEasy

Elo rating

ParameterDefaultDescription
ELO_STARTING1200Starting Elo for new players
ELO_FLOOR100Rating never drops below this value
ELO_DIFF_CLAMP400Maximum Elo difference used in expected-score calculation (FIDE standard)
ELO_K_PROVISIONAL40K-factor during the first ELO_K_PROVISIONAL_GAMES games
ELO_K_PROVISIONAL_GAMES15Number of games in the provisional period
ELO_K_MID20K-factor when Elo < ELO_THRESHOLD_HIGH
ELO_K_HIGH10K-factor when Elo ≥ ELO_THRESHOLD_HIGH
ELO_THRESHOLD_HIGH2100Elo threshold for switching to the high K-factor

Elo updates apply only to ranked games. Games where the coach fires an alert are automatically set to unranked. Drawfish games are always unranked.


Analysis pipeline

ParameterDefaultDescription
INCREMENTAL_DEPTH20Stockfish search depth for pre-eval during play
INCREMENTAL_MAX_QUEUE5When the pre-eval queue exceeds this depth, the engine switches to depth-18 catch-up mode

Post-game pass depths are fixed in the pipeline: pass 1 = depth 18 (depth 20 for pre-evals already cached), pass 2 = depth 22 MultiPV-3, pass 3 = Maia policy probe (go nodes 2).


Playing strength estimation

The strength estimator uses a calibrated linear model over Regan-Haworth scaled error. See Strength Estimation for the full methodology.

ParameterDefaultDescription
STRENGTH_ANCHOR_ELO1600Calibration anchor point: Maia-1600's Elo rating
STRENGTH_ANCHOR_ASE0.2638Calibration anchor point: Maia-1600's measured mean scaled error
STRENGTH_ELO_PER_ASE6500Linear slope: Elo units per unit increase in mean scaled error
STRENGTH_CP_CAP300Centipawn loss is capped at this value before applying the log transform
STRENGTH_DECIDED_CP600Positions where `
STRENGTH_MIN_PLIES12Minimum eligible plies in a game for a valid strength sample
STRENGTH_ROLLING_N10Number of games in the inverse-variance weighted rolling aggregate

The honest per-game noise floor is ±250–300 Elo at one standard error. The rolling aggregate reduces this substantially over multiple games.

Coefficients are version-tracked in calibration/strength-model.json. Running scripts/refit-strength.js produces a new coefficient set from ≥ 20 strength samples spanning ≥ 3 distinct Elo ratings.


Repertoire

ParameterDefaultDescription
REP_PLY_MAX30Coach is silent beyond this ply (half-moves from the start)
REP_CONFIRM_OBS2Observations of a move needed before it graduates from candidate to canonical
REP_ADMIT_WIN_PTS10Win% loss below this → move admitted (gate 1 passes)
REP_QUARANTINE_WIN_PTS20Win% loss in [10, 20) → move quarantined rather than refused
REP_MIN_ABS_WIN_PCT35Gate 3: win% after the move must reach this level when the best available move can (absolute floor)
REP_LINE_BUDGET_WIN_PTS20Gate 4: cumulative win% loss along the line must not exceed this (from Lincke 2001)
REP_RECENCY_HALFLIFE_DAYS120Half-life for the recency-weighted canonical vote
REP_BOOTSTRAP_CONFIRMED_MIN20Minimum confirmed canonical nodes before the coach begins alerting
REP_ALERTS_PER_GAME_MAX3Maximum coach alerts per game before the coach falls silent
REP_ALERT_TIMEOUT_SEC60Seconds the player has to respond to an alert before it auto-resolves
REP_AUTO_PROMOTEtrueAutomatically promote challengers when the evidence rules are satisfied
REP_CHALLENGE_ENGINE_TOL3Win% tolerance for classifying engine signal as "neutral" in challenge rules
REP_CHALLENGE_ENGINE_CLEAR2Win% advantage for an "engine-clear" promotion (challenge rule 2)
REP_AUDIT_DEPTH22Stockfish depth for A/B engine evaluations during challenge audits
REP_REVERSAL_SUPPRESS_ENCOUNTERS8Encounters a reversed challenger is suppressed before being re-eligible

Soundness gates

The four gates are evaluated in order. First failure determines the verdict:

  1. Forced mate — If the position has a forced mate, the move is refused regardless of win%.
  2. Per-move cost (REP_QUARANTINE_WIN_PTS) — Win% loss ≥ 20 → refused. Win% loss in [10, 20) → quarantined.
  3. Absolute floor (REP_MIN_ABS_WIN_PCT) — After the move, win% must be reachable to 35% if any available move can reach 35%.
  4. Line budget (REP_LINE_BUDGET_WIN_PTS) — Cumulative win% loss along the full line must not exceed 20 points.

Released under the MIT License.