ai-battle: support team assignment (--teams) and establish team start-pacts - #1957
Open
FlexApex wants to merge 11 commits into
Open
ai-battle: support team assignment (--teams) and establish team start-pacts#1957FlexApex wants to merge 11 commits into
FlexApex wants to merge 11 commits into
Conversation
…t to file-only in headless mode
Adds a --teams option (e.g. "0,1;2,3" for a 2v2) that assigns players to teams in the headless harness, so the AI can be verified under real team rules instead of a free-for-all. Player indices map to Team1, Team2, ... in the order the groups are given; unlisted players stay teamless. The assignment is threaded through HeadlessGame into GeneratePlayerInfo, which sets PlayerInfo::team. Establishing the actual alliances follows in the next commit.
…ient GameClient::StartGame calls GamePlayer::MakeStartPacts() for every player on a fresh map, creating the ally + non-aggression pacts that bind a team together. HeadlessGame never did this, so in a recorded ai-battle with teams the teammates had no pacts and GamePlayer::IsAttackable() returned true for them: the AIs attacked their own team. On replay GameClient *does* set up the pacts, so the recorded inter-team attack commands hit allied players and were resolved differently, producing an object-count divergence (and replay desync) without any RANDOM divergence. MakeStartPacts() early-returns for Team::None, so this is a no-op until players are actually assigned to teams via --teams.
FlexApex
force-pushed
the
ai-battle-teams
branch
from
June 28, 2026 11:04
789dd1f to
2841770
Compare
Flamefire
reviewed
Jul 12, 2026
|
|
||
| ggs.objective = GameObjective::TotalDomination; | ||
| HeadlessGame game(ggs, mapPath, ais); | ||
| const auto wares = options["wares"].as<std::string>(); |
Member
There was a problem hiding this comment.
Any specific reason to use this construct here but have lua/settings_path "auto-filled"?
Author
There was a problem hiding this comment.
No deliberate reason beyond following the file's two pre-existing conventions: the always-present, default-valued options are read inline with options[...].as<>() (this is how objective was already handled), while the genuinely-optional file paths are bound to optionalstd::string via po::value(&var) so their presence can be checked with if(var) (this is how replay/save were already handled). I kept wares like objective, and lua/settings like replay/save.
Flamefire
reviewed
Jul 12, 2026
…skipping Silently skipping unparseable or unknown [addons] entries hid exactly the mistakes the settings file is meant to catch (a mistyped key/value or an accidental section name went unnoticed while the user's intent was not applied). Make loadAddonsFromIni strict: - an entry that doesn't parse -> hard error naming the entry, - an unknown/unsupported addon id (getAddon(id) == null) -> hard error, - a missing [addons] section -> hard error, except for a fully empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ai-battle: support team assignment (
--teams) and establish team start-pactsBuilds on #1949 (uses its
HeadlessGame). Two commits:1.
--teamsoption for team assignmentAdds a
--teamsoption to the headless ai-battle harness so the AI can beverified under real team rules instead of a free-for-all:
Groups are separated by
;, player indices by,. The first group becomesTeam1, the secondTeam2, and so on; unlisted players stay teamless. Theassignment is threaded through
HeadlessGameintoGeneratePlayerInfo, whichsets
PlayerInfo::team.2. Establish team start-pacts in
HeadlessGameto matchGameClient(avoid desyncs in replays)GameClient::StartGamecallsGamePlayer::MakeStartPacts()for every player ona fresh map, creating the ally + non-aggression pacts that bind a team together.
HeadlessGamenever did this, so a recorded ai-battle with teams had no pacts:GamePlayer::IsAttackable()returnedtruefor teammates and the AIs attackedtheir own team. On replay
GameClientdoes set up the pacts, so the recordedinter-team attack commands hit allied players and were resolved differently —
producing an object-count divergence (replay desync) without any RANDOM
divergence.
MakeStartPacts()early-returns forTeam::None, so the call is a no-op untilplayers are actually assigned to teams via
--teams. Together with theMapLoader::SetupResources()call already inHeadlessGame, this makes theheadless world setup match the
GameClientreplay path.Note