Read the fishing gauge at native resolution, and correct the darts wind ratio - #9
Merged
Merged
Conversation
Two more captures in, the platform-height coupling that motivates all of this is itself unreliable. Across three captures it measures -0.13, -0.46 and -0.82; within the two halves of the third, -0.81 and -0.96. That is not one effect measured three times, it is what a correlation looks like at n = 6 to 12. Curvature is the one parameter the anchor cannot touch mathematically -- shotA is a*W straight off the fit, platform position never enters it -- so a coupling there is either real physics or estimator bias. The time domain rules out the first: across those flights vx holds to 1.3%, and neither vx nor fitted g tracks platform height, -0.11 and -0.14. The physical curvature g/2vx^2 is constant, so the wobble is noise in medA. A slope cap was tried on the strength of it -- refit y(x) using only the part of the arc below some |dy/dx|, since the steep tail is where the parameterisation degenerates and how much tail is tracked depends on how high the platform was. On its own capture it looked excellent, 12.3% down to 5.1%. It does not replicate: no effect on a second capture, and on a third the single whole-segment fit underneath it blows up to 92% spread, that fit being precisely what the gated median exists to avoid. Not shipped. What is stable is the thing already merged: the gated median's spread runs 4-12% by session against 47% before the screens. Anything finer needs more than a dozen flights per session to see, and every correction derived from a dozen has failed on the next dozen.
The game quantises the cast gauge: _event_Minigames1 charges with power = 1 - |sin(theta)|, theta stepping one degree a tick from 90, and sets the fill sprite's scaleY to round(64 * power). So the fill has 65 heights and one rung is one game unit. The track and fill sprites attach from one anchor at -87 and -23, and 87 - 23 = 64, so a full fill reaches exactly the track's top edge and fill/track really is the power. readMeter was being handed the 4x-downscaled frame. That put the whole gauge at ~21 rows, so a rung was under a third of a row and the reading could not resolve the game's own quantum at all. A one-row error was ~5% of the gauge, and since the aim curve's slope near full charge is ~1.18 lane-fractions per unit of power, it arrived as ~6% of the lane -- four times the error of the curve it was feeding. That is why long casts were far off while short ones looked fine: the charge law is flattest at the bottom, so the same row is worth very little near zero power. The gauge now gets its own native-resolution grab, about a fortieth of the frame and only once a lane exists. Replayed against five fishing clips: at the plateau the old code reads 0.727 -- 16/22, the only vocabulary it had -- where native reads 0.778. No clip reads worse than before and one improves 84 -> 88 frames measured. Two things this turned up. The column scan had to start bridging gaps: its "longest unbroken run" test only ever passed because the downscale blurred the pole's texture, the fill/track seam and the marker line into pixels that matched. At native resolution those gaps are real, a strict run measured 16-32 rows against the 38 demanded, and every frame above about a third charge failed outright. And the gauge geometry is now settled once and held rather than re-derived every frame -- a denominator that drifts is a slow scale error on every prediction, not noise the aim marker can average away. Snapping to the 64-rung ladder is computed and reported but deliberately not used. A rung is ~1.4px at this canvas size and a fill edge resolves to maybe half a pixel, so snapping misassigns rungs often enough to give back most of what it wins. 585 readings across the five clips sit no closer to the rungs than random. Consistent with a real ladder buried in measurement noise, which is what the pixel arithmetic predicts. calVer 7: samples paired with a gauge read through the downscale carry that error in the power axis, and refitting on them fits the error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The wind is one vector, but the game does not push equally hard along both axes with it. The minigame builds the wind as 30*cos(phi) and 30*sin(phi) into two slots, then each flight tick adds the horizontal slot over 600 and the vertical slot over 750. Same vector, different divisors, so the horizontal acceleration is 750/600 = 1.25x the vertical. predict() was applying one coefficient to both. Which axis moves is settled by how windK was measured: it was solved from the vertical acceleration difference between two wind clusters, so 0.0158 is the /750 term and it stays. The horizontal is the one that was never independently confirmed -- the per-throw x-fits scattered +-300px/s^2 -- and it is the one that was wrong. This is a ratio between two terms in one model, which is why it can be taken from the game at all. vN and gN would need the game's tick rate and design resolution to convert into these units, and neither is recoverable from the bundle, so those measured values are left alone. It should also account for the residual recorded against landN, "the unexplained leftover splits +-20px WITH the wind sign". A horizontal wind error does exactly that: it changes how long the dart takes to reach the board, so the dart lands at the wrong point on an otherwise correct vertical curve, and the error flips sign when the wind does. landN was fitted with the horizontal term 20% light and is carrying some of it, so it wants re-measuring on throws recorded after this. Unverified against footage. Both darts recordings replay identically before and after, because neither has any detected cyan wind for the term to act on -- which confirms no regression, not the fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The snap comment said this had to be answered on a live canvas rather than a recording, and it now has been. Watched through tools/chrome over a session of real casts, totalPx reads 64 and never anything else: the backing store is the game's own resolution, so a rung is exactly one pixel and fillPx/64 IS k/64 by construction. Fifteen distinct locked charges all came back whole rungs -- 0.063, 0.094, 0.141, 0.156 through 0.813 -- with the largest departure the 0.05% that three decimal places of printout account for on their own. That also explains the replay result the previous commit recorded, where 585 readings sat no closer to the rungs than random at any spacing. The recordings captured the canvas at its CSS size, 750 tall against the game's 540, so a unit spanned 1.389px and the gauge measured the ~89px we saw. A 1.4px rung, through H.264, against a fill edge the colour masks resolve to about half a pixel, does not survive being measured. The ladder was there and the capture destroyed it -- which was one of the two explanations offered, now decided. The snap stays off, and the pair of measurements makes the case better than either did alone: it is an identity at 64px, exactly where it would be safe, and unreliable at 89px, exactly where it would have to earn its place. There is no canvas size at which it is worth having. `snapped` stays in the probe as a tripwire -- if it ever diverges from rawFrac on a live canvas, the gauge is being read at a scale nobody has considered. Comment and provenance only; no behaviour changes, so no @Version bump. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
fd54875 edited idleon-hoops.user.js without bumping @Version, so Tampermonkey would never offer the update. It was pushed to hoops-anchor-note after that branch's PR had already merged, so no CI run ever saw it and the failure sat latent until this branch carried the commit into a PR against main. Comment-only or not, the check does not care and neither should it: a file that differs from the installed one and claims the same version is a file nobody receives. Co-Authored-By: Claude Opus 5 (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.
Suite goes to 1.18, fishing to 2.4 (calVer 7), darts to 1.5.
Both fixes came out of the game's own shipped bundle rather than off more
footage, and both were checked against the existing recordings with
tools/replay.Fishing: the gauge was read through the 4x downscale
The game quantises the cast gauge — it charges with
power = 1 - |sin(theta)|,theta stepping a degree a tick from 90, and sets the fill sprite's
scaleYtoround(64 * power). The track and fill attach from one anchor at -87 and -23,and
87 - 23 = 64, so a full fill reaches exactly the track's top edge andfill/trackreally is the power.readMeterwas being handed the downscaled frame, which put the whole gauge at~21 rows — a rung was under a third of a row, so the reading could not resolve
the game's own quantum. A one-row error was ~5% of the gauge, and with the aim
curve's slope near full charge at ~1.18 lane-fractions per unit of power it
arrived as ~6% of the lane. That is why long casts were far off while short
ones looked fine: the charge law is flattest at the bottom.
The gauge now gets its own native-resolution grab (~1/40th of the frame, only
once a lane exists), and its geometry is settled once and held rather than
re-derived every frame.
Replayed over five fishing clips: at the plateau the old code reads 0.727 —
16/22, the only vocabulary it had — where native reads 0.778. No clip reads
worse, one improves 84 -> 88 frames measured. Confirmed in live play.
Two things this turned up:
only ever passed because the downscale blurred the pole's texture and the
fill/track seam into pixels that matched; at native resolution a strict run
measured 16-32 rows against the 38 demanded, and every frame above about a
third charge failed outright.
totalPxis 64 and never anything else, so a rung isexactly one pixel and readings land on exact rungs by construction. The
recordings disagreed only because they captured the canvas at its CSS size
(750 tall vs the game's 540), making a rung 1.4px — which H.264 destroys.
The snap to the ladder is therefore computed and reported but left off: an
identity where it would be safe, unreliable where it would have to earn its
place.
Darts: the wind pushes sideways 1.25x harder than it pushes down
The wind is one vector, but the game does not push equally hard along both axes
with it — it builds
30*cos(phi)and30*sin(phi)into two slots, then addsthe horizontal over 600 and the vertical over 750.
predict()applied asingle coefficient to both.
windKwas solved from the vertical acceleration difference between two windclusters, so 0.0158 is the /750 term and stays; the horizontal is the one that
was never independently confirmed, and it gets the 1.25.
This is a ratio inside one model, which is why it can be taken from the game at
all —
vNandgNwould need the tick rate and design resolution to convert,so those measured values are untouched.
It should also account for the residual recorded against
landN("splits+-20px WITH the wind sign"): a horizontal wind error changes how long the dart
takes to reach the board, so it lands at the wrong point on an otherwise correct
vertical curve, sign-flipping with the wind.
landNis carrying some of thatand wants re-measuring on throws recorded after this.
Unverified against footage — both darts recordings replay identically before
and after, because neither has any detected cyan wind for the term to act on.
That confirms no regression, not the fix.
Note on the commit list
This branch also carries
fd54875("Also record that the correlation behind itis not stable"), a comment-only hoops commit. It was pushed to
hoops-anchor-noteafter that branch's PR (#8) had already merged, so nothingelse would ever carry it to
main. It rides along here rather than stayingstranded.
Not included
The lane is untouched, and is now the larger of the two remaining errors.
stableLanestill takesmin(x0)/max(x1)over 1.5s — a monotone envelope thatcan only grow — and the blue run is still the wrong reference for a cast the
game measures from the launch point.
🤖 Generated with Claude Code