I've been doing a bunch of playtesting this week, because while this is only a game for me, I spent the first few weeks more interested in building things out than actually playing. Now that I've gotten a chance to run through a bunch of seasons and looking more under the hood, I've noticed things that I wanted to address.
Some of these changes were simply cruft. I started working on this as a random number generator that could make plausible box scores. Before I knew it, I had a very good tool with a bad GUI and decided to upgrade from Streamlit to NiceGUI, mostly because I didn't want to build another React tool. This has come with some complications, but has mostly worked.
FastAPI connects the Python app and is wired to the GUI so it can display information visually. The /stats/ site for Viperball is where my ugly GUI gets hidden and lets me present information publicly if I were for some reason ever interested in sharing out what's happening in a particular Viperball save.
The past week brought four major systems overhauls to the game engine. Here's what shipped:
1. Challenge System Rework: No Perfect Information
Coaches no longer cheat. Previously they had two separate code paths: _coach_challenge() that knew calls were blown, and _coach_wastes_challenge() that knew they were correct. Backwards.
Now coaches evaluate every play through a single decision framework without access to the truth:
Decision factors: Ref crew reputation (scouting the officials), coach judgment quality (clock_management), game situation (close games → more challenges), and play type
Challengeable plays: Spot of the ball, catch/no-catch, fumble/INT rulings, scoring plays, turnover on downs
NOT challengeable: Penalties (judgment calls, matching NFL), plays inside the 3-minute warning
Success rates scale with coach quality: 25% success (McDaniel tier) → 42% (NFL average) → 60% (Daboll tier)
Results from 100-game test: 1.58 challenges per game, 43.7% success rate, 80% of games with at least one challenge.
UI: Box scores now show clickable referee names with blown-call summaries. New /college/{id}/referees listing page and individual referee profiles (career stats, season breakdowns, full game logs, blown-call breakdown).
2. Penalty Catalog Expansion: 42 → 146 Entries
The penalty system now covers 99 unique infractions across 6 phases:
New Kickpass Phase (24 penalties):
Contact on Kickpass Receiver, Roughing the Kickpasser, Hit on Defenseless Kickpass Receiver, Kickpass Interference (15 yards, auto first down)
Offensive Kickpass Interference, Illegal Kickpass Formation, Illegal Double Kickpass (loss of down)
Viperball-Specific Physicality (defending the unique lateral-heavy game):
Roughing the Zeroback (QB-equivalent protection)
Contact Before Lateral Arrives / Excessive Contact on Lateral Receiver (defenseless receiver protection)
Illegal Viper Substitution / Missing Viper Jersey
NFHS Backfill (standard football penalties previously missing):
Striking, Piling On, Assisting the Runner, Blocking Below the Waist, Hurdling, Hands to the Face, Late Hit Out of Bounds, Fighting, Removal of Helmet, Spiking the Ball, Disconcerting Signals, Illegal Shift, Illegal Numbering, Illegal Participation
Plus: "Too Many Men on Field" now callable during live play (not just pre-snap) at realistic rates.
All probabilities conservative (0.001-0.005) to maintain the ~12-14 penalties per game target matching real football.
3. KenPom Efficiency Metrics Live on Standings Page
The backend was computing these metrics, but they were never wired into the UI until now. Standings page now displays a "KenPom Efficiency Metrics" section with color-coded stat thresholds:
| Metric | Definition |
|--------|-----------|
| Raw O | Points per 10 drives |
| Raw D | Opponent points per 10 drives |
| EK% | Value-weighted kick success |
| TO% | Turnovers per drive |
| TOD% | Forced turnovers per opponent drive |
| LR% | Lateral recovery rate |
| FDR | Penalty first downs per play |
| RLE | Rush yards per carry |
| KP% | Kick pass completion rate |
| Tempo | Plays per game |
Full glossary at docs/kenpom_glossary.md.
4. Injury System Expanded & Deployed
Injury catalog grown from 83 to 190 entries with realistic metadata:
| Field | Purpose |
|-------|---------|
| freq | Injury weighting (common injuries appear more) |
| reinjury | Risk of re-injury to same body part |
| nagging | Can flare up after returning to play |
| surgery | Surgery requirement (extends timeline) |
| inf_run / inf_kick / inf_lateral | Attribute impact on each skill type |
| min_weeks / max_weeks | Override tier default timelines |
Profile shift: Away from hard-hit football injuries toward soccer/basketball (more soft tissue, elevated concussions for a no-helmet sport).
UI: Injury report now filters by conference + team, shows nagging/surgery metrics, displays re-injury risk and attribute impacts, includes "Injuries by Body Part" histogram and "Nagging / Re-injury Watch" section.
5. Talent Generation Rebalanced (Deployed Earlier)
Still worth mentioning since it's live — the system was generating too many 90+ OVR players, causing 100+ point games.
Changes deployed:
Stat centers dropped ~30 points (blue_blood 95→64, national_power 89→52, etc.)
Per-stat ceiling capped at 96 (development system creates 97-99 players)
Stat budget system prevents "all 99s" (total stats capped at
center × 11 + 44)Recruiting ranges dropped 30 (5-star: 83-98 → 49-63)
Transfer portal ranges dropped 30
OVR floor lowered from 40 to 10
Result: A blue blood freshman arrives at 52-76 OVR. Reaching 90+ requires 2-4 seasons of development. Upsets become possible. Coaching and scheme matter again.
Bonus: Sprite Work
111 female coaches, 85 male coaches, 21 referees. I'd been slowing introducing these faces into the game, but did a big overhaul and added more just for my own immersion, not really any other reason.
More things will be inevitable. It's a lot of fun to build this out, including adding more graphics -- 16-bit style static images -- as it keeps things interesting for me. Much like real sports design, playing this out and building it real time has felt closer to building a sport than just building a game.
I've spent years working on beta teams for my favorite text sims, and i've always actually shipping real products, but I have it easy here since I'm building for an audience of one.
Eventually, I'll share screenshots and I'll put thing down, but as a long-time text simer player and one-time sport designer, getting to meld them together has been a treat.