Skip to content

Commit 28c3d9a

Browse files
authored
Merge pull request #797 from Axelrod-Python/789
Add initial play summary to the results set.
2 parents ce0f161 + 05e5761 commit 28c3d9a

File tree

3 files changed

+112
-19
lines changed

3 files changed

+112
-19
lines changed

axelrod/result_set.py

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ def _build_empty_metrics(self, keep_interactions=False):
383383
self.cooperation = [[0 for opponent in plist] for player in plist]
384384
self.normalised_cooperation = [[[] for opponent in plist]
385385
for player in plist]
386+
self.initial_cooperation_count = [0 for player in plist]
386387
self.state_distribution = [[Counter() for opponent in plist]
387388
for player in plist]
388389
self.good_partner_matrix = [[0 for opponent in plist]
@@ -532,6 +533,24 @@ def _update_cooperation(self, p1, p2, cooperations):
532533
self.cooperation[p1][p2] += cooperations[0]
533534
self.cooperation[p2][p1] += cooperations[1]
534535

536+
def _update_initial_cooperation_count(self, p1, p2, initial_cooperations):
537+
"""
538+
During a read of the data, update the initial cooperation count
539+
attribute
540+
541+
Parameters
542+
----------
543+
544+
p1, p2 : int
545+
The indices of the first and second player
546+
initial_cooperations : tuple
547+
A 2 tuple with a 0 or 1 indicating if the initial move of each
548+
player was Cooperation (0) or Defection (1), e.g. (0, 1) for a
549+
round (C, D)
550+
"""
551+
self.initial_cooperation_count[p1] += initial_cooperations[0]
552+
self.initial_cooperation_count[p2] += initial_cooperations[1]
553+
535554
def _update_state_distribution(self, p1, p2, counter):
536555
"""
537556
During a read of the data, update the state_distribution attribute
@@ -606,6 +625,16 @@ def _build_good_partner_rating(self):
606625
max(1, float(self.total_interactions[player]))
607626
for player in range(self.nplayers)]
608627

628+
@update_progress_bar
629+
def _build_initial_cooperation_rate(self):
630+
"""
631+
At the end of a read of the data, build the normalised initial
632+
cooperation rate attribute
633+
"""
634+
return [self.initial_cooperation_count[player] /
635+
max(1, float(self.total_interactions[player]))
636+
for player in range(self.nplayers)]
637+
609638
def _build_score_related_metrics(self, progress_bar=False,
610639
keep_interactions=False):
611640
"""
@@ -653,11 +682,14 @@ def _build_score_related_metrics(self, progress_bar=False,
653682
self._update_normalised_scores(repetition, p1, p2,
654683
scores_per_turn)
655684
self._update_cooperation(p1, p2, cooperations)
685+
initial_coops = iu.compute_cooperations(interaction[:1])
686+
self._update_initial_cooperation_count(p1, p2,
687+
initial_coops)
656688
self._update_state_distribution(p1, p2, state_counter)
657689
self._update_good_partner_matrix(p1, p2, cooperations)
658690

659691
if progress_bar:
660-
self.progress_bar = tqdm.tqdm(total=11 + 2 * self.nplayers,
692+
self.progress_bar = tqdm.tqdm(total=12 + 2 * self.nplayers,
661693
desc="Finishing")
662694
self._summarise_normalised_scores()
663695
self._summarise_normalised_cooperation()
@@ -670,6 +702,7 @@ def _build_score_related_metrics(self, progress_bar=False,
670702
self.payoff_diffs_means = self._build_payoff_diffs_means()
671703
self.vengeful_cooperation = self._build_vengeful_cooperation()
672704
self.cooperating_rating = self._build_cooperating_rating()
705+
self.initial_cooperation_rate = self._build_initial_cooperation_rate()
673706
self.good_partner_rating = self._build_good_partner_rating()
674707
self.eigenjesus_rating = self._build_eigenjesus_rating()
675708
self.eigenmoses_rating = self._build_eigenmoses_rating()
@@ -739,8 +772,8 @@ def summarise(self):
739772

740773
self.player = namedtuple("Player", ["Rank", "Name", "Median_score",
741774
"Cooperation_rating", "Wins",
742-
"CC_rate", "CD_rate", "DC_rate",
743-
"DD_rate"])
775+
"Initial_C_rate", "CC_rate",
776+
"CD_rate", "DC_rate", "DD_rate"])
744777

745778
states = [(C, C), (C, D), (D, C), (D, D)]
746779
state_prob = []
@@ -756,7 +789,8 @@ def summarise(self):
756789
state_prob.append(counts)
757790

758791
summary_measures = list(zip(self.players, median_scores,
759-
self.cooperating_rating, median_wins))
792+
self.cooperating_rating, median_wins,
793+
self.initial_cooperation_rate))
760794

