Skip to content

Add Parameterized N-Player Social Dilemma game - #1571

Open
jaisinha77777 wants to merge 4 commits into
google-deepmind:masterfrom
jaisinha77777:param-social-dilemma
Open

Add Parameterized N-Player Social Dilemma game#1571
jaisinha77777 wants to merge 4 commits into
google-deepmind:masterfrom
jaisinha77777:param-social-dilemma

Conversation

@jaisinha77777

@jaisinha77777 jaisinha77777 commented Jul 19, 2026

Copy link
Copy Markdown

Closes #1431.

Adds python_param_social_dilemma, generalizing the 2-player Iterated Prisoner's Dilemma to N players (players param, 2-10). A player's payoff depends on their own action and the fraction of other players who cooperated that round — the standard linear N-player generalization of the 2x2 PD matrix, and it reduces exactly to the classic payoffs at N=2.

On top of the base game:

  • Dynamic payoffs: multiple (Temptation, Reward, Punishment, Sucker) regimes, switched via a chance node with probability payoff_change_prob.
  • Stochastic rewards: reward_noise_std adds discretized, zero-mean noise through a proper chance node rather than raw RNG, so outcomes stay reproducible/serializable and the model is fully specified via chance_outcomes() (matching how e.g. iterated_prisoners_dilemma.py handles its termination randomness). Note this is a SIMULTANEOUS-dynamics game, so algorithms that require SEQUENTIAL dynamics (e.g. cfr.py, which asserts on this) need pyspiel.convert_to_turn_based() first — exercised in param_social_dilemma_test.py::test_game_as_turn_based. I overstated this as "usable with tree-search/CFR-style algorithms" in the original description; that's only true after the turn-based conversion, not out of the box.
  • Bots inspired by Axelrod's 2-player IPD tournament (Tit-for-Tat, Grim Trigger, Always-Cooperate, Always-Defect) in param_social_dilemma_example.py, generalized to N players. There's no single canonical way to extend 2-player-specific strategies like these to N players — see the docstrings on tit_for_tat/grim_trigger for the specific (documented, not claimed-canonical) rule each one uses.

Went with the Python-only route per the guidance in the issue thread.

Testing:

  • param_social_dilemma_test.py covers default/custom params, players=2..10, deterministic vs. noisy rewards, regime switching, and turn-based conversion.
  • Ran pyspiel.random_sim_test across player counts 2-10 and all feature combinations (noise, dynamic payoffs, both together) with serialization enabled.
  • Playthrough added and regenerates identically via generate_playthrough.replay.
  • Registered in open_spiel/python/games/__init__.py, CMakeLists.txt, and pyspiel_test.py's expected-games list; added a row to games.md.

Also flagged and filed separately: python_liars_poker's players param didn't propagate to num_players() — see #1572.

Generalizes the 2-player Iterated Prisoner's Dilemma to N players
(python_param_social_dilemma), with configurable payoff regimes that
can switch over time and discretized stochastic reward noise, both
resolved via chance nodes for compatibility with tree-search
algorithms. Includes Tit-for-Tat/Grim Trigger/Always-Cooperate/
Always-Defect bots in an example script.

Closes google-deepmind#1431
@lanctot

lanctot commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Thanks!

