Skip to content

Commit

Permalink
Update docs, rework simulate play and some test internals, add warnin…
Browse files Browse the repository at this point in the history
…gs for mismatched histories
  • Loading branch information
marcharper committed Jan 16, 2017
1 parent 6be7c6a commit cb61be5
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 98 deletions.
58 changes: 36 additions & 22 deletions axelrod/mock_player.py
@@ -1,55 +1,69 @@
from collections import defaultdict
import copy
from axelrod import (Actions, Player, get_state_distribution_from_history,
update_history, update_state_distribution)
import warnings
from axelrod import (Actions, Player, update_history, update_state_distribution)

C, D = Actions.C, Actions.D


class MockPlayer(Player):
"""Creates a mock player that enforces a particular next move for a given
player."""
"""Creates a mock player that copies a history and state distribution to
simulate a history of play, and then plays a given sequence of actions. If
no actions are given, plays like Cooperator.
"""

name = "Mock Player"

def __init__(self, player, move):
def __init__(self, actions=None, history=None, state_dist=None):
# Need to retain history for opponents that examine opponents history
# Do a deep copy just to be safe
super().__init__()
self.history = copy.deepcopy(player.history)
self.cooperations = player.cooperations
self.defections = player.defections
self.move = move
if history:
# Make sure we both copy the history and get the right counts
# for cooperations and defections.
for action in history:
update_history(self, action)
if state_dist:
self.state_distribution = dict(state_dist)
if actions:
self.actions = list(actions)
else:
self.actions = []

def strategy(self, opponent):
# Just return the saved move
return self.move
# Return the next saved action, if present.
try:
action = self.actions.pop(0)
return action
except IndexError:
return C


def simulate_play(P1, P2, h1=None, h2=None):
"""
Simulates play with or without forced history. If h1 and h2 are given, these
moves are enforced in the players strategy. This generally should not be
actions are enforced in the players strategy. This generally should not be
necessary, but various tests may force impossible or unlikely histories.
"""

if h1 and h2:
# Simulate Players
mock_P1 = MockPlayer(P1, h1)
mock_P2 = MockPlayer(P2, h1)
mock_P1.state_distribution = defaultdict(
int, zip(P1.history, P2.history))
mock_P2.state_distribution = defaultdict(
int, zip(P2.history, P1.history))
mock_P1 = MockPlayer(actions=[h1], history=P1.history)
mock_P2 = MockPlayer(actions=[h2], history=P2.history)
# Force plays

s1 = P1.strategy(mock_P2)
s2 = P2.strategy(mock_P1)
if (s1 != h1) or (s2 != h2):
warnings.warn(
"Simulated play mismatch with expected history: Round was "
"({}, {}) but ({}, {}) was expected for player: {}".format(
s1, s2, h1, h2, str(P1))
)
# Record intended history
# Update Cooperation / Defection counts
update_history(P1, h1)
update_history(P2, h2)
update_state_distribution(P1, h1, h2)
update_state_distribution(P2, h2, h1)
return (h1, h2)
return (s1, s2)
else:
s1 = P1.strategy(P2)
s2 = P2.strategy(P1)
Expand Down
1 change: 1 addition & 0 deletions axelrod/strategies/appeaser.py
Expand Up @@ -2,6 +2,7 @@

C, D = Actions.C, Actions.D


