From e4464711319bd88ce363159e4a09ab402cda7252 Mon Sep 17 00:00:00 2001 From: barlowa124 Date: Sat, 10 Oct 2026 10:57:01 -0400 Subject: [PATCH] Fix death-birth MoranProcess pairing wrong players In mode="db", _matchup_indices() used the enumerate position in the dead node's neighbour list as the player index, so score_all() played and credited the wrong players. Use self.index[source] so both modes use global player positions. Fixes #1499. --- axelrod/moran.py | 3 ++- axelrod/tests/unit/test_moran.py | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/axelrod/moran.py b/axelrod/moran.py index 0b7ea7320..a41e42c6b 100644 --- a/axelrod/moran.py +++ b/axelrod/moran.py @@ -354,7 +354,8 @@ def _matchup_indices(self) -> Set[Tuple[int, int]]: else: # birth-death is global sources = sorted(self.locations) - for i, source in enumerate(sources): + for source in sources: + i = self.index[source] for target in sorted(self.interaction_graph.out_vertices(source)): j = self.index[target] if (self.players[i] is None) or (self.players[j] is None): diff --git a/axelrod/tests/unit/test_moran.py b/axelrod/tests/unit/test_moran.py index 2566118af..489bff8ae 100644 --- a/axelrod/tests/unit/test_moran.py +++ b/axelrod/tests/unit/test_moran.py @@ -572,6 +572,31 @@ def test_cycle_death_birth(self): winner2 = mp.winning_strategy_name self.assertEqual((winner == winner2), outcome) + def test_death_birth_matchup_indices_use_player_positions(self): + """Regression test: in death-birth mode the matchup pairs must be + player positions, not positions in the dead node's neighbour list. + + On a 5-cycle, when the player on vertex 2 dies its neighbours + (vertices 1 and 3) each play their own out-neighbours, so the + matchups are (1, 0) and (3, 4). + """ + N = 5 + players = [axl.Cooperator(), axl.Defector(), axl.TitForTat(), + axl.Cooperator(), axl.Defector()] + mp = MoranProcess( + players, + interaction_graph=axl.graph.cycle(N), + mode="db", + seed=1, + ) + # Reproduce the state __next__ creates before score_all() + mp.dead = 2 + mp.players[2] = None + self.assertEqual(mp._matchup_indices(), {(1, 0), (3, 4)}) + scores = mp.score_all() + # Only the two neighbours of the dead node and their opponents play + self.assertEqual(scores, [0.0, 5.0, 0, 0.0, 5.0]) + class TestApproximateMoranProcess(unittest.TestCase): """A suite of tests for the ApproximateMoranProcess"""