New Project: LastBall — the Machine Fails 8,191 Times, You Throw the Last Ball
There is a small tribe of Pokémon players who accept exactly one way of hunting shinies: real cartridge, real console. No emulators, no cheat codes, no save editing, no RNG manipulation — the odds table says 1/8192, so you draw the lottery 8,192 honest times. The most hardcore scene in this community is three or four GBAs laid out on a desk, both hands cycling A+B+Start+Select soft resets, eyeballing each intro animation for an off-color sprite, for a week straight.
I respect that stubbornness. I just don't want to own those hands. So I'm starting a new project called LastBall: build a machine that presses through those 8,191 failures for me.
The name is the entire ruleset. The machine soft-resets, walks the route, glances at the Pokémon's color as it comes out, and goes again if it's normal. The instant a round looks wrong, it stops all input, pushes that frozen frame to my phone, and waits — doing nothing. Running, catching, resetting past it: none of these are within its authority. In the words of a comment in the codebase: come throw the last ball. The lottery grind belongs to the machine; the last ball belongs to the human.
To serve this tribe, the machine itself has to survive the same scrutiny. So the boundary is drawn hard: it only does what human hands do — press physical buttons, look at a screen. The end state is a mechanical finger resting on a real console's buttons and a camera pointed at a real screen. Not one byte of the game is touched; the shiny is rolled entirely by the cartridge itself. An emulator will appear below, but only as a development rig: get the loop and the detector reliable in software first, then move onto hardware.
Roadmap and where it stands
Four steps, with the interfaces cut on day one: the loop engine only knows two abstractions, a "hand" (ButtonDriver) and an "eye" (FrameSource). It never learns whether a press lands in an emulator or on a real console, whether a frame comes from a screenshot or a camera. Swapping hardware means swapping implementations of those two interfaces; the loop doesn't change by a single line.
P0: get the loop running on a software rig (done). mGBA plus a Lua bridge plays both hand and eye, and the full loop runs against an Emerald starter (Torchic): soft reset, replay a button route to the Pokémon's send-out, grab a frame, compare the judge region's hue histogram against a baseline — Gen 3 shinies literally are an alternate palette, so color comparison is the right tool, not a proxy. The loop body contains zero LLM calls. The model does exactly one job, once: interactively feel out the "walk to send-out" button route in the emulator, then trim it over a few verified attempts down to the shortest reproducible version. Locked in, it replays tens of thousands of times at zero cost.
P1: the reliability layer (done). The goal is a machine you dare leave unattended for days. ntfy pushes reach my phone with the frozen frame attached as a PNG. Every round is audited, with a human review page. There's an RNG canary: Emerald boots with a constant seed of 0, so a robot with perfect timing would meet the same Pokémon forever — the scheduler deliberately sweeps the delay frame by frame with jitter, and the canary watches judge-frame hashes and alarms if repeats pile up. A shiny drill injects a fake shiny palette into the emulator's video memory to prove the FREEZE path actually fires. And an LLM supervisor backstops execution accidents — a stuck menu or a blank screen may be reviewed and retried, but a suspected shiny never passes through its hands; it goes straight to the human. The detector's whole stance fits in one sentence: false alarms are fine, misses are not. This phase also brought two rounds of performance work, cutting a round from ~49 seconds to ~26.
P2: swap the hand (not started). ButtonDriver moves from the mGBA bridge to a mechanical finger driven by a Raspberry Pi Pico, pressing a real console's physical buttons. The problems here are all about execution determinism: consistent press force and duration, a reliable serial link, and the loop noticing when a press silently didn't register.
P3: swap the eye (not started). FrameSource moves from emulator screenshots to a camera filming a real screen. The detector has to hold up under lighting changes, moiré, and perspective distortion — the baseline and thresholds will almost certainly need recalibrating against camera frames. Only when this step lands does the machine deserve the word "authentic."
The next step touches no hardware: run one complete hunt on the software rig — thousands of rounds — and let the long-running problems surface on their own. When it has drawn all eight thousand lottery tickets for me and that one wrong-looking screenshot pops up on my phone, I'll go throw the last ball.