feat(stats): add clutch rate stat rendered as a percentage#318
Open
Amund211 wants to merge 1 commit into
Open
Conversation
Add `clutchRate` as a gamemode stat: the win rate in games where you lost
your bed. Every loss loses your bed, so bed-loss games = bedsLost and clutch
wins = bedsLost - losses, giving clutchRate = (bedsLost - losses) / bedsLost.
Like winrate it is a fraction in [0, 1] rendered everywhere as a percentage
(e.g. 85.4%) via the existing "percentage" format kind; on charts the plotted
value stays in [0, 1] and only tooltip/label text shows %.
Wiring:
- keys: promote the commented `clutchRate` placeholder into GAMEMODE_STAT_KEYS
- format: STAT_FORMAT_KIND entry ("percentage") — routes cards, table,
progression value/milestone, and both chart tooltips through it for free
- index: getStat/computeStat compute (bedsLost - losses) / bedsLost, guarding
the zero case as a fraction (a loss always increments bedsLost)
- labels: "Clutch rate" / "Clutch"
- progression: clutchRate joins the QuotientProgression family and reuses
winrate's 5%-step, [0, 1]-clamped milestones. Its dividend is synthetic
(bedsLost - losses), not a single stored stat, so computeQuotientProgression
is generalized to take dividend/divisor value accessors instead of stat keys
(behavior-preserving for the five existing quotient stats). The zero-divisor
"collapses to the dividend counter" fallback takes a nullable stat; clutchRate
passes null (no counter for a synthetic dividend) and reports no progress,
mirroring winrate when no games are played.
- session page: isLinearStat, getRelatedStats (bedsLost, losses), the
close-to-all-time set, and a bespoke ProgressionCaption case (% for the
quotient, plain counts for clutch wins/day and beds lost/day)
Chart series, URL schemas, explore checkboxes, and the stat-card grid pick
clutchRate up automatically by iterating ALL_STAT_KEYS. No cache-buster bump:
clutchRate is derived and touches no cached PlayerDataPIT type.
Progress computation built red-green TDD: dedicated clutch rate progression
cases (up/down 5% steps across all gamemodes, the clamp-at-100% reached case,
and the no-beds-lost degenerate case) plus nextNaturalMilestone cases.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deploying rainbow with
|
| Latest commit: |
608c6dc
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://6e229bb5.rainbow-ctx.pages.dev |
| Branch Preview URL: | https://add-clutch-rate-stat.rainbow-ctx.pages.dev |
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new derived Bedwars stat, clutch rate ((bedsLost - losses) / bedsLost), and integrates it across formatting, labels, session UI, and progression/milestone projection (including generalized quotient progression to support synthetic dividends).
Changes:
- Introduces
clutchRateas a newStatKey, computes it frombedsLostandlosses, and renders it using the existing"percentage"stat format kind. - Generalizes
computeQuotientProgressionto accept dividend/divisor value accessors (enabling synthetic dividend stats like clutch rate) and handles the zero-divisor/no-data case for clutch rate. - Adds comprehensive unit tests for clutch rate progression behavior and milestone selection.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/stats/progression.unit.test.ts | Adds clutch-rate progression and milestone unit tests across all gamemodes. |
| src/stats/progression.ts | Extends milestone strategy for clutch rate and refactors quotient progression to support accessor-based dividends/divisors with a synthetic-dividend degenerate-case path. |
| src/stats/labels.ts | Adds full/short labels for clutchRate. |
| src/stats/keys.ts | Adds clutchRate to the exported stat key set. |
| src/stats/index.ts | Implements getStat/computeStat logic for clutchRate. |
| src/stats/format.ts | Marks clutchRate as "percentage" for consistent rendering. |
| src/routes/session/$uuid.tsx | Wires clutchRate into session stat selection, related-stats list, and progression caption rendering. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Amund211
added a commit
that referenced
this pull request
Jul 10, 2026
DeltaTag decided good/bad from a per-call goodIfPositive flag, a hand-maintained inverse of the app's isBadStat semantics. Take a `stat` instead and route the decision through getTrendSentiment/getTrendDirection (#318), so the delta's colour follows the same good/bad rules as the rest of the app and the caller no longer encodes which way is "good". Make it tri-state: an exactly-flat delta now renders neutral (muted, flat arrow) instead of a green "+0.00". Colour follows sentiment; the arrow follows raw direction. The sentiment→colour map is typed against a ThemeColor union derived from the MUI Palette, so palette paths like "success.main" are typo-checked at compile time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a clutch rate stat: the win rate in games where you lost your bed. In Bedwars every loss loses your bed, so bed-loss games =
bedsLostand clutch wins =bedsLost - losses, giving:Like
winrate(added in #316) it is stored/computed as a fraction in[0, 1]but rendered everywhere as a percentage (e.g.85.4%) via the existing"percentage"format kind: stat cards, sessions table, progression value + milestone + caption, and chart tooltips. On charts the plotted value stays in[0, 1]; only tooltip/label text shows%, and the Y-axis stays numeric.Progress computation (red-green TDD)
The milestone/progression math is the heart of this change, so it was built test-first:
clutchRatekey and wrote the progression spec (up/down 5% steps across every gamemode, the clamp-at-100% "reached" case, the no-beds-lost degenerate case, andnextNaturalMilestonecases). 22 failing assertions.Clutch rate joins the
QuotientProgressionfamily and reuses winrate's 5%-step,[0, 1]-clamped milestones. Its dividend is synthetic (bedsLost - losses) rather than a single stored stat, socomputeQuotientProgressionis generalized to take dividend/divisor value accessors instead of stat keys — behavior-preserving for the five existing quotient stats (fkdr/kdr/bblr/wlr/winrate), which now pass(pd) => getStat(pd, gamemode, "…")and keep their original dividend stat as the fallback.The zero-divisor case (never lost a bed ⇒ no clutch data) has no dividend counter to collapse to, so it reports "no progress" — mirroring winrate when no games are played.
No cache-buster bump
Clutch rate is derived and changes no cached
PlayerDataPITtype; the only touched persisted surface is thez.enum(ALL_STAT_KEYS)URL param, which is not cached query data. Chart series, URL schemas, explore checkboxes, and the stat-card grid pick clutch rate up automatically by iteratingALL_STAT_KEYS.Preview
Open the same page on both and compare. On each, search a player, then select Clutch rate (staging — the new stat) or an existing ratio like WLR (current — to sanity-check the shared quotient path still renders identically). Clutch rate is a new stat, so it only appears on the staging column.
Staging is the Cloudflare Pages branch preview for
add-clutch-rate-stat(tracks the branch head).Test plan
pnpm tsc(app) +pnpm tsc:node(tests) — clean. TheRecord<StatKey>format map, the no-default label switches, andcomputeStatProgression'ssatisfies neverforce every required edit.pnpm lint:check— clean (oxfmt + oxlint).pnpm test:unit— 807 pass (incl. dedicated clutch rate progression cases: up/down 5% steps across all gamemodes, clamp-at-100%, no-beds-lost, plusnextNaturalMilestoneclutch rate cases).pnpm test(browser) — 1008 pass.🤖 Generated with Claude Code