Every formula the DLL runs, every number we measured while building it, and every assumption we made along the way. Written so a curious player can follow it and a sceptical one can check it.
The game runs CPU player development once a year, at Training Results. AutoProgress hooks three points in that pass and nothing else: which skill group a CPU player buys with his points, whether a development trait can fall as well as rise, and how far each player's skill ceilings sit from the players like him, with his program's quality and his own production on the field weighing on that last one. The user's own progression screen, recruiting, and the season itself are untouched.
| Change | Vanilla behaviour | With AutoProgress | Measured effect |
|---|---|---|---|
| Skill-point spending | A random affordable skill group | Groups weighted by overall gained per point, discounted by room left | About +2.3 overall per CPU player per offseason: 4.6 gained from his points against 2.3 with the game’s random pick and 4.8 for a perfect picker (section 2) |
| Development trait | Can only rise | Can fall after a poor season, by the game's own season score | About 50 falls a season against 100 to 150 rises |
| Skill ceilings | Fixed when the player is generated | Re-rolled yearly around the average of players like him; 8% bust; cuts fund breakouts | Players at 90+ doubled and players under 65 doubled after three seasons, league average up half a point |
| Program quality | No effect on development | Coach, facilities and upgrades nudge ceiling targets up to ±10 levels within a trait; producers are shielded from the downside | Top-quarter programs grow 1.3 overall more than bottom-quarter ones over three seasons |
At Training Results the game gives each CPU player a number of skill points and spends them one purchase at a time. For each purchase it lists the skill groups he can still afford and can still level, then picks one at random. Buying a level in a group raises every skill in that group by 2, up to the group's ceiling, so which group he buys decides how much his overall rating moves.
The DLL replaces the random pick with a weighted one. Each candidate group gets a weight from how much overall it buys per point, and the pick is drawn in proportion to those weights. The game's own random number is still consumed, so the rest of the game's random sequence is exactly as in vanilla.
The value tables come from two views of a player: what raises his overall rating, and what matters on the field for his position. The shipped table blends them 75/25 toward overall.
Value tables regenerated 20 September 2026, re-measured 21 September. The overall formulas the tables are built from were replaced by a fit that is 99.97% exact against the game’s own overall rating over 11,730 rostered players (the earlier fit was 51.5% exact and overfit for 19 archetypes). For 20 of the 60 archetypes that changed which skill group is worth most. One Training Results was then run on the new tables from the 2028 signing-day save (7,771 players scored with the same tool as the chart above): the hook’s picks bought 4.61 overall per player from the offseason’s points, against 2.34 for the game’s random pick on the same save and 4.82 for a perfect picker, so 96% of the optimum, and the share of points wasted on groups worth under 0.15 overall per level fell from 14% on the old tables to 10%. The chart above still shows the old-table simulation; its shape holds, its bars sit a little low.
progression-efficiency.js). The overall table was also measured in game at 4.12, within 3% of its simulated 4.00.Better picks mean faster growth. Measured one offseason from the same save, the whole player table grew +1.20 overall per player in vanilla and +2.40 with the hook. That is what we call human pace: CPU teams developing the way a person who reads the skill trees would. Two companion mods scale the game's offseason experience table down so the CPU keeps its smarter picks but grows closer to vanilla speed.
A player's development trait (Normal, Impact, Star, Elite) sets how many points he gets. The game rolls a rise once a year from his season score, a 0 to 100 figure it computes from his stats for his position. The chance is the score minus a threshold for his current tier, scaled by a coach bonus:
Nothing in the game ever lowers a trait. The DLL adds the mirror image, using the game's own season score, random number generator and field setter:
The gates came from the first run: 286 falls, of which 247 were players with a season score of 0, who had not played, not played badly. With the gates the validated run gave 109 rises and 47 falls; across a three-season line the counts were 97/54, 132/45 and 143/57.
With smarter spending, players outgrow their traits. Following each tier through a season (who left, who arrived, who was promoted or demoted) showed the Star pool needs about 57 promotions a season to hold its size and was getting 16; Elite needs about 10 and was getting 1. The thresholds are data in the game's expression file, so we lowered them and measured, one Training Results from the same save each time:
Before the rise tuning and the spread existed, the smart-spending hook and the fall roll ran for three seasons from the same 2028 save, against a vanilla line from the same save. These counts are the whole player table, recruits included, so they are larger than the rostered-only figures later on.
Every player has six skill ceilings, one per skill group, 0 to 20 each, 120 in total. A ceiling is a hard stop: once a group reaches it, no more levels can be bought there. The game fixes them when a player is generated, and they track his recruiting stars closely, so a five-star almost never busts and a two-star almost never breaks out. The spread re-rolls them once a year so that surprises in both directions happen at realistic rates, without moving any group's average.
This is the Dynasty Development app's roster pass, ported line for line into the DLL and checked against the app player by player (section 8). It runs league-wide at the first development-trait roll of Training Results, which sits before any team spends a point. Here is the whole computation for one player, in order.
raw. The talent shield blunts the downside for players who have already produced: a player's production rank within his position (section 6a) sets a shield strength of (rank − 0.6) / 0.4, clamped to 0..1, and a negative bias is multiplied by 1 − 0.9 × strength; players without a season are shielded by how far their ceiling already sits above their anchor instead. The trait's mean bias is then restored on the unshielded, so the bias moves players relative to each other and never a trait as a whole.SHA-1("bust|seed|key")) below 0.08 marks him a bust: his ceiling becomes the lower of what the stretch gave him and his overall minus 25, so he stops growing near where he is. Same odds at every star rating. It exists because a five-star's 112-of-120 ceiling never binds otherwise.Port first, game second. The C port was run on the same inputs as the app's own pass and agreed on every one of 7,528 rostered players and 5,058 recruits, ceilings and skill points alike. In the game, two runs on the 2028 save and a second season on top verified the reads (every dumped value equal to the save), the writes (every ceiling in the post-run save equal to what the DLL wrote, apart from the game's own raises) and the store.
| Rostered players, one step | no spread | spread |
|---|---|---|
| mean overall after | 75.76 | 76.17 |
| growth per player, mean / spread (sd) | 4.32 / 3.21 | 4.72 / 3.95 |
| gained 8 or more | 1,257 | 1,764 |
| gained nothing or lost | 1,127 | 1,467 |
| busts / fuelled players / fuel budget | – | 538 / 1,734 / 34,680 |
| fuelled players' growth vs the rest | – | +8.59 vs +3.71 |
| Rostered players, after season two | human-pace line, no spread | spread on |
|---|---|---|
| mean overall | 76.38 | 76.63 |
| players at 90+ | 100 | 180 |
| players under 65 | 150 | 219 |
| Star / Elite players | 324 / 88 | 345 / 98 |
| mean ceiling total | 85.3 | 81.4 |
The league average drifts up about a fifth of a point a season at the shipped fuel rate of 0.4 points per level cut. That is a choice, not an accident: 0.2 would hold the average flat at the cost of fewer breakouts, and it is one line in the ini.
The shield needs to know who actually played. The DLL reads it the way the app does: each player's SeasonStats array points at his rows in the four season stat tables (offence, defence, offensive line, kicking); the counting stats are weighted in the spirit of fantasy scoring (passing yards 0.04, touchdowns 4 to 6, tackles 1, sacks 4, interceptions 6, drops and fumbles negative, and so on), divided by games played, and ranked within his position. Linemen barely register in the individual tables, so their score is the unit's output from the team's season stats (yards per carry, red-zone conversion, two-point tries, penalties and fumbles lost per game) plus their own sacks allowed and pancakes per hundred snaps, measured against the league median and shrunk toward neutral for players with few snaps. Every player row with a stat array is ranked, graduated seniors included, because the app ranks against everyone. On the 2030 save this ranked 8,727 players from 19,535 stat rows in 15 ms, identical to the app for every one of the 4,867 ranked players in that pass.
With every term verified, the full pass (ceilings, busts, fuel, program bias with the production shield) ran three seasons from the same 2028 save. These are measured on the cohort present in the start and every line's save, matched by name, birthdate and position, so recruiting classes that differ between lines do not enter. Growth is a player's overall now minus his overall at the start; Q1..Q4 split the cohort by the program score of the team he was on at the start.
| After two seasons, cohort of 4,834 | no spread | spread, bias on |
|---|---|---|
| mean overall | 78.70 | 79.01 |
| players at 90+ / under 65 | 139 / 58 | 269 / 95 |
| Impact / Star / Elite | 1,071 / 265 / 70 | 1,116 / 264 / 61 |
| growth, mean / spread (sd) | 7.85 / 4.31 | 8.16 / 5.44 |
| gained 8 or more / gained nothing or lost | 2,547 / 316 | 2,588 / 522 |
| growth at Q1 (worst programs) … Q4 (best) | 7.85 … 7.48 | 7.50 … 8.26 |
| program effect, Q4 minus Q1 | −0.37 | +0.77 |
| After three seasons, cohort of 3,044 | no spread | spread, bias on |
|---|---|---|
| mean overall | 80.49 | 80.97 |
| players at 90+ / under 65 | 162 / 15 | 296 / 35 |
| Impact / Star / Elite | 750 / 192 / 36 | 786 / 183 / 27 |
| growth, mean / spread (sd) | 10.49 / 4.56 | 10.96 / 6.06 |
| gained 8 or more / gained nothing or lost | 2,290 / 64 | 2,160 / 150 |
| growth at Q1 … Q4 | 10.40 … 10.32 | 10.10 … 11.38 |
| program effect, Q4 minus Q1 | −0.09 | +1.28 |
Both lines were saved at the same point of the season (the first day of the new year) and both ran on the same value tables, the corrected ones, after the no-spread line was re-run on them on 21 September 2026. Two things to read from that. The bias creates the effect it exists for, and it compounds: a player at a top-quarter program grows about 0.8 more than one at a bottom-quarter program after two seasons and 1.3 after three, where without it program quality made no difference (a line with the spread but the bias off, run earlier on the old tables, gave −0.40 after two seasons). And the spread’s own signature holds up over time: nearly twice the players at 90 or better, twice the sub-65 players, a wider spread of individual growth, and a league average about half a point higher, which is the fuel drift. The corrected tables themselves were worth about 0.1 of growth over two seasons on the no-spread line (7.66 against 7.56 at the same after-training-results stage), so the earlier caveat that they might explain the mean gap is settled: they do not.
Each entry says what was assumed, why, and what it costs if wrong. Items marked verified were checked in game.
coach_gate, which sets the spending exponent from each team's program rank, and xp_gate, which scales each team's offseason skill-point award by the same rank. Both are built and self-tested against fake records, and the XP site is verified byte by byte at startup, but neither has been run for a season in a game. Every number in this book was measured with both at 0.Matched runs. Every in-game comparison starts from the same checkpoint save, advances through one Training Results with one thing changed, and compares the saved results player by player, matching players by name and age rather than by id, because the game hands graduates' ids to new freshmen.
Exactness before plumbing. The spread's maths was validated offline before it ever wrote a ceiling in game: the app's own pass was run for real on a scratch copy of a save, the same inputs were fed to the C port, and every row had to agree, ceilings and skill points included. Every summation runs in table order and every formula keeps the app's operand order, so even the unrounded totals agree. A 628-player fixture is re-checked on every build.
Plumbing after. The DLL dumps every input it read and every value it wrote. A checker compares the inputs with the save the run started from (reads and row alignment), re-runs the port on the dumped inputs (maths), and compares the post-run save's ceilings with what was written (writes), and reports the growth of fuelled players against the rest.
Self-tests. The chooser, the fall roll, the coach-gating maths, the XP scaling and the spread each have a self-test the build script runs, against fake records shaped like the game's. A fourth check, check-signatures, re-verifies all three hook sites against a memory dump of any new game build.
Re-measured after the value-table fix. The spending figures were re-measured on the regenerated tables on 21 September 2026 (section 2), and the no-spread comparison line for the tables in section 6 was re-run on the same tables so that every line in them shares one set.
The same standard for the in-game reads. The program scores and production ranks are logged and dumped by the DLL at the roll; a checker runs the app's own functions on a checkpoint of the save taken before that Training Results and compares every team and every player. Both had to come back identical to one part in a billion before the bias was switched on, and the three-season line above ran only after they did.