class Appeaser(Player):
"""A player who tries to guess what the opponent wants.
Expand Down
6 changes: 2 additions & 4 deletions axelrod/tests/unit/test_alternator.py
Expand Up @@ -23,9 +23,7 @@ class TestAlternator(TestPlayer):
def test_strategy(self):
# Starts by cooperating.
self.first_play_test(C)
# Simply does the opposite to what the strategy did last time.
self.second_play_test(D, D, C, C)
for i in range(10):
self.responses_test([C, D] * i)
self.responses_test([C], [C, D, D, D], [C, C, C, C])
self.responses_test([D], [C, C, D, D, C], [C, D, C, C, C])
self.responses_test([C], [C, D, C, D], [C, C, C, C])
self.responses_test([D], [C, D, C, D, C], [C, C, C, C, C])
2 changes: 1 addition & 1 deletion axelrod/tests/unit/test_cooperator.py
@@ -1,4 +1,4 @@
"""Test for the Cooperator strategy."""
"""Tests for the Cooperator strategy."""

import axelrod
from .test_player import TestPlayer
Expand Down
47 changes: 26 additions & 21 deletions axelrod/tests/unit/test_mock_player.py
Expand Up @@ -9,27 +9,31 @@
class TestMockPlayer(unittest.TestCase):

def test_strategy(self):
for move in [C, D]:
m = MockPlayer(axelrod.Player(), move)
for action in [C, D]:
m = MockPlayer( [action])
p2 = axelrod.Player()
self.assertEqual(move, m.strategy(p2))

def test_cloning(self):
p1 = axelrod.Cooperator()
p2 = axelrod.Defector()
moves = 10
for i in range(moves):
p1.play(p2)
m1 = MockPlayer(p1, C)
m2 = MockPlayer(p2, D)
self.assertEqual(m1.move, C)
self.assertEqual(m1.history, p1.history)
self.assertEqual(m1.cooperations, p1.cooperations)
self.assertEqual(m1.defections, p1.defections)
self.assertEqual(m2.move, D)
self.assertEqual(m2.history, p2.history)
self.assertEqual(m2.cooperations, p2.cooperations)
self.assertEqual(m2.defections, p2.defections)
self.assertEqual(action, m.strategy(p2))

actions = [C, C, D, D, C, C]
m = MockPlayer(actions)
p2 = axelrod.Player()
for action in actions:
self.assertEqual(action, m.strategy(p2))

def test_history(self):
t = TestOpponent()
m1 = MockPlayer([C], history=[C]*10)
self.assertEqual(m1.actions[0], C)
self.assertEqual(m1.history, [C] * 10)
self.assertEqual(m1.cooperations, 10)
self.assertEqual(m1.defections, 0)
self.assertEqual(m1.strategy(t), C)
m2 = MockPlayer([D], history=[D]*10)
self.assertEqual(m2.actions[0], D)
self.assertEqual(m2.history, [D] * 10)
self.assertEqual(m2.cooperations, 0)
self.assertEqual(m2.defections, 10)
self.assertEqual(m2.strategy(t), D)


class TestUpdateHistories(unittest.TestCase):
Expand Down Expand Up @@ -67,9 +71,10 @@ def test_various(self):
self.assertEqual(p1.defections, 0)
self.assertEqual(p2.defections, 0)

# TestOpponent always returns C
for h1 in [C, D]:
for h2 in [C, D]:
self.assertEqual(simulate_play(p1, p2, h1, h2), (h1, h2))
self.assertEqual(simulate_play(p1, p2, h1, h2), (C, C))
self.assertEqual(p1.cooperations, 3)
self.assertEqual(p2.cooperations, 3)
self.assertEqual(p1.defections, 2)
Expand Down
21 changes: 12 additions & 9 deletions axelrod/tests/unit/test_player.py
@@ -1,8 +1,9 @@
import random
import warnings
import unittest

import axelrod
from axelrod import DefaultGame, Player, simulate_play
from axelrod import DefaultGame, MockPlayer, Player, simulate_play


C, D = axelrod.Actions.C, axelrod.Actions.D
Expand Down Expand Up @@ -131,7 +132,7 @@ def test_responses(test_class, player1, player2, responses, history1=None,
# method. Still need to append history manually.
if history1 and history2:
for h1, h2 in zip(history1, history2):
simulate_play(player1, player2, h1, h2)
s1, s2 = simulate_play(player1, player2, h1, h2)
# Run the tests
for response in responses:
s1, s2 = simulate_play(player1, player2)
Expand Down Expand Up @@ -265,10 +266,14 @@ def first_play_test(self, play, seed=None):
def second_play_test(self, rCC, rCD, rDC, rDD, seed=None):
"""Test responses to the four possible one round histories. Input
responses is simply the four responses to CC, CD, DC, and DD."""
self.responses_test(rCC, [C], [C], seed=seed)
self.responses_test(rCD, [C], [D], seed=seed)
self.responses_test(rDC, [D], [C], seed=seed)
self.responses_test(rDD, [D], [D], seed=seed)
test_responses(self, self.player(), axelrod.Cooperator(),
rCC, [C], [C], seed=seed)
test_responses(self, self.player(), axelrod.Defector(),
rCD, [C], [D], seed=seed)
test_responses(self, self.player(), axelrod.Cooperator(),
rDC, [D], [C], seed=seed)
test_responses(self, self.player(), axelrod.Defector(),
rDD, [D], [D], seed=seed)

def responses_test(self, responses, history1=None, history2=None,
seed=None, tournament_length=200, attrs=None,
Expand All @@ -285,10 +290,8 @@ def responses_test(self, responses, history1=None, history2=None,

player1 = self.player(*init_args, **init_kwargs)
player1.set_match_attributes(length=tournament_length)
# player1.match_attributes['length'] = tournament_length
player2 = TestOpponent()
player2 = MockPlayer()
player2.set_match_attributes(length=tournament_length)
# player2.match_attributes['length'] = tournament_length
test_responses(self, player1, player2, responses, history1, history2,
seed=seed, attrs=attrs)

Expand Down

0 comments on commit cb61be5

Please sign in to comment.