Add Capture the Flag game#1544
Open
kvr06-ai wants to merge 1 commit into
Open
Conversation
Simultaneous-move adversarial gridworld where two players race to grab the opponent's flag and return it to their own base. A carrier in the defender's home territory is vulnerable to being tagged, which returns the flag and respawns the carrier. Partially addresses google-deepmind#843.
Collaborator
|
Thanks! |
Collaborator
|
I'm overloaded at the moment. For fairness, I'll be doing the PRs in order they were received, so this might sit for a few weeks, just a heads up. |
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.
Summary
Adds Capture-the-Flag, a simultaneous-move adversarial gridworld game requested in the Call for New Games umbrella. Two players occupy opposite ends of a symmetric grid and race to grab the opponent's flag and return it to their own base. A flag carrier in the defender's home territory is vulnerable: if the carrier ends a step Manhattan-adjacent to the defender while inside the defender's home, the carrier is tagged, the flag returns to its home base, and the carrier respawns at their own base. Empty-handed players cannot tag each other.
Partially addresses #843. Picks up from the Capture-the-Flag thread (Feb 2026, @lanctot's Feb 9 comment with detailed implementation guidance) after the original volunteer went inactive.
Modeling
Dynamics::kSimultaneous,ChanceMode::kExplicitStochastic,Information::kPerfectInformationUtility::kZeroSum(default) orkGeneralSumviazero_sumparameterRewardModel::kTerminal: returns+1to the winner and-1to the loser in zero-sum mode (or+1to the winner and0to the loser in general-sum); both0on drawGame parameters
horizon1000-1disables the horizon so the game ends only onscore_limitzero_sumtruetrue, utility is zero-sumscore_limit1grid.empty,*obstacle,aPlayer A's base (spawn + flag),bPlayer B's base. Exactly oneaand onebDefault grid:
Home territory split: A owns cols 0–2, B owns cols 4–6, col 3 is neutral (with odd-width grids). Even-width grids split exactly in half with no neutral column.
Tradeoffs
I picked a 1v1 design rather than a multi-agent team variant (2v2) for the first cut, on the strength of the
even if it's a pretty basic version would be greatlicense in @lanctot's Feb 9 comment. The two-player setup captures the core CTF dynamics (asymmetric flag-grab-and-return + territory-aware tagging) with the smallest action space. A future PR can add ateam_sizeparameter to extend to team play; the currentGrid/ResolveMove/ResolveTagscode paths only assume 2 players in the move-resolution ordering, so the extension would be additive.The other notable design choice: tag resolution fires after both players have moved, including the pickup step. This means a defender camping next to their flag can deny pickup, which matches standard CTF rules and creates the intended tension where the defender must vacate to attack.
Files
capture_the_flag.hcapture_the_flag.cccapture_the_flag_test.ccCMakeLists.txtpyspiel_test.pydocs/games.mdplaythroughs/capture_the_flag.txthorizon=20to match the markov_soccer / laser_tag conventions)Testing
C++ test executable (
capture_the_flag_test) covers:BasicCaptureTheFlagTests—LoadGameTest,ChanceOutcomesTest,RandomSimTest(100)RandomSimGeneralSumTest— random sim withzero_sum=falseRandomSimHigherScoreLimitTest— random sim withscore_limit=3CarrierCapturesFlagTest— orchestrate full pickup + return + score; assert returns +1 / −1CarrierTaggedInDefenderTerritoryTest— orchestrate pickup, then defender intercepts in defender's home; assert flag back at home and carrier respawnedNoTagInCarrierHomeTerritoryTest— assert defender adjacent to carrier in carrier's home territory does NOT tagBlockingCollisionTest— assert that attempting to enter the opponent's cell is a no-opScoreLimitTerminationTest—score_limit=2; game continues after first capture, terminates after secondHorizonDrawTest—horizon=5; no scoring; assert draw with0, 0returnsObservationTensorShapeTest— verify the 5-plane tensor encodes positions correctlyGridWithObstaclesTest— random sim on a 5×5 grid with obstaclesIntegration:
playthrough_test.py—test_playthrough_capture_the_flag.txtpasses (replays the recorded action sequence)pyspiel_test.py—capture_the_flagis in the expected mandatory games listAll tests pass locally on macOS (Apple Silicon, clang++ 17, Python 3.12). I also ran the surrounding sim-move and game-addition tests as a regression check (
laser_tag_test,markov_soccer_test,coop_box_pushing_test,chinese_checkers_test,banqi_test,catch_test,bridge_test) — all pass.References
cc @lanctot