Linux headless: -years/-seed/-threads/-dump, and hand the auto-picked player nation back to the AI - #2320
Linux headless: -years/-seed/-threads/-dump, and hand the auto-picked player nation back to the AI#2320ruggsea wants to merge 2 commits into
Conversation
-years N (with -headless) runs N game years with no speed cap and exits; -seed N fixes the game seed; -threads N caps the worker threads (-threads 1 makes runs reproducible); -dump DIR writes monthly nations/prices/provinces CSVs. Nothing changes without the flags.
network::init picks the first non-player nation by rank for the local player and create_mp_player marks it is_player_controlled. Headless only cleared local_player_nation, so that nation (ENG in 1836 vanilla, RUS in GFM) kept the player flag: its armies never went on guard, assign_targets never found them, and the AI never declared a war for it.
|
You should probably discuss new features in the PA discord. While I might be amenable to additional logging behind a build flag, I am not a fan of fprintf calls directly in the logic of functions, even if they are behind an if. |
|
Have you benchmarked the new fused_sum_over_demographics ? It doesn't use multiple threads and the way it access memory makes me think it would be strictly slower |
4d5633b to
fe0f2e5
Compare
|
Sorry such a big PR was a mistake, I have now tried to reduce it to a minimal state |
I would make a separate PR for it but yes you are right it is slower on multiple threads, but my new versions is still optimal for single threaded runs therefore I would use it for headless runs that need to be deterministic |
|
probably best not to describe it as "fast" then, as it would probably confuse normal players |
| [&](uint8_t const* ptr_in, uint32_t length) { read_save_section(ptr_in, ptr_in + length, state); }); | ||
|
|
||
| state.game_seed = uint32_t(std::random_device()()); | ||
| state.game_seed = getenv("ALICE_SEED") ? uint32_t(strtoul(getenv("ALICE_SEED"), nullptr, 10)) : uint32_t(std::random_device()()); // -seed N sets ALICE_SEED |
There was a problem hiding this comment.
I like the idea of being able to specify a seed, but I think that it is probably a bad practice to pass that value around in a global variable, and definitely a bad idea to do it by setting an environment variable. Instead I suggest passing the seed value, if present, in a std::optional<uint32_t> through to the function creating the new scenario from scratch
Two small commits for running AI-only campaigns on Linux without a window.
-headless,-years Nruns N game years with no speed cap and exits, printing seconds per game year.-seed Nfixes the game seed,-threads Ncaps the worker threads (with-threads 1the same seed gives the same campaign), and-dump DIRwrites monthlynations.csv,prices.csvandprovinces.csv. The driver is a new header,headless_run.hpp; the only other changes are the flag parsing in entry_point_nix.cpp and reading the seed in serialization.cpp. Without these flags nothing changes.network::initstill picks the top-ranked nation for the local player and setsis_player_controlledon it. Headless then only resetslocal_player_nation, so that nation keeps the flag all game: its armies are never put under AI control and the AI never declares a war for it. That is ENG in 1836 vanilla and RUS in GFM. Over 100-year headless campaigns ENG declared 0 wars in 40 of 40, and GFM Russia lost its starting war against Dagestan in 109 of 109. With the fix ENG declares 15.8 wars per campaign (112 campaigns) and GFM Russia beats Circassia and Dagestan by 1832. Three lines in each entry point.Tested on Linux (clang, Release): 3-year vanilla run, 29 s on one core with
-threads 1; two runs with the same seed write byte-identical CSVs.