Skip to content

Fix NaN when province admin cost is exactly zero - #2314

Merged
schombert merged 2 commits into
schombert:mainfrom
ruggsea:fix-zero-admin-cost-nan
Oct 3, 2026
Merged

schombert merged 2 commits into
schombert:mainfrom
ruggsea:fix-zero-admin-cost-nan

Conversation

@ruggsea

@ruggsea ruggsea commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

update_administrative_efficiency can turn a province's control_ratio into NaN, and that NaN later spreads to tax income, treasuries and market prices.

Cause. When control_ratio is below 0.01, the factor 1/(1.01 - control) - 1 in the admin cost is negative, so (pop * conc + area * 100) * factor + 100 can come out exactly 0. Then desire = prize / mass is inf, and control_scale * desire is 0 * inf = NaN.

Where we hit it. A headless AI-only campaign (seed 97). On 1869-10-13, province 593 (25,234 people, 3,322.6 km², control 0.00972) had mass == 0. The NaN reached the owner's treasury and every market, and the game aborted in 1871.

Fix. On a tick where mass is exactly 0, leave control unchanged. Every other case computes exactly the same values as before.

Testing. On our Linux headless build (single-threaded, fixed seeds):

  • The seed 97 campaign now runs the full 100 years with no NaN.
  • Three other seeds produce byte-identical output with and without the change over 100 years (monthly nation, province and price dumps, plus the war log).
  • This branch builds on current main.

For control_ratio < 0.01 the factor 1/(1.01 - control) - 1 in the admin cost is
negative, so (pop * conc + area * 100) * factor + 100 can land on exactly 0.
update_administrative_efficiency then computes desire = prize / 0 = inf and
control_scale * desire = 0 * inf = NaN, so control_ratio becomes NaN. The NaN
reaches tax income, the owner's treasury and then every market price, and the
game later aborts.

Seen in a headless AI-only campaign (seed 97): province 593 (25,234 people,
3,322.6 km2, control 0.00972) hit mass == 0 on 1869-10-13; the abort followed
in 1871. With this change control is left unchanged on such a day; every
other case computes exactly the same values (other seeds stay bit-identical
over 100 years).
@ineveraskedforthis

Copy link
Copy Markdown
Contributor

I would recommend adjusting calculation of admin cost instead to avoid negative numbers by definition.

…rol update

For control below 0.01 the factor 1/(1.01-control)-1 is negative, so the admin
cost could reach exactly 0 and produce inf/NaN. Clamping keeps the cost >= 100.
@ruggsea

ruggsea commented Oct 3, 2026

Copy link
Copy Markdown
Contributor Author

Done, thanks: the skip is gone and the factor is clamped with std::max/ve::max(0.f, ...) in both versions of the admin cost, so the cost is always >= 100.

Checked it on headless AI campaigns: the seed that originally went NaN (1869) and crashed (1871) now runs through 1872 with no NaN in prices or treasuries. 20 seeds x 100 years with vs without the change show no difference in world population, industrial score, treasury, prices, nations alive/civilized or wars started (all Mann-Whitney p > 0.06), and none of the 40 campaigns has a NaN.

@schombert
schombert merged commit c46fa50 into schombert:main Oct 3, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants