← Developer Journal
·9 min read·Terry Pugh

How the Pitch Engine Works

A look inside the pitch-by-pitch simulation: count leverage, pitch selection, contact resolution, and why we rebuilt it from at-bat outcomes.

Diamond Legend originally resolved at-bats as single events: take the hitter's ratings, take the pitcher's ratings, roll an outcome. It worked, it was fast, and it was subtly wrong in ways that got harder to ignore the longer you played.

What at-bat resolution gets wrong

When you resolve an entire plate appearance at once, several things simply cannot exist. Count leverage cannot exist — there is no 0-2 count for a hitter to protect against and no 3-1 count for him to hunt in. Pitch efficiency cannot exist, so a pitcher's stamina drains on a schedule rather than in response to how hard the inning actually was. And plate discipline collapses into a single modifier on walk rate, which drastically understates what a patient hitter does to an opposing starter.

That last one was the tipping point. Discipline should be a weapon that shortens outings and drags a bullpen into innings it was not built for. In at-bat resolution it was a small nudge to a number.

The pitch loop

The rebuilt engine simulates each pitch. Every pitch runs through the same sequence:

1. Determine the count state. Balls and strikes define who is ahead, which shapes everything after it. 2. Pitch selection. The pitcher chooses a location and an approach based on his control, his stuff, and the count. Ahead in the count he expands the zone; behind it he has to come closer to the middle. 3. Swing decision. The hitter's discipline determines whether he offers at a pitch outside the zone, and his approach shifts with the count — protecting with two strikes, hunting with three balls. 4. Contact resolution. If he swings, contact rating against the pitch's difficulty determines whether he misses, makes weak contact, or squares it up. 5. Batted-ball outcome. Squared-up contact gets an exit profile from power and the batted-ball type, then park factors and defense decide what actually happens. 6. State update. Count, pitch counts, fatigue, and baserunners all advance.

What emerged that we did not code directly

The parts worth the rewrite are the behaviors nobody wrote a rule for.

Patient lineups now genuinely drive starters out early. Not because there is a rule that says so, but because each additional pitch a hitter sees is a real pitch on a real pitch count. Face a disciplined team and you meet their middle relief in the sixth.

Control-heavy pitchers now go deeper than velocity-heavy ones with the same overall quality, because working ahead means fewer pitches per out. That relationship exists in real baseball and we never had to model it explicitly — it falls out of the loop.

Two-strike approach changes create real strikeout and foul-ball patterns, which makes recaps read like baseball instead of like a list of results.

What it cost

Speed. A pitch-level engine does far more work per game than an at-bat engine, and Diamond Legend simulates full seasons across a 30-team league with four affiliate levels. Most of the engineering time went into making the loop cheap: no allocation inside the hot path, precomputed probability tables, and a fast path for lopsided matchups where the extra fidelity would not change the distribution.

Sim Season on a full league is still the heaviest operation in the game, and it is the thing we keep optimizing.

What is next

Two things we know are incomplete. Pitch sequencing is currently count-aware but not memory-aware — a pitcher does not yet remember what he threw the last time he faced this hitter. And defensive positioning is still static within a game rather than shifting by hitter tendency.

Both are on the list. Both will change outcomes in small ways that add up across a season, which is exactly the kind of change that is worth being careful with.

Keep reading