Skip to content

Spruce up game and its tests #1206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 37 additions & 9 deletions axelrod/game.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,61 @@
from typing import Tuple, Union

from .action import Action
from axelrod import Action
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this better? (I'm perfectly happy, just asking as a learning opportunity :))

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, relative imports are less common (also in the Axelrod library AFAIR) and generally discouraged. The rationale is mostly that relative imports can be ambiguous, whereas absolute ones are not. For more context: https://www.python.org/dev/peps/pep-0328

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome: thanks for the explanation.


C, D = Action.C, Action.D

Score = Union[int, float]


class Game(object):
"""A class to hold the game matrix and to score a game accordingly."""
"""Container for the game matrix and scoring logic.

Attributes
----------
scores: dict
The numerical score attribute to all combinations of action pairs.
"""

def __init__(self, r: Score = 3, s: Score = 0, t: Score = 5, p: Score = 1) -> None:
self.scores = {(C, C): (r, r), (D, D): (p, p), (C, D): (s, t), (D, C): (t, s)}
"""Create a new game object.

Parameters
----------
r: int or float
Score obtained by both players for mutual cooperation.
s: int or float
Score obtained by a player for cooperating against a defector.
t: int or float
Score obtained by a player for defecting against a cooperator.
p: int or float
Score obtained by both player for mutual defection.
"""
self.scores = {
(C, C): (r, r),
(D, D): (p, p),
(C, D): (s, t),
(D, C): (t, s)}

def RPST(self) -> Tuple[Score, Score, Score, Score]:
"""Return the values in the game matrix in the Press and Dyson
notation."""
"""Returns game matrix values in Press and Dyson notation."""
R = self.scores[(C, C)][0]
P = self.scores[(D, D)][0]
S = self.scores[(C, D)][0]
T = self.scores[(D, C)][0]
return (R, P, S, T)

def score(self, pair: Tuple[Action, Action]) -> Tuple[Score, Score]:
"""Return the appropriate score for decision pair.
"""Returns the appropriate score for a decision pair.

Parameters
----------
pair: tuple(Action, Action)
A pair actions for two players, for example (C, C).

Returns the appropriate score (as a tuple) from the scores dictionary
for a given pair of plays (passed in as a tuple).
e.g. score((C, C)) returns (2, 2)
Returns
-------
tuple of int or float
Scores for two player resulting from their actions.
"""
return self.scores[pair]

Expand Down
65 changes: 32 additions & 33 deletions axelrod/tests/unit/test_game.py
Original file line number Diff line number Diff line change
@@ -1,82 +1,81 @@
import unittest

import axelrod
from axelrod.tests.property import *
from axelrod import Action, Game
from axelrod.tests.property import games

from hypothesis import given, settings
from hypothesis.strategies import integers

C, D = axelrod.Action.C, axelrod.Action.D
C, D = Action.C, Action.D


class TestGame(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.game = axelrod.Game()

def test_init(self):
def test_default_scores(self):
expected_scores = {
(C, D): (0, 5),
(D, C): (5, 0),
(D, D): (1, 1),
(C, C): (3, 3),
}
self.assertEqual(self.game.scores, expected_scores)
self.assertEqual(Game().scores, expected_scores)

def test_RPST(self):
def test_default_RPST(self):
expected_values = (3, 1, 0, 5)
self.assertEqual(self.game.RPST(), expected_values)
self.assertEqual(Game().RPST(), expected_values)

def test_score(self):
self.assertEqual(self.game.score((C, C)), (3, 3))
self.assertEqual(self.game.score((D, D)), (1, 1))
self.assertEqual(self.game.score((C, D)), (0, 5))
self.assertEqual(self.game.score((D, C)), (5, 0))

def test_equality(self):
game_1 = Game(1, 2, 3, 4)
game_2 = Game(1, 2, 3, 4)
not_equal = Game()

self.assertEqual(game_1, game_2)
self.assertNotEqual(not_equal, game_1)
def test_default_score(self):
game = Game()
self.assertEqual(game.score((C, C)), (3, 3))
self.assertEqual(game.score((D, D)), (1, 1))
self.assertEqual(game.score((C, D)), (0, 5))
self.assertEqual(game.score((D, C)), (5, 0))

def test_default_equality(self):
self.assertEqual(Game(), Game())

def test_not_default_equality(self):
self.assertEqual(Game(1, 2, 3, 4), Game(1, 2, 3, 4))
self.assertNotEqual(Game(1, 2, 3, 4), Game(1, 2, 3, 5))
self.assertNotEqual(Game(1, 2, 3, 4), Game())

def test_wrong_class_equality(self):
self.assertNotEqual(Game(), "wrong class")

@given(r=integers(), p=integers(), s=integers(), t=integers())
@settings(max_examples=5, max_iterations=20)
def test_property_init(self, r, p, s, t):
"""Use the hypothesis library to test init"""
def test_random_init(self, r, p, s, t):
"""Test init with random scores using the hypothesis library."""
expected_scores = {
(C, D): (s, t),
(D, C): (t, s),
(D, D): (p, p),
(C, C): (r, r),
}
game = axelrod.Game(r, s, t, p)
game = Game(r, s, t, p)
self.assertEqual(game.scores, expected_scores)

@given(r=integers(), p=integers(), s=integers(), t=integers())
@settings(max_examples=5, max_iterations=20)
def test_property_RPST(self, r, p, s, t):
"""Use the hypothesis library to test RPST"""
game = axelrod.Game(r, s, t, p)
def test_random_RPST(self, r, p, s, t):
"""Test RPST method with random scores using the hypothesis library."""
game = Game(r, s, t, p)
self.assertEqual(game.RPST(), (r, p, s, t))

@given(r=integers(), p=integers(), s=integers(), t=integers())
@settings(max_examples=5, max_iterations=20)
def test_property_score(self, r, p, s, t):
"""Use the hypothesis library to test score"""
game = axelrod.Game(r, s, t, p)
def test_random_score(self, r, p, s, t):
"""Test score method with random scores using the hypothesis library."""
game = Game(r, s, t, p)
self.assertEqual(game.score((C, C)), (r, r))
self.assertEqual(game.score((D, D)), (p, p))
self.assertEqual(game.score((C, D)), (s, t))
self.assertEqual(game.score((D, C)), (t, s))

@given(game=games())
@settings(max_examples=5, max_iterations=20)
def test_repr(self, game):
def test_random_repr(self, game):
"""Test repr with random scores using the hypothesis library."""
expected_repr = "Axelrod game: (R,P,S,T) = {}".format(game.RPST())
self.assertEqual(expected_repr, game.__repr__())
self.assertEqual(expected_repr, str(game))