Robot Gridiron

Devlog · August 12, 2026

60 commits

A lot to cover here

Update

Last entry ended with me staring at a stat line that said zero drops and thousands of passes defended, and saying I was going to recalibrate the passing game. Two weeks later I have actual numbers, so here they are.

Passing yards per team per game went from 946 to 181. Real football is around 230. Yards per attempt is 7.65 against a real-world 7.0. Touchdowns per attempt went from 17.5% to 6.5%, where real is about 4.5%. Interceptions exist now. For the first time the passing game is in the neighborhood of the sport it’s imitating.

That took longer to explain than to fix, which is usually a sign you learned something.

Build the instrument first

Before I changed a single number I wrote a measuring tool. It runs the real playbook through the real resolver at several roster levels and reports what the box score physically cannot tell you: how much separation the receiver actually had at the moment of the throw, the air-versus-after-catch split, which read the QB was on, how long until first contact, and how many completions were never touched by a defender at all.

That last one was the tell. You can’t fix a system you can only see the totals of. Every hour I spent on the instrument paid for itself twice.

A scale with no floor is a switch

The passing bug turned out to be three bugs. Then all three turned out to be the same bug wearing different clothes.

Each one was a term scaled by a robot stat, written to make a thing more true as the stat climbs. Prefer running straight. Separation should reduce interceptions. A smarter defender takes better pursuit angles. All sensible, all correct-looking in the source.

And all three multiplied out to zero across the entire range my league actually occupies. The average CPU stat in a genesis league is about 4 out of 100. At 4, those terms weren’t scaling anything down a little. They were off. Hole-finding was mathematically impossible. Interceptions were impossible on 84% of throws. No defender ever took an intercepting angle, ever, in any game that had been played.

I’d written three switches and labeled them dials. If a term scales with a stat, it needs a floor, or the low end of the range silently deletes the feature. I’m now suspicious of every stat-scaled multiplier in the codebase, which is probably the correct amount of suspicious.

The arm actually matters

A related thing that had been quietly wrong: the sim decided every catch at the moment of release, using separation at that instant, and the ball had no air time at all. A genesis QB with the arm strength of a wet noodle completed a 60-yard bomb at the same rate as a cannon.

The throw is now a real ballistic launch, same physics family as the punts and field goals. Arm strength sets launch speed, the maximum distance falls out of the physics instead of being a number I picked, and the ball spends real seconds in the air. Ten yards is about a third of a second; forty yards is over a second. During that flight the sim keeps running. The receiver breaks off his route and plays the ball. Defenders react to it. Rushers keep rushing.

The catch resolves when the ball arrives, not when it leaves. Which means you can now throw into a window that was open at release and shut by the time it got there. That’s football.

The defense has to figure out what you’re doing

The other big one. Defenders used to know the play instantly. Now every defender executes his authored assignment, zone dropping to his landmark, man carrying his receiver, spy shadowing, until he diagnoses run or pass, and how fast that happens is a pure function of his CPU stat. Roughly 0.79 seconds for a genesis robot, 0.21 for an elite one. No dice rolls anywhere in it.

The gap between those two numbers is enormous in practice. A genesis cover-3 bleeds 8.68 yards per carry on an outside stretch play. The same defense with elite CPU gives up 0.65.

Which finally makes CPU a stat you can feel buying, and it opens the door to deception. Play action makes the early evidence lie, and a fooled defender un-fools at his own CPU’s pace, so a slow defense stays fooled measurably longer. The draw is the inverse: a run dressed as a pass, worth about two thirds of a yard against a slow defense and negative against a sharp one. It’s an explicit bet on the other guy being dumb.

Game film

You can now watch any completed game in your ladder, play by play, with next and previous controls and 1x/2x/4x speed.

The architecture call I’m proudest of: store the film, never re-simulate it. The sim changes weekly. A replay that re-runs the current sim against an old game would produce a “replay” that disagrees with the recorded box score, which is a lie that would surface within about a day of anyone using it. So every play is captured as it happens, in exactly the wire format a live client already renders. The stored timeline is what actually happened, and it stays true no matter how much the engine changes underneath it.

It costs about a megabyte per game. Worth it.

Current Dev

Season 2 measurement turned up the next thing. Sacks fell from 5.94 per game to 2.50, where real is about 5, and scrambles are up to about six a game averaging negative yardage. Pass protection got sticky enough to suppress clean sacks, so pressured quarterbacks now tuck and run and get dropped for losses that book as rushes instead of sacks. The pressure is still there. It’s just landing in the wrong column.

So that’s next, along with finishing the directional blocking work that this is really a symptom of.

More next time.