feat: interpolate the survival ramp instead of stepping it (#74 knee step 1) - #245
Merged
Merged
Conversation
…step 1) The acceleration milestones were a step function: at t=180 the multiplier jumped 1.6 -> 2.0 and target RPS went 12.0 -> 15.0 in a SINGLE frame, which the smoother then chased down in under two seconds. Step 0 measured the reference board's readable band at roughly 20% wide in arrival rate, so a 25% step vaults over it — at exactly the three-minute mark #74 complains about. The multiplier is now interpolated linearly between milestones. Endpoints are unchanged: every milestone time still yields exactly its milestone multiplier, so the difficulty envelope is the same curve without the cliffs between its sample points. Interpolating from 1.0 at t=0, rather than holding 1.0 until the first milestone, is deliberate — leaving that edge in place would keep one 30% discontinuity, and the point is that the ramp is continuous everywhere. The surge WARNINGS still fire at the original milestone times. The player's "a surge just landed" beat is unchanged; only the arrival is spread out, and announcing the threshold still tells them a new tier of pressure is in effect. Measured, and reported honestly: the worst single-frame jump in target RPS across a 700-second run falls from ~25% to 0.20%, and every milestone's multiplier is hit exactly. But the effect on the readable band is SMALL — time with target RPS inside the band goes 26.9s -> 29.0s, +8%, not the 5x the design hoped for. The reason is visible in the numbers: the early milestones (t=60, t=120) land while target RPS is still ~2-4, well below the band, so smoothing them changes little. The band widening has to come from the knee itself (step 4) and the smoothed axis (step 2); this step's real value is removing a discontinuity that would otherwise skip whatever band those steps build, and making the ramp a continuous, testable function. Tests: interpolation hits every milestone exactly, is monotonic, holds past the last, survives a 1- or 0-milestone config and a zero-width span, never moves target RPS more than 1% in a frame, crosses the old t=180 cliff smoothly, and still fires all six surge warnings at their original times. Mutation-verified: restoring the step function reddens three of them. 835/835 x3, eslint clean.
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.
Step 1 of the failure-knee work, on top of the step 0 baseline.
The problem
rpsAcceleration.milestoneswere a step function. At t=180 the multiplier jumped 1.6 → 2.0 and target RPS went 12.0 → 15.0 in a single frame. Step 0 measured the reference board's readable band at ~20 % wide in arrival rate, so a 25 % step vaults clean over it — at exactly the three-minute mark #74 complains about.The change
The multiplier is interpolated linearly between milestones. Endpoints are unchanged — every milestone time still yields exactly its milestone multiplier, so the difficulty envelope is the same curve without the cliffs between its sample points.
Interpolating from 1.0 at t=0, rather than holding 1.0 until the first milestone, is deliberate: leaving that edge would keep one 30 % discontinuity, and the point is continuity everywhere.
Surge warnings still fire at the original milestone times — the "a surge just landed" beat is unchanged, only the arrival is spread out.
Measured — including where it fell short of the design's hope
The band effect is +8 %, not the 5× the design hoped for, and the numbers show why: the early milestones (t=60, t=120) land while target RPS is still ~2–4, well below the band, so smoothing them changes little. Reporting it rather than quietly moving the goalpost.
This step's real value is therefore narrower than pitched: it removes a discontinuity that would otherwise skip whatever band steps 2 and 4 build, and turns the ramp into a continuous, testable function. The band widening itself has to come from the knee (step 4) and the smoothed axis (step 2).
Verification
Interpolation hits every milestone exactly, is monotonic, holds past the last, survives 1-milestone / 0-milestone / zero-width-span configs, never moves target RPS >1 % per frame, crosses the old t=180 cliff smoothly, and still fires all six surge warnings at their original times. Mutation-verified: restoring the step function reddens three of these tests. 835/835 ×3, eslint clean.