feat(butterfly): a jollier chase, and a catch that is a catch - #42
Merged
Merged
Conversation
The mid-visit eviction checked config.workMode, while the spawn gate a few lines above already used workModeOn(). So when Focus Guard flagged a meeting it blocked NEW visits but left a butterfly that was already here flying through the whole call. Same predicate on both sides now, plus a test that flips the focus signal mid-visit.
The 08-26 fix rested the paw 2.6-4.0s between swats and put a 900ms floor under the whoosh, but nothing guarded either number: the only whoosh test drives the scroll leaf with the butterfly switched off. Drive a whole visit with the bug parked in swat range and count: one whoosh per swat, swats at least 2600ms apart, whooshes at least 900ms apart, and the constants pinned by name so a nudge cannot slip through the bounds.
The catch was six white pixels and a heart, no sound, no face, and the cat snapped straight back to idle; a leap that missed looked exactly the same. Now a catch throws a fan of pink sparkles at the paws, one pleased trill (Sound on only), and opens a short victory window the idle branch turns into a quick double bounce with happy eyes and a blush. A whiff gets none of that: the cat lands and looks up after the bug instead. All of it is render-only, so a pet set to stay put still does not move a pixel, and the sparkle fan is derived rather than rolled, so the seeded PRNG stream the self-play parity test reads is untouched. The whoosh cadence from the 08-26 quiet fix is not touched either. Both pounce triggers stand down under --shot, so the beat was unreachable in a QA capture: --joy pins it open (documented next to --bfly and rearup).
A catch used to eject the bug the same frame the paws reached it, which read as a hit, not a catch. Now it is held: pinned between the reaching paws for the rest of the leap, then cupped in front of the chest for half a second with its wings barely moving while the cat peers down at it wide-eyed, and only then does it slip out and climb away with the victory bounce behind it. 'held' is a new bfMode. Every eviction still forces 'out' (visit end, mouse use, toggles, species swap), so the one place that clears bfOn is unchanged. A held bug sits 50px under the head, inside the swat's reach and the dodge radius, so both are guarded against it. Found on the way: the dodge trigger had no mode guard at all. A bug leaving after a catch passed under the head and was startled straight back into a dodge, so the old catch that "ended the visit" never actually did. It is guarded on 'out' now too.
…aying The swat moved two arms on a static body; the cat now rises on its haunches with the reaching paw (render-only, a few pixels, scaled by mood) and the whoosh takes the strength knob renderRearBat always accepted but nobody set. The cadence from the 08-26 quiet fix is untouched and the test from two commits ago proves it. The cat's tail takes an excitement argument, mirroring the mood argument the dog's tail already had: a quicker sway added on top of the resting wag while a butterfly is in play and for a few seconds after a catch. It is additive on purpose; changing the base rate with a decaying value jumps phase at large t and reads as a twitch. wagBoost now decays for cats instead of being zeroed every frame, which is what kept them locked out of it. Spotting the bug (bfMode in to wander) gets a small perk and the "!" glyph over the head for half a second, silent, drawn above whichever body is up. The --joy preview beat loops the way --state=done does, because a shot's --at counts from page load while the renderer clock started earlier, so no single --at lands on a known phase of a one-shot beat. The notice opens on the first frame, before the capture timer starts, so it is pinned by a test rather than a shot. Changelog and the features tour updated.
Work mode (and a Focus Guard busy signal) exist to stop unsolicited visits and to keep the pet from wandering off; they were also sending home the butterfly you had just asked for with "Send a butterfly", on the very next frame, which made the button a dead click for anyone who works with the mode on. The treat and the ball already outrank the park for the same reason (see giveTrekOn). A summoned visit is now exempt from that one eviction for its duration; the cat plays from its parked corner, and the next autonomous visit is gated exactly as before. Found live: the user's settings have work mode on, and the butterfly left the instant it arrived.
The pounce aimed the cat's FEET at the butterfly. The catch happens at the paws, POUNCE_REACH (a paw-length plus the leap's apex) above the feet, so the cat flew through the bug feet-first and the paws closed on air. Measured over 30 natural visits with the harness: 316 pounces, 0 catches. The whole catch-and-release beat was unreachable in practice. The feet now aim one paw-reach under the bug and meet it at the top of the leap. The bug is frozen for the whole hunt (the flight integrator is paused while hunting), so a correctly aimed leap always connects and a visit would end at the first pounce a couple of seconds after arrival, before the stalk and the swats. Two things keep it a game: no pounce in the first 7 seconds of a visit, and a 30 percent chance the bug flits aside as the cat coils, so the cat, already committed, whiffs and watches it go. After: 41 pounces in 30 visits, 30 catches, 73 percent of pounces connect, mean visit 15 s. A seeded test pins the shape (most visits end in a catch, some pounces still whiff, none before the embargo). The random roll on the dive path stays first in its condition so the per-frame Math.random() count is unchanged.
…t reach BF_POUNCE_AFTER_MS 7000 -> 4000: the bug is noticed at about 1.5 s and the first swat lands around 2-3 s, so 4 s leaves room for one before the first pounce can come. Median first pounce is now 5.1 s after arrival and the catch 5.9 s (was 7.3 s / 8.1 s). Measuring that exposed a second miss the jink did not explain: in every non-jink whiff the cat stood at x = 1812, the right edge of its play zone, leaping straight up under a bug at x 1846-1880. The hunt target is clamped to the play area, but the bug flies on screen-margin clamps of its own, so it hovers past an edge the cat may not cross. A cat resting in a corner met this on most visits. Two changes: the glider's wander zone now also stays inside the cat's reach, and bugPounceable() keeps both pounce triggers from committing to a leap whose clamped target cannot put the paws on the bug (it swats instead). Both checks sit after the random roll, so the per-frame Math.random() count is unchanged. Over 60 natural visits: 89 pounces, 60 catches, 67 percent connect, 0 whiffs that are not jinks (was 10 of 110). The work-mode test now treats a catch inside its window as the play working rather than an eviction.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Supersedes #41, which GitHub closed when #40's branch was deleted on merge (a closed PR cannot be retargeted). Same 8 commits, rebased onto
mainafter #40 landed; tests green on the rebased tree.What the cat does now
stalk -> swats that rise on the haunches -> butt-wiggle -> leap -> catch: a fan of pink sparkles and a heart at the paws, one pleased trill, the bug pinned between the reaching paws -> land, sit, peer down at it wide-eyed with a blush while its wings barely move (550 ms) -> it slips out and climbs away -> the cat does a quick double bounce after it with happy eyes and an excited tail. A whiff: land, look up after it, no sparkle, no sound.
Before this the catch was six white pixels and a heart, no sound, no face, and the cat snapped straight back to idle; a leap that missed looked exactly the same; the swat was two arms on a static body; the tail never changed.
Commits
fix(butterfly): a Focus Guard busy signal sends a flying butterfly home. The mid-visit eviction readconfig.workModewhile the spawn gate readworkModeOn(), so a meeting blocked new visits but left one already flying.test(butterfly): pins the 08-26 quiet fix. Nothing guardedbfSwatCool = t + 2600 + rand*1400,BF_SWAT_MS = 600or the 900 ms whoosh floor; the only whoosh test drives the scroll leaf with the butterfly off. A full visit with the bug parked in swat range now asserts one whoosh per swat, swats >= 2600 ms apart, whooshes >= 900 ms apart, and the constants by name.feat(butterfly): the catch is a victory beat and a whiff looks like one.feat(butterfly): hold the catch between the paws before it escapes (bfMode = 'held').feat(butterfly): rise into the swat, notice the bug, and wag while playing.fix(butterfly): the button outranks work mode, the way a treat does. Found live: with work mode on, "Send a butterfly" summoned a bug that left on the next frame, a dead click since July. A summoned visit is exempt from that one eviction for its duration; unsolicited visits are gated exactly as before (test covers both).fix(butterfly): aim the leap at the paws, and keep the catch a game. Measured with the harness over 30 natural visits: 316 pounces, 0 catches. The pounce aimed the feet at the bug; the catch happens at the paws, a paw-reach higher, so the cat flew through the bug feet-first. The feet now aim one paw-reach under the bug and meet it at the leap's apex. Since the bug is frozen during a hunt that would connect every time and end every visit at the first pounce, so: no pounce in the first 7 s of a visit (notice, stalk, a swat or two first) and a 30% jink where the bug flits aside as the cat coils. After: 41 pounces / 30 visits, 30 catches, 73% connect, mean visit 15 s. Seeded test pins the shape.fix(butterfly): a shorter warm-up (7 s -> 4 s; median first pounce 5.1 s, catch 5.9 s), and no leap at a bug the cat cannot reach. Measuring exposed a second miss the jink did not explain: the hunt target is clamped to the play area, the bug flies on screen-margin clamps of its own, so near an edge it hovered where the cat may not go and the leap went straight up under nothing (a cat resting in a corner met this most visits). The glider's zone now stays inside the cat's reach andbugPounceable()keeps both triggers from committing to a leap that cannot land. 60 visits: 89 pounces, 60 catches, 67% connect, 0 non-jink whiffs.Found on the way
outandheld.renderRearBatalways accepted astrengthfor the whoosh; no caller set it. It now scales with mood (calm 0.73, playful 0.85, zoomies 1.0).wagBoostwas zeroed for cats every frame, which is what kept the cat's tail locked out of the excitement the dog's already had.Sound budget, worst case per 22-30 s visit
playSwipe)playChirp) on a catchconfig.soundOn, and the catch is unrepeatable (heldguard)Nothing periodic was added. Sound off is fully silent.
Invariants kept
pos: a stay-put pet (roamOn: false) is asserted at exactly 0 px drift through a catch, the hold and the bounce.Math.random(): the sparkle fan is derived from its index, so the seeded streamtests/self-play.test.jsreads (dog ball and cat butterfly on the same frame) is untouched.bfModenames, the 5.5 speed cap, the grace window, spawn gates,startBflyVisit(t), the singlebfOn = falseexit,FORCED_STATE === 'rearup',batSpriteForgeometry: unchanged.previews/cat-poses.pngsha256f0e06b1c...identical before and after every commit (no composer changes).site/cat-live.jsuntouched.Tests
tests/butterfly-play.test.js(new, 8 tests): metronome regression + constant pins, catch (one trill with Sound on, none off, pink sparks, happy eyes that end), whiff (nothing fires, cat looks up), zero drift through catch/hold/release plus held cannot arm a swat or a dodge and the bounce amplitude curve, notice "!" for exactly its window, cat tail arity + 0..1 excitement +wagBoostdecay with the dog path unchanged, Focus Guard eviction. Suite: 226 tests, 0 failures. Lint clean.npm run test:bootpasses.Seeing it
npx electron . --shot --bfly --state=rearup --at=1021vs--at=817: stroke peak vs trough, body a few px higher at the peak.npx electron . --shot --joy --at=100(and 500, 900, 1300): the bounce loops every 2 s under the flag because--atcounts from page load while the renderer clock started earlier; one of those is mid-air with ^^ eyes and blush.