761795
summary_data = []
762796
for rank, i in enumerate(self.ranking):
@@ -769,7 +803,7 @@ def write_summary(self, filename):
769803
"""
770804
Write a csv file containing summary data of the results of the form:
771805
772-
"Rank", "Name", "Median-score-per-turn", "Cooperation-rating", "Wins", "CC-Rate", "CD-Rate", "DC-Rate", "DD-rate"
806+
"Rank", "Name", "Median-score-per-turn", "Cooperation-rating", "Initial_C_Rate", "Wins", "CC-Rate", "CD-Rate", "DC-Rate", "DD-rate"
773807
774808
Parameters
775809
----------

axelrod/tests/unit/test_resultset.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ def setUpClass(cls):
120120
[0, 0, 0],
121121
]
122122

123+
cls.expected_initial_cooperation_count = [6, 6, 0]
124+
cls.expected_initial_cooperation_rate = [1, 1, 0]
125+
123126
cls.expected_state_distribution = [
124127
[], [], []
125128
]
@@ -223,13 +226,13 @@ def test_init_with_different_game(self):
223226
def test_with_progress_bar(self):
224227
rs = axelrod.ResultSet(self.players, self.interactions)
225228
self.assertTrue(rs.progress_bar)
226-
self.assertEqual(rs.progress_bar.total, 11 + 2 * rs.nplayers)
229+
self.assertEqual(rs.progress_bar.total, 12 + 2 * rs.nplayers)
227230
self.assertEqual(rs.progress_bar.n, rs.progress_bar.total)
228231

229232
rs = axelrod.ResultSet(self.players, self.interactions,
230233
progress_bar=True)
231234
self.assertTrue(rs.progress_bar)
232-
self.assertEqual(rs.progress_bar.total, 11 + 2 * rs.nplayers)
235+
self.assertEqual(rs.progress_bar.total, 12 + 2 * rs.nplayers)
233236
self.assertEqual(rs.progress_bar.n, rs.progress_bar.total)
234237

235238
def test_match_lengths(self):
@@ -345,6 +348,14 @@ def test_cooperation(self):
345348
self.assertEqual(len(rs.cooperation), rs.nplayers)
346349
self.assertEqual(rs.cooperation, self.expected_cooperation)
347350

351+
def test_initial_cooperation_count(self):
352+
rs = axelrod.ResultSet(self.players, self.interactions,
353+
progress_bar=False)
354+
self.assertIsInstance(rs.initial_cooperation_count, list)
355+
self.assertEqual(len(rs.initial_cooperation_count), rs.nplayers)
356+
self.assertEqual(rs.initial_cooperation_count,
357+
self.expected_initial_cooperation_count)
358+
348359
def test_normalised_cooperation(self):
349360
rs = axelrod.ResultSet(self.players, self.interactions,
350361
progress_bar=False)
@@ -353,6 +364,14 @@ def test_normalised_cooperation(self):
353364
self.assertEqual(rs.normalised_cooperation,
354365
self.expected_normalised_cooperation)
355366

367+
def test_initial_cooperation_rate(self):
368+
rs = axelrod.ResultSet(self.players, self.interactions,
369+
progress_bar=False)
370+
self.assertIsInstance(rs.initial_cooperation_rate, list)
371+
self.assertEqual(len(rs.initial_cooperation_rate), rs.nplayers)
372+
self.assertEqual(rs.initial_cooperation_rate,
373+
self.expected_initial_cooperation_rate)
374+
356375
def test_state_distribution(self):
357376
rs = axelrod.ResultSet(self.players, self.interactions,
358377
progress_bar=False)
@@ -459,6 +478,12 @@ def test_summarise(self):
459478
self.assertEqual([float(player.Wins) for player in sd],
460479
ranked_median_wins)
461480

481+
ranked_initial_coop_rates = [self.expected_initial_cooperation_rate[i]
482+
for i in rs.ranking]
483+
self.assertEqual([float(player.Initial_C_rate)
484+
for player in sd],
485+
ranked_initial_coop_rates)
486+
462487
for player in sd:
463488
self.assertEqual(player.CC_rate + player.CD_rate + player.DC_rate + player.DD_rate, 1)
464489

@@ -471,7 +496,7 @@ def test_write_summary(self):
471496
csvreader = csv.reader(csvfile)
472497
for row in csvreader:
473498
ranked_names.append(row[1])
474-
self.assertEqual(len(row), 9)
499+
self.assertEqual(len(row), 10)
475500
self.assertEqual(ranked_names[0], "Name")
476501
self.assertEqual(ranked_names[1:], rs.ranked_names)
477502

@@ -809,6 +834,9 @@ def setUpClass(cls):
809834
[0, 0, 0],
810835
]
811836

837+
cls.expected_initial_cooperation_count = [6, 3, 0]
838+
cls.expected_initial_cooperation_rate = [1, 1, 0]
839+
812840
cls.expected_vengeful_cooperation = [[2 * element - 1 for element in row]
813841
for row in cls.expected_normalised_cooperation]
814842

@@ -1026,6 +1054,9 @@ def setUpClass(cls):
10261054
[0.0, 0.0, mean([5 / 5.0 for _ in range(3)]), 0.0]
10271055
]
10281056

1057+
cls.expected_initial_cooperation_count = [3.0, 3.0, 0, 3.0]
1058+
cls.expected_initial_cooperation_rate = [1.0, 1.0, 0, 1.0]
1059+
10291060
cls.expected_vengeful_cooperation = [[2 * element - 1 for element in row]
10301061
for row in cls.expected_normalised_cooperation]
10311062

@@ -1188,6 +1219,9 @@ def setUpClass(cls):
11881219
[0.0, 0.0, 0.0, mean([5 / 5.0 for _ in range(3)])]
11891220
]
11901221

1222+
cls.expected_initial_cooperation_count = [0, 0, 0, 0]
1223+
cls.expected_initial_cooperation_rate = [0, 0, 0, 0]
1224+
11911225
cls.expected_vengeful_cooperation = [[2 * element - 1 for element in row]
11921226
for row in cls.expected_normalised_cooperation]
11931227

@@ -1265,3 +1299,4 @@ def test_summarise_without_failure(self, tournament):
12651299
total_rate = round(player.CC_rate + player.CD_rate +
12661300
player.DC_rate + player.DD_rate, 3)
12671301
self.assertTrue(total_rate in [0, 1])
1302+
self.assertTrue(0 <= player.Initial_C_rate <= 1)

docs/tutorials/getting_started/tournament_results.rst

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ This tutorial will show you how to access the various results of a tournament:
2424
- State distribution: the count of each type of state of a match
2525
- Normalised state distribution: the normalised count of each type of state of a
2626
match
27+
- Initial cooperation count: the count of initial cooperation by each player.
28+
- Initial cooperation rate: the rate of initial cooperation by each player.
2729
- Cooperation rating: cooperation rating of each player
2830
- Vengeful cooperation: a morality metric from the literature (see
2931
:ref:`morality-metrics`).
@@ -63,8 +65,8 @@ This gives the length of the matches played by each player::
6365
[[10, 10, 10, 10], [10, 10, 10, 10], [10, 10, 10, 10], [10, 10, 10, 10]],
6466
[[10, 10, 10, 10], [10, 10, 10, 10], [10, 10, 10, 10], [10, 10, 10, 10]]]
6567

66-
Every player plays 200 turns against every other player for every repetition of
67-
the tournament.
68+
Every player plays 10 turns against every other player (including themselves)
69+
for every repetition of the tournament.
6870

6971
Scores
7072
------
@@ -267,6 +269,28 @@ the second the action of the opponent::
267269
Counter({('C', 'C'): 1.0}),
268270
Counter()]]
269271

272+
273+
Initial cooperation counts
274+
--------------------------
275+
276+
This gives the count of cooperations made by each player during the first turn
277+
of every match::
278+
279+
>>> results.initial_cooperation_count
280+
[9, 0, 9, 9]
281+
282+
Each player plays an opponent a total of 9 times (3 opponents and 3
283+
repetitions). Apart from the :code:`Defector`, they all cooperate on the first
284+
turn.
285+
286+
Initial cooperation rates
287+
-------------------------
288+
289+
This gives the rate of which a strategy cooperates during the first turn::
290+
291+
>>> results.initial_cooperation_rate
292+
[1.0, 0.0, 1.0, 1.0]
293+
270294
Morality Metrics
271295
----------------
272296

@@ -299,10 +323,10 @@ that summarises the results of the tournament::
299323

300324
>>> summary = results.summarise()
301325
>>> pprint.pprint(summary)
302-
[Player(Rank=0, Name='Defector', Median_score=2.6..., Cooperation_rating=0.0, Wins=3.0, CC_rate=...),
303-
Player(Rank=1, Name='Tit For Tat', Median_score=2.3..., Cooperation_rating=0.7, Wins=0.0, CC_rate=...),
304-
Player(Rank=2, Name='Grudger', Median_score=2.3..., Cooperation_rating=0.7, Wins=0.0, CC_rate=...),
305-
Player(Rank=3, Name='Cooperator', Median_score=2.0..., Cooperation_rating=1.0, Wins=0.0, CC_rate=...)]
326+
[Player(Rank=0, Name='Defector', Median_score=2.6..., Cooperation_rating=0.0, Wins=3.0, Initial_C_rate=0.0, CC_rate=...),
327+
Player(Rank=1, Name='Tit For Tat', Median_score=2.3..., Cooperation_rating=0.7, Wins=0.0, Initial_C_rate=1.0, CC_rate=...),
328+
Player(Rank=2, Name='Grudger', Median_score=2.3..., Cooperation_rating=0.7, Wins=0.0, Initial_C_rate=1.0, CC_rate=...),
329+
Player(Rank=3, Name='Cooperator', Median_score=2.0..., Cooperation_rating=1.0, Wins=0.0, Initial_C_rate=1.0, CC_rate=...)]
306330

307331
It is also possible to write this data directly to a csv file using the
308332
`write_summary` method::
@@ -313,8 +337,8 @@ It is also possible to write this data directly to a csv file using the
313337
... csvreader = csv.reader(outfile)
314338
... for row in csvreader:
315339
... print(row)
316-
['Rank', 'Name', 'Median_score', 'Cooperation_rating', 'Wins', 'CC_rate', 'CD_rate', 'DC_rate', 'DD_rate']
317-
['0', 'Defector', '2.6...', '0.0', '3.0', '0.0', '0.0', '0.4...', '0.6...']
318-
['1', 'Tit For Tat', '2.3...', '0.7', '0.0', '0.66...', '0.03...', '0.0', '0.3...']
319-
['2', 'Grudger', '2.3...', '0.7', '0.0', '0.66...', '0.03...', '0.0', '0.3...']
320-
['3', 'Cooperator', '2.0...', '1.0', '0.0', '0.66...', '0.33...', '0.0', '0.0']
340+
['Rank', 'Name', 'Median_score', 'Cooperation_rating', 'Wins', 'Initial_C_rate', 'CC_rate', 'CD_rate', 'DC_rate', 'DD_rate']
341+
['0', 'Defector', '2.6...', '0.0', '3.0', '0.0', '0.0', '0.0', '0.4...', '0.6...']
342+
['1', 'Tit For Tat', '2.3...', '0.7', '0.0', '1.0', '0.66...', '0.03...', '0.0', '0.3...']
343+
['2', 'Grudger', '2.3...', '0.7', '0.0', '1.0', '0.66...', '0.03...', '0.0', '0.3...']
344+
['3', 'Cooperator', '2.0...', '1.0', '0.0', '1.0', '0.66...', '0.33...', '0.0', '0.0']

0 commit comments

Comments
 (0)