One thing worth flagging: while building this I noticed python_liars_poker's players param doesn't actually propagate to num_players() (it's fixed at the module-level _GAME_INFO, so load_game("python_liars_poker", {"players": 4}).num_players() still returns 2). Not part of this PR, but happy to open a separate issue/fix if useful.

Sure, that'd be a welcome contribution, please do! And thanks for flagging.

@jaisinha77777

Copy link
Copy Markdown
Author

Sure, that'd be a welcome contribution, please do! And thanks for flagging.

Thanks for sure .

@alexunderch

Copy link
Copy Markdown
Contributor

Hello!

  1. What is your implementation based on? Why do you need an observer for a fully observable game? Provide a reference please? Why do you mention CFR algorithms if the game is simultaneous, and former is for extensive games?
  2. Axelrode's bots were for 2 player variant of the social dilemmas, called iterative prisoner's dilemma, they don't usually extend for the general case. Also, how would tit-for-tat work with N players? Please, provide an explanation.

Additionally, if possible provide an example of any emergent behaviour that reproduces a pattern from your source or, for example, https://arxiv.org/abs/1702.03037.

Cheers!

generalization, not a canonical one

Axelrod's strategies were defined for exactly 2 players and reference
a single opponent's last move, which has no unique extension to N
players. Documents the specific rule each bot uses and why, per
feedback on PR google-deepmind#1571.
@jaisinha77777

Copy link
Copy Markdown
Author

Good questions, taking them in order:

1. Basis / observer / CFR. The payoff model is the standard linear N-player generalization of the 2x2 PD (each player's payoff is a linear interpolation in the fraction of co-players who cooperated) — see e.g. Hauert & Schuster '97 for the analysis of this exact construction. It reduces exactly to the 2-player matrix at N=2, checked in the tests.

On the observer: this isn't specific to imperfect-information games — every Python game in this repo implements make_py_observer, including fully public ones (e.g. tic_tac_toe.py has a BoardObserver). It's the generic interface the RL environment / MCTS / policy code queries for ObservationString/tensors regardless of information type, not something added because of hidden information here.

On CFR — you're right to push on this, and I overstated it. cfr.py asserts dynamics == SEQUENTIAL, so it doesn't run on this game directly; this is SIMULTANEOUS. What's actually true is narrower: routing the noise/regime-switch through chance_outcomes() rather than raw RNG keeps the model fully specified and serializable, and the game works with CFR-style algorithms after pyspiel.convert_to_turn_based() (exercised in the test suite). I've corrected the PR description to say this precisely instead of the broader claim.

2. N-player Tit-for-Tat / Grim Trigger. Also a fair catch — Axelrod's strategies are inherently 2-player (they mirror the opponent's last move; with N-1 co-players there's no single move to mirror). There's no agreed-upon canonical extension. What I actually implemented: Tit-for-Tat here defects iff a majority of co-players defected last round (majority-vote aggregation), and Grim Trigger defects against everyone forever if any co-player has ever defected (harsher than the 2-player original, since actions aren't addressed to a specific opponent so there's no way to retaliate selectively). I've pushed a commit documenting this explicitly in the docstrings rather than leaving it implied.

3. Comparison to Leibo et al. I haven't validated against that paper specifically, and want to be upfront that I don't think it's a fair comparison to ask for directly — that paper's sequential social dilemmas are spatially-extended gridworld environments with pixel-level observations and learned policies; this is a repeated matrix-game abstraction with fixed bots, closer in spirit to classical N-player IPD (Hauert & Schuster) than to that line of MARL work. What I can show is that it reproduces the qualitative dynamics you'd expect at this level of abstraction (e.g. an Always-Defect player exploiting Always-Cooperate, Grim Trigger locking into permanent defection once betrayed, TFT reciprocating). Happy to post example output if useful, or to adjust scope/naming if "social dilemma" is implying more than the implementation delivers.

Per feedback on PR google-deepmind#1571 (alexunderch): the existing "linear" payoff
model's defector advantage depends only on the fraction of
cooperating co-players, never on N itself, so it cannot reproduce the
group-size-dependent erosion of cooperation that's a central finding
in N-player social dilemma research. Confirmed this algebraically and
empirically before making any change.

Adds a payoff_kind="public_goods" mode (classic linear public-goods
game: contribute an endowment to a shared, multiplied pool split
evenly among all N players) where the free-rider advantage is
E * (1 - multiplier / N) -- provably increasing in N for fixed
multiplier, matching the standard public-goods group-size effect
(e.g. Ledyard '95).

Also fixes a latent bug this change surfaced: MinUtility()/MaxUtility()
were computed as min/max_round_payoff * max_game_length, which is only
a valid bound when the per-round payoff can be negative. For an
always-positive per-round payoff (as in public_goods mode, and
possible in linear mode too with a custom all-positive payoff_regimes),
the worst-case total is from the *shortest* possible game (1 round),
not the longest -- the old formula produced a MinUtility() far above
some actually-achievable returns. Fixed to take the bound over both
round-count extremes.
@jaisinha77777

Copy link
Copy Markdown
Author

Follow-up on point 3 (emergent behavior). Sat with this more and think it deserved a real answer instead of a defense of scope.

Checked it algebraically: the linear model's defector-advantage-over-cooperator, holding co-player behavior fixed, is (P-S) + frac*(T-P-R+S) — depends only on the fraction of cooperating co-players, never on N. Verified numerically too (identical advantage at N=2,3,5,10 for the same fraction). So the linear generalization structurally cannot reproduce the group-size-dependent erosion of cooperation that's the actual point of interest in most N-player social dilemma work, including Hauert & Schuster which I cited as the basis — that's not a missing validation, it's excluded by the formula itself.

Pushed a payoff_kind="public_goods" mode to address this directly: classic linear public-goods game (contribute an endowment to a shared, multiplied pool, split evenly regardless of contribution). There, the free-rider advantage is E * (1 - multiplier/N), which is provably increasing in N for fixed multiplier — the standard public-goods group-size effect (e.g. Ledyard '95). Confirmed empirically too: with E=10, multiplier=1.5, the advantage goes 2.5 → 5.0 → 7.0 → 8.5 as N goes 2 → 3 → 5 → 10.

Along the way, testing this surfaced a real bug that was already present (not introduced by this addition): MinUtility()/MaxUtility() were computed as min/max_round_payoff * max_game_length, which is only a valid bound when the per-round payoff can go negative. With an always-positive per-round payoff — which public_goods mode has by construction, and which linear mode could also hit with a custom all-positive payoff_regimes — the true worst-case total comes from the shortest possible game (1 round), not the longest, so the old bound was invalid (too high) and would fail basic_tests.cc's return-bound check. Fixed and added a regression test.

Both modes are now covered in param_social_dilemma_test.py, including one test that explicitly demonstrates the N-invariance of linear mode's advantage side-by-side with the N-growth of public_goods mode's advantage, so the contrast isn't just asserted in a docstring.

The PR description claims cfr.py's CFRSolver works after
pyspiel.convert_to_turn_based(), but the existing test only ran
random_sim_test on the converted game -- checking the conversion is
well-formed, not that CFR itself runs on it. Add a test that actually
instantiates CFRSolver and runs a few iterations, so the claim is
backed by a real test instead of an inference.
@jaisinha77777

Copy link
Copy Markdown
Author

Quick honest follow-up on point 3 since I don't want to overstate what I did.

I didn't actually reproduce anything from Leibo et al. or from Hauert & Schuster specifically — Leibo's setup is spatial/pixel-based, not comparable at this abstraction level, and H&S's actual results are about evolutionary/replicator dynamics across populations, which isn't something this game engine simulates. So I'm not claiming to have satisfied that literally.

What I did instead: checked whether the original linear model could even show N-dependent effects, and it can't — algebraically the defector's advantage only depends on the fraction of cooperators, never on N itself (verified this numerically too, identical at N=2,3,5,10). So I added a second payoff mode (public_goods, classic linear public-goods game) where the free-rider advantage is E*(1-m/N), which does provably grow with N — confirmed 2.5→5.0→7.0→8.5 for N=2,3,5,10. That's grounded in Ledyard '95, not the two sources you named, so it's an adjacent answer, not a direct one.

Figured you'd rather know that distinction than have me round it up to "done."

@alexunderch

Copy link
Copy Markdown
Contributor

Thank you for the exaplanation! Can you provide links to the papers that you cited? Curious to get more familiar with your changes.

@jaisinha77777

Copy link
Copy Markdown
Author

Sure, links:

One correction on H&S while I'm here: I actually read the full text just now, and my payoff formula is the per-opponent-averaged version of their rule, not identical to it — their raw rule sums R/S/T/P over each opponent individually (so it scales with absolute N, not just fraction). Their actual conclusion — "an increasing number of players... hinder[s] the establishment of cooperation" — comes from simulating evolutionary dynamics over many generations of memory-conditioned strategies, not from the stage-game payoff alone. Wanted to flag that distinction rather than let the citation imply more than it supports.

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.

Add Parameterized N-Player Social Dilemma Games with Dynamic and Stochastic Payoffs

3 participants