Idea: a bot/agent API for Zero Hour, in the spirit of BWAPI
The short version
StarCraft: Brood War has had BWAPI since 2009 — a C++ framework that lets external programs observe game state and issue unit commands. It turned a 1998 game into the standard testbed for RTS AI research, and it is still generating activity in 2026: AIIDE and IEEE CoG competitions, the SSCAIT ladder, dozens of open-source bots, and a steady stream of academic papers. None of that was planned by the game's developers; it happened because somebody exposed a stable API.
Generals: Zero Hour is now in a better position than Brood War ever was — the actual source is here, it builds, it's GPLv3, and it's actively maintained. BWAPI had to do all of this by injecting into a closed binary and reverse-engineering memory offsets. We wouldn't have to.
I'd like to gauge whether there's appetite for a first-class agent API, and if so, agree on a shape before anyone writes code.
(Filing here rather than in Discussions > Ideas because it's as much an architecture question as a feature request — happy for a maintainer to move it if Ideas is the better home.)
Why this repo is unusually well suited to it
The hard part of a bot API is usually determinism and a headless mode. This project already has both, for its own reasons:
Core/GameEngine/Source/Common/ReplaySimulation.cpp — ReplaySimulation::simulateReplays() already runs replays without graphics, across worker processes, and returns non-zero on mismatch.
.github/workflows/reusable-check-replays.yml already runs that in CI against a corpus of replays.
- The command stream is already serialized and validated for network play and replays (
GameMessageParser, NetCommandMsg, NetCommandList).
So the expensive infrastructure — a deterministic, graphics-free simulation you can run many of in parallel — exists. A bot API is substantially "expose what's already there", not "build a new engine mode".
Proposed shape
The design goal is that a bot is indistinguishable from a human player at the engine boundary. That's what protects everything this project cares about.
1. Commands go through the existing GameMessage path.
Bot orders would be enqueued as the same GameMessages that mouse and keyboard input produce, dispatched via GameLogicDispatch. Consequences that matter here:
- Replays record bot games for free, in the existing format.
- Network determinism and CRC checking are unaffected — the engine cannot tell the difference, so there is no new mismatch surface.
- A bot cannot do anything a player couldn't do, which sidesteps a whole class of design arguments.
2. Observation is a read-only view, fog-respecting by default.
A versioned, read-only snapshot of what the controlled player can legitimately see: objects, positions, health, states, resources, upgrades, supply, map/terrain, visibility. BWAPI's equivalent lesson is worth copying exactly — its CompleteMapInformation flag is off by default, so bots see only what a player sees, and a tournament module can pin it. Same for a UserInput equivalent. Get the default right on day one and the "is this bot cheating" argument never has to happen.
3. Out-of-process bridge, not just an in-process DLL.
BWAPI started in-process only and later added a shared-memory client bridge, because in-process has two chronic problems: a crashing or hanging bot takes the game with it, and everyone is locked to one language and ABI. Starting with a bridge means bots can be written in Python, Rust, Java or anything else, and a misbehaving bot is a dead pipe rather than a crash report filed against this repo. Notably, the one BW bot that currently uses a neural network (Pluto) runs its inference engine as a separate 64-bit process talking to a 32-bit DLL over stdio pipes — precisely because in-process wasn't an option.
4. A headless + uncapped-speed mode for training and tournaments.
Mostly a matter of reusing the ReplaySimulation path with a live agent driving instead of a recorded command stream.
Suggested staging
Deliberately incremental, because per CONTRIBUTING.md this shouldn't land as one enormous pull request:
| Stage |
Scope |
Gameplay risk |
| 1 |
Read-only observation API + headless observation of existing replays |
None — no new commands exist |
| 2 |
Command emission via GameMessage, skirmish only, behind a build flag and an off-by-default command-line switch |
Low — no new code path in the engine |
| 3 |
Out-of-process bridge + a reference bot and one non-C++ binding |
None to the game |
| 4 |
Tournament/ladder harness, match runner, result reporting |
None to the game |
Stage 1 alone is already independently useful: a stable read-only observation API over replays gives the project replay analytics, balance telemetry and regression tooling, whether or not anyone ever writes a bot.
Open questions for maintainers
- Is this something the project wants at all, or is it out of scope? Worth knowing before any design work.
- Should it live in-tree, or as a separate repo under the org consuming a small set of hooks added here? I lean toward the second — it keeps this repo's diff tiny and lets the API iterate at its own pace.
- Zero Hour first, per the precedence rule in
CONTRIBUTING.md, with Generals to follow — is that right here, or is this one where sharing via Core/ from the start makes more sense?
- Is there any appetite for the observation API to also serve the existing skirmish
AIPlayer / AISkirmishPlayer, or should those stay entirely separate?
- Anything in the 1.04 compatibility rules that a bot API would trip over that isn't obvious from outside?
Offer to help
I'm willing to do the implementation work, starting with the smallest useful slice (Stage 1) so there's something concrete to react to rather than a wall of text.
On tooling, to be straightforward about it up front since AI_POLICY.md asks: I use AI-assisted development in my workflow. I've read the policy and I'm not proposing to point an agent at the codebase and open pull requests — that's explicitly out of bounds and I agree with the reasoning. Anything I submit would be disclosed per section 2, scoped small enough to actually review, and I'd be accountable for explaining every line of it in review, in my own words. If the project would rather this area stay entirely hand-written, say so and I'll respect that.
Happy to take this to Discord instead if that's a better venue for hashing out the design.
Idea: a bot/agent API for Zero Hour, in the spirit of BWAPI
The short version
StarCraft: Brood War has had BWAPI since 2009 — a C++ framework that lets external programs observe game state and issue unit commands. It turned a 1998 game into the standard testbed for RTS AI research, and it is still generating activity in 2026: AIIDE and IEEE CoG competitions, the SSCAIT ladder, dozens of open-source bots, and a steady stream of academic papers. None of that was planned by the game's developers; it happened because somebody exposed a stable API.
Generals: Zero Hour is now in a better position than Brood War ever was — the actual source is here, it builds, it's GPLv3, and it's actively maintained. BWAPI had to do all of this by injecting into a closed binary and reverse-engineering memory offsets. We wouldn't have to.
I'd like to gauge whether there's appetite for a first-class agent API, and if so, agree on a shape before anyone writes code.
(Filing here rather than in Discussions > Ideas because it's as much an architecture question as a feature request — happy for a maintainer to move it if Ideas is the better home.)
Why this repo is unusually well suited to it
The hard part of a bot API is usually determinism and a headless mode. This project already has both, for its own reasons:
Core/GameEngine/Source/Common/ReplaySimulation.cpp—ReplaySimulation::simulateReplays()already runs replays without graphics, across worker processes, and returns non-zero on mismatch..github/workflows/reusable-check-replays.ymlalready runs that in CI against a corpus of replays.GameMessageParser,NetCommandMsg,NetCommandList).So the expensive infrastructure — a deterministic, graphics-free simulation you can run many of in parallel — exists. A bot API is substantially "expose what's already there", not "build a new engine mode".
Proposed shape
The design goal is that a bot is indistinguishable from a human player at the engine boundary. That's what protects everything this project cares about.
1. Commands go through the existing
GameMessagepath.Bot orders would be enqueued as the same
GameMessages that mouse and keyboard input produce, dispatched viaGameLogicDispatch. Consequences that matter here:2. Observation is a read-only view, fog-respecting by default.
A versioned, read-only snapshot of what the controlled player can legitimately see: objects, positions, health, states, resources, upgrades, supply, map/terrain, visibility. BWAPI's equivalent lesson is worth copying exactly — its
CompleteMapInformationflag is off by default, so bots see only what a player sees, and a tournament module can pin it. Same for aUserInputequivalent. Get the default right on day one and the "is this bot cheating" argument never has to happen.3. Out-of-process bridge, not just an in-process DLL.
BWAPI started in-process only and later added a shared-memory client bridge, because in-process has two chronic problems: a crashing or hanging bot takes the game with it, and everyone is locked to one language and ABI. Starting with a bridge means bots can be written in Python, Rust, Java or anything else, and a misbehaving bot is a dead pipe rather than a crash report filed against this repo. Notably, the one BW bot that currently uses a neural network (Pluto) runs its inference engine as a separate 64-bit process talking to a 32-bit DLL over stdio pipes — precisely because in-process wasn't an option.
4. A headless + uncapped-speed mode for training and tournaments.
Mostly a matter of reusing the
ReplaySimulationpath with a live agent driving instead of a recorded command stream.Suggested staging
Deliberately incremental, because per
CONTRIBUTING.mdthis shouldn't land as one enormous pull request:GameMessage, skirmish only, behind a build flag and an off-by-default command-line switchStage 1 alone is already independently useful: a stable read-only observation API over replays gives the project replay analytics, balance telemetry and regression tooling, whether or not anyone ever writes a bot.
Open questions for maintainers
CONTRIBUTING.md, with Generals to follow — is that right here, or is this one where sharing viaCore/from the start makes more sense?AIPlayer/AISkirmishPlayer, or should those stay entirely separate?Offer to help
I'm willing to do the implementation work, starting with the smallest useful slice (Stage 1) so there's something concrete to react to rather than a wall of text.
On tooling, to be straightforward about it up front since
AI_POLICY.mdasks: I use AI-assisted development in my workflow. I've read the policy and I'm not proposing to point an agent at the codebase and open pull requests — that's explicitly out of bounds and I agree with the reasoning. Anything I submit would be disclosed per section 2, scoped small enough to actually review, and I'd be accountable for explaining every line of it in review, in my own words. If the project would rather this area stay entirely hand-written, say so and I'll respect that.Happy to take this to Discord instead if that's a better venue for hashing out the design.