Skip to content

Fishing gauge native read - #13

Merged
averagenative merged 7 commits into
mainfrom
fishing-gauge-native-read
Sep 13, 2026
Merged

averagenative merged 7 commits into
mainfrom
fishing-gauge-native-read

Conversation

@averagenative

Copy link
Copy Markdown
Owner

No description provided.

averagenative and others added 7 commits September 12, 2026 18:32
readWind matched two of the game's three arrow sprites. DartWind2 -- the one
shown at 18 mph and up -- is red-orange, matched neither hue window, and so the
strongest winds in the game read as 'none' and were modelled as still air. The
same shape of bug as the magenta gate fixed in 2e4a0ab, and the last of them.

Red is harder than the other two because it shares the HUD's own palette. The
brown panel behind the arrow is hue 0-32 at saturation .30-.75; the amber text
and trim beside it run hue 33-44. The arrow's own hue is quantised -- 73.5% of
it under 36.3 and the rest exactly at 36.3, sitting inside the amber -- so hue
cannot separate them at any cutoff.

Brightness looked like the answer and was not. The arrow is v=1.00 throughout
and the brown never passes .72, but the amber reaches .96. A first cut at .85
invented a wind on 70 frames of a recording that has none; .97 still let 22
through. That was fitting a constant to whichever frame had last been looked
at, which is the same mistake this file already carries a warning about in the
aim reach floor.

What actually separates an arrow from HUD text is that an arrow is a solid
blob. The sprites fill 4-8% of the scanned window (480, 518 and 258 px of a
window 0.12W x 0.10H); the amber scatter being picked up was 22-32 px, under
half a percent. The old "at least 8 pixels" test dates from when only cyan and
magenta were matched, neither of which appears in the HUD at all. It is now 2%
of the window area, which sits in a 15x gap and is resolution independent.

Replayed against three recordings, all of which report no wind: 1032, 2369 and
3043 frames, zero false reds, and aim counts unchanged at 1032, 2369 and 2983.

Those recordings can only prove red is not seen where it should not be. None
of them contains an 18+ mph wind, so that red IS seen when it should be remains
unverified until one turns up in play.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Expanding the clicker pushed a collapsed Darts panel most of a screen down,
past the empty space under the Suite panel where it plainly belonged. That is
what shelving does: every wrapped panel starts below the TALLEST one before it,
so one tall panel sets the depth for everything that follows and leaves a hole
beside the short ones.

Panels now run along the dock's edge until the viewport is used up, which fixes
a set of lanes -- columns for a top dock, rows for a left one -- and everything
after goes into whichever lane is currently shallowest. A short panel fills the
gap beside a short neighbour rather than clearing the tall one. Lanes are
disjoint along the edge, so nothing can overlap however the depths fall, and a
panel is placed in the shallowest lane it actually FITS in where one exists,
since the panels differ in width by about 30px and one dropped into a narrower
lane would hang over its neighbour.

The rebuilt suite also carries the darts wind change from 1a18ba3.

Note for anyone editing the suite parts: `node --check` on 00-head.js or any
other 0*-*.js will always fail. They are fragments -- 00-head opens the IIFE
and 05-hub closes it -- so a syntax error there means nothing. Check the built
idleon-suite.user.js instead. Reading that failure as real cost a revert of a
good edit today.

Not seen running; the packing postdates the last injection.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The helper marked where each fish is and what power lands on it, but said
nothing about how close is close enough. That is knowable exactly. The game's
test is

    |fishX - bobberX| < 6 + SIZE[type]

with SIZE = [6,6,9,10,12,13,17,17] in lane units, the 6 being the bobber's own
half-width. Points identify the type -- 1pt is type 2, 2pt type 3, 3pt type 4,
5pt type 6 -- so the tolerances are 15, 16, 18 and 23 lane units, and the
pufferfish (type 5, size 13) is 19.

Those are stored as fractions of the lane rather than pixels, because the lane
is about 299.5 lane units across. Two independent routes agree on that number:
inverting the measured aim curve puts the lane's ends at game x 11 and 311, and
the game seeds fish between 40 and 295 with the bobber landing between 24 and
285, all inside that span. A fraction survives any window size; a pixel count
would not.

Two things are drawn from it. On the lane, a bar as wide as the real tolerance,
so a near miss looks near instead of mysterious -- a whale is half again as
forgiving as a fish, which the sprites do not suggest. On the gauge, the tick
becomes a BAND: the ends of the catch window mapped back through the aim curve
are the range of gauge fills that still land the cast. That is the release
slack, which is the thing actually being aimed at; a tick says where perfect is
and nothing about the room around it. The curve is not linear, so the band sits
asymmetrically around the tick and tightens for fish further out.

The pufferfish gets the same bar, since its window is how far away to stay, and
at 19 units it is wider than every catch except the whale.

Replayed over a fishing recording: 1289 frames, 1267 with a lane, no exceptions
-- which matters because the standalone has no error containment around its
loop, so a throw in the drawing would stop the helper dead. The bars and bands
have NOT been seen rendered; that needs a live fishing spot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The oscillator correction was disabled in 57faab9 because recovering cos from
|sin| plus a direction-of-travel sign is discontinuous exactly where the
platform spends most of its visible time. On a real run it flipped sign 34
times and jumped by more than 0.5 in cos 17 times, worst case +0.946 to -0.955
across a single frame, so the preview leapt between the strongest and weakest
shot.

Nothing needs guessing, because the period is known exactly: G16[0] gains 1.3
every 20ms and phi = 1.1*G16[0] degrees, which is 71.5 deg/s and a 5.035s
period. With w fixed,

    platY(t) = y0 + A*sin(wt) + B*cos(wt)

is ordinary linear least squares in three unknowns over a rolling window, and
cos(phi) = (A*cos(wt) - B*sin(wt)) / hypot(A,B) comes straight out -- continuous
everywhere, with no sign to choose and nothing to flip at a turning point.

The window is just over half a period. Shorter and sin and cos are too alike
across it to separate, which lets A and B trade places freely. The fit is
rejected unless its amplitude lands near the real swing (110 of 540 on the
design canvas) and its residual is small against that amplitude, because a fit
that has latched onto drift instead of the oscillation gives a confident wrong
phase, which is worse than no correction.

Replayed against the recording of the run that regressed:

                     before    after
    sign flips          34        7     (genuine zero crossings; cos passes
    jumps > 0.5         17        0      through zero twice a cycle)
    largest jump      1.90    0.119
    cos range                -0.99 .. 1.00

STILL DISABLED at the call site. Fixing the estimator is not the same as
showing the correction helps, and the test for that -- whether each shot's
fitted R tracks the cos it was thrown on -- cannot be run here: neither hoops
recording commits a single per-flight fit, since commitCal wants 6+ tracked
frames per flight and the replay does not produce them. cosPhi and the
per-flight fit are both still published on the probe, so a live run would
settle it. Re-enabling is one argument at the shotCurve call.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…enable

The physics was never in doubt: platform height and release velocity are one
oscillator in quadrature, so whether the platform is rising or falling changes
where the shot lands from the same height. What was wrong was the geometry for
how that redistributes across the arc, and it was wrong confidently.

Re-cutting the parabola from a fixed release point predicted the effect landing
almost entirely on R, the far crossing, and barely touching L. Driven against
the offline rip of the game -- 65 shots, 14 committed per-flight fits, cos
sampled across its full range -- it is the other way round:

    A vs cos   +0.0235 +-0.0435   r2 0.02    predicted 0
    L vs cos   -0.0949 +-0.0165   r2 0.73    predicted -0.0171
    R vs cos   +0.0253 +-0.0132   r2 0.23    predicted -0.0654

Curvature ignores the oscillator exactly as the physics says, which is the part
of the model that holds -- g/2vx^2 sees neither. But the coupling lands in L at
5.8 standard errors while R has the wrong sign and does not clear two. Most
likely because the tracked part of a flight pins the descending branch, leaving
L to absorb the change: R is measured, L is extrapolated.

So the slopes now come from the fits. R's stays in at its measured value
despite being weak, since dropping it would tilt the arc and 0.0253 is small
enough that being wrong about it costs little.

Re-enabled at the call site. It was switched off in 57faab9 because the old
estimator made the preview jump at every turning point; measured again now with
the phase fit and the correction live, over 12s of play: cos spans the full
-1..1, six sign changes where a 5.035s period predicts about five real zero
crossings, no jump over 0.5, largest 0.150. The old estimator managed 17 jumps
over 0.5 and a worst of 1.90.

None of this could be measured before. Neither hoops recording commits a single
per-flight fit -- commitCal wants 6+ tracked frames per flight and the replays
do not produce them -- so the offline rip is what made the test possible at all,
and it gives better data than live play could: unlimited shots, no cooldown,
and cos swept across its whole range by shooting slightly off the period.

Still not outcome-validated. That the predicted arc now matches the measured
coupling does not prove it sinks more shots, and the honest test for that is
whether ghostMade tracks made over a long run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…shotA

Re-enabling this in 10998a2 rested on the per-flight fits correlating with cos.
That is the helper's own reading of its own arc, and it is a much weaker test
than whether shots go in. Measured properly against the offline rip, using the
game's own score as ground truth and pairing each release to its result:

    GREEN  n=28    1/28 swish     19/28 scored (68%)
    red    n=21    0/21 swish     14/21 scored (67%)

Green means "this arc threads the hole". One of 28 did. And green scores no
better than red, so the preview carries no information about whether the shot
goes in. A second-order correction cannot be judged against a prediction that
is not discriminating at all.

Play matches. Descending shots come out worse than ascending (10/16 against
9/12, which is ~0.7 SE and proves nothing alone, but is the reported direction)
and ascending is off too when the rim is CLOSE -- small u, where the arc is
dominated by uL, which is exactly the term this correction moves hardest, up to
0.095 of canvas width. The measured L slope looks too large to apply raw.

Nothing measured here is thrown away: platCos and its phase fit stay, and so do
the coupling numbers. The physics is still right. But this cannot ship while
the arc's height at the rim is off by a mean of 54.7px, worst 117.5px, against
a hole far narrower than that -- a 70px correction is not measurable on top of
a 55px baseline error, which is also why 1 in 28 is the swish rate.

shotA goes back to 2.233 as well. v7 replaced that fitted value with 2.177,
derived as g/2vx^2 on the 960-wide design canvas, arguing the 2.6% gap was a
tracking bias rather than noise. The offline rip's own per-flight fits put
curvature at 2.2205, with the original fit and not with the derivation. Two
independent measurements agreeing against one derivation means the derivation
was wrong -- the same way the L/R geometry was.

The next thing worth measuring is the SIGNED error at the rim: the ball's actual
height as it crosses, against the predicted arc's height there, over a few
hundred automated shots. That gives direction and magnitude instead of "missed",
which is what fixing the base arc needs. The offline rip makes it free.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…r signed

"Missed" is not a measurement. The number that decides a hoops shot is the
height of the arc where it passes the rim, so the helper now publishes exactly
that -- ghostRimY -- which turns the error into something with a direction and
a size instead of a verdict.

Measured against the offline rip, using the game's own state for both sides:
its ball position [91][92] and hoop position [95][96], sampled in-page each
frame, against the helper's own predicted arc at the same x. No pixel inference
anywhere, on either side.

    SIGNED rim error, n=15   (negative = ball passes ABOVE the prediction)
      mean -76.3px   median -91.5px   sd 66.2   range -161.9 .. +49.6
      ascending  -93.1px (n=12)
      descending  -9.2px (n=3)

The ball flies well above the predicted arc, and nearly all of it is on
ascending shots; descending ones sit close to the prediction. That is what the
game's constants predict if the oscillator is ignored: ascending is cos<0, and
vy = -2.9 + 0.7*cos makes that the STRONGER shot at -3.6 against -2.2, so a
prediction built on an average-strength shot is undershot vertically by exactly
the sort of margin seen here.

So the coupling disabled in 5f0193a is real and is needed; what was wrong was
its form. Applying it through uL moved the arc in a way that did not correct
the height at the rim -- which is consistent with the earlier finding that
ascending and descending had the SAME error at the rim while the correction was
active, and with the report that close-rim shots were off.

A first pass at this measurement gave mean +32 against a median of -22, which
is a distribution dragged by one +346px outlier: the ball resets to the
player's hands between shots, crossing the hoop's x travelling LEFT. Requiring
a rightward crossing during flight fixed it and the two statistics now agree in
sign. Worth remembering that a mean and median of opposite signs means the
summary is being set by outliers, not by the effect.

n=15 with only 3 descending is thin. The rig is cheap to re-run now, and the
next step is enough shots to fit the correction against the rim error directly
rather than against the per-flight fits.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@averagenative averagenative self-assigned this Sep 13, 2026
@averagenative
averagenative merged commit 7bb6ca2 into main Sep 13, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant