A stat-based baseball simulator in the spirit of Micro League Baseball: real player stats drive probabilistic at-bat outcomes, rather than arcade-style reflexes or direct player control. v1 is a single exhibition game between the 2021 Atlanta Braves and 2021 Houston Astros, using each team's actual season stat lines.
npm install
npm run devOpen the printed localhost URL. Click Next Play to step one plate
appearance at a time, or Play Ball to simulate the full game instantly.
New Game resets and replays with fresh randomness.
npm testRuns the engine's Vitest suite, including a statistical check that simulates 20,000 plate appearances and confirms batters converge to within a few points of their real batting average.
src/engine/ is pure logic with no UI dependencies:
statConversion.jsturns each player's raw counting stats (PA, AB, H, 2B/3B/HR, BB, SO, HBP for batters; BF, H, BB, SO, HR for pitchers) into per-plate-appearance event rates.log5.jsblends a batter's and pitcher's rate for each event (walk, strikeout, homer, hit, etc.) against a league-average baseline, using the standard sabermetric log5 odds-ratio method. The baseline is derived from the two teams' own combined stats rather than an outside data source.atBat.jsrolls the blended probabilities to resolve one plate appearance into a walk, HBP, strikeout, homer, single/double/triple, or an out (further split into groundout/flyout/lineout by a league-typical batted-ball profile).baseRunning.jsapplies that outcome to the current base/out state: force plays, double plays, sacrifice flies, tag-ups, extra-base advances.fatigue.jsdegrades a pitcher's rates toward league average as their pitch count climbs past 75, so tiring starters get hit harder late.gameState.jsruns the 9-inning (+extras) game loop, rotates each team's fixed starter → setup → closer bullpen, and aggregates the batting and pitching box scores as the game plays out.
src/data/teams/ holds each team's 9-batter lineup plus starter/setup/closer,
entered by hand from Baseball-Reference's 2021 season totals. See
statConversion.js for the exact fields each player record needs if you want
to add another team.
src/components/Diamond.jsx renders a simple pixel-art-style SVG diamond:
grass, infield dirt, foul lines, bases, a pitcher and batter with an idle bob
animation, and a batted ball that flies toward a spot on the field sized by
hit type (short for a groundout, deep for a home run). Baserunners are
tracked by id and slide between bases as gameState.js's lastPlay snapshot
(the base state immediately before and after each plate appearance) changes.
The ball's landing spot is randomized for visual variety only — it isn't
driven by the engine's RNG and has no effect on the outcome.
This is Phase 1+3 of the build (see the original build prompt for the full roadmap). Not yet implemented: manager controls (pinch hitters, steals, bunts, mid-game pitching changes), a roster/team editor, and season/series/ franchise modes. The engine is structured to support all of these without a rewrite.