Skip to content

Commit 8601e4e

Browse files
authored
Merge pull request #84 from Axelrod-Python/random_seed
Random seeding
2 parents fba2749 + e30437c commit 8601e4e

File tree

6 files changed

+74
-34
lines changed

6 files changed

+74
-34
lines changed

.travis.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
language: python
22
python:
3-
- 3.5
43
- 3.6
4+
- 3.7
55
dist: xenial
66
sudo: required
77
services:
@@ -19,5 +19,6 @@ before_install:
1919
install:
2020
- pip install -r requirements.txt
2121
- pip install pytest
22+
- python setup.py install
2223
script:
23-
- pytest
24+
- pytest tests/

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
axelrod>=4.8.0
1+
axelrod>=4.10.0

src/axelrod_fortran/player.py

+21-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from ctypes import byref, c_float, c_int, POINTER
2-
import random
32
import warnings
43

54
import axelrod as axl
@@ -42,16 +41,19 @@ def __init__(self, original_name):
4241
A instance of an axelrod Game
4342
"""
4443
super().__init__()
44+
# The order of the next 4 lines is important. We must first check that
45+
# the player name is valid, then grab a copy of the shared library,
46+
# and then setup the actual strategy function.
47+
self.original_name = original_name
4548
self.index, self.shared_library_filename = \
46-
shared_library_manager.get_filename_for_player(original_name)
49+
shared_library_manager.get_filename_for_player(self.original_name)
4750
self.shared_library = load_library(self.shared_library_filename)
48-
self.original_name = original_name
4951
self.original_function = self.original_name
52+
5053
is_stochastic = characteristics[self.original_name]['stochastic']
5154
if is_stochastic is not None:
5255
self.classifier['stochastic'] = is_stochastic
5356

54-
5557
def __enter__(self):
5658
return self
5759

@@ -63,11 +65,11 @@ def original_name(self):
6365
return self.__original_name
6466

6567
@original_name.setter
66-
def original_name(self, value):
67-
if value in characteristics:
68-
self.__original_name = value
68+
def original_name(self, key):
69+
if key in characteristics:
70+
self.__original_name = key
6971
else:
70-
raise ValueError('{} is not a valid Fortran function'.format(value))
72+
raise ValueError('{} is not a valid Fortran function'.format(key))
7173

7274
@property
7375
def original_function(self):
@@ -105,7 +107,7 @@ def strategy(self, opponent):
105107
my_last_move = original_actions[self.history[-1]]
106108
move_number = len(self.history) + 1
107109
if self.classifier["stochastic"]:
108-
random_value = random.random()
110+
random_value = self._random.random()
109111
else:
110112
random_value = 0
111113
original_action = self.original_strategy(
@@ -119,7 +121,16 @@ def _release_shared_library(self):
119121
# thread closes before the player class is garbage collected, which
120122
# tends to happen at the end of a script.
121123
try:
122-
shared_library_manager.release(self.original_name, self.index)
124+
name = self.original_name
125+
index = self.index
126+
except AttributeError:
127+
# If the Player does finish __init__, because the name of a
128+
# non-existent strategy is supplied, a copy of the shared library
129+
# won't be loaded, nor will self.original_name or self.index
130+
# exist. In that case there's nothing to do.
131+
return
132+
try:
133+
shared_library_manager.release(name, index)
123134
except FileNotFoundError:
124135
pass
125136

src/axelrod_fortran/shared_library_manager.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def create_library_copy(self):
5959
# Copy the library file to a new (temp) location.
6060
temp_directory = tempfile.gettempdir()
6161
copy_number = len(self.filenames)
62-
filename = "{}-{}-{}".format(
62+
filename = "{}-{}-{}".format(
6363
self.prefix,
6464
str(copy_number),
6565
self.shared_library_name)

src/axelrod_fortran/strategies.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@
348348
'original_rank': None},
349349
}
350350

351-
all_strategies = characteristics.keys()
351+
all_strategies = list(characteristics.keys())
352352

353353
# Players from Axelrod's second tournament.
354354
second_tournament_strategies = [

tests/test_player.py

+47-19
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
from ctypes import c_int, c_float, POINTER, CDLL
1+
from ctypes import c_int, c_float, POINTER
22
import itertools
33

44
import pytest
55

66
from axelrod_fortran import Player, characteristics, all_strategies
77
from axelrod import (Alternator, Cooperator, Defector, Match, MoranProcess,
8-
Game, basic_strategies, seed)
8+
Game, RandomGenerator, Tournament, basic_strategies)
99
from axelrod.action import Action
1010

1111

@@ -122,32 +122,33 @@ def test_implemented_strategies():
122122
"""
123123
for strategy, dictionary in characteristics.items():
124124
axelrod_class = dictionary["axelrod-python_class"]
125-
player = Player(strategy)
126-
if (axelrod_class is not None and
127-
player.classifier["stochastic"] is False):
128-
axl_player = axelrod_class()
125+
stochastic = Player(strategy).classifier["stochastic"]
126+
if axelrod_class is not None and not stochastic:
129127
for opponent_strategy in basic_strategies:
128+
player = Player(strategy)
130129
opponent = opponent_strategy()
131130
match = Match((player, opponent))
132131
interactions = match.play()
132+
133+
axl_player = axelrod_class()
134+
opponent = opponent_strategy()
133135
axl_match = Match((axl_player, opponent))
134-
assert interactions == axl_match.play(), (player, opponent)
136+
axl_interactions = axl_match.play()
137+
assert interactions == axl_interactions
135138

136139

137140
def test_champion_v_alternator():
138141
"""
139-
Specific regression test for a bug.
142+
Specific regression test for a bug. See:
143+
https://github.com/Axelrod-Python/axelrod-fortran/issues/62
140144
"""
141145
player = Player("k61r")
142146
opponent = Alternator()
143-
144-
match = Match((player, opponent))
145-
146-
seed(0)
147+
seed = 3
148+
match = Match((player, opponent), seed=seed)
147149
interactions = match.play()
148150
assert interactions[25:30] == [(C, D), (C, C), (C, D), (D, C), (C, D)]
149-
150-
seed(0)
151+
match = Match((player, opponent), seed=seed)
151152
assert interactions == match.play()
152153

153154

@@ -157,9 +158,7 @@ def test_warning_for_self_interaction(recwarn):
157158
"""
158159
player = Player("k42r")
159160
opponent = player
160-
161161
match = Match((player, opponent))
162-
163162
interactions = match.play()
164163
assert len(recwarn) == 1
165164

@@ -168,13 +167,10 @@ def test_no_warning_for_normal_interaction(recwarn):
168167
"""
169168
Test that a warning is not given for a normal interaction
170169
"""
171-
player = Player("k42r")
172-
opponent = Alternator()
173170
for players in [(Player("k42r"), Alternator()),
174171
(Player("k42r"), Player("k41r"))]:
175172

176173
match = Match(players)
177-
178174
interactions = match.play()
179175
assert len(recwarn) == 0
180176

@@ -185,3 +181,35 @@ def test_multiple_copies(recwarn):
185181
mp = MoranProcess(players)
186182
mp.play()
187183
mp.populations_plot()
184+
185+
186+
def test_match_reproducibility():
187+
for _ in range(100):
188+
rng = RandomGenerator()
189+
seed = rng.random_seed_int()
190+
strategies = rng.choice(all_strategies, size=2)
191+
players1 = [Player(strategy) for strategy in strategies]
192+
match1 = Match(players1, turns=200, noise=0.1, seed=seed)
193+
results1 = match1.play()
194+
195+
players2 = [Player(strategy) for strategy in strategies]
196+
match2 = Match(players2, turns=200, noise=0.1, seed=seed)
197+
results2 = match2.play()
198+
199+
assert (results1 == results2)
200+
201+
202+
def test_tournament_reproducibility():
203+
rng = RandomGenerator()
204+
seed = rng.random_seed_int()
205+
strategies = rng.choice(all_strategies, size=10)
206+
players1 = [Player(strategy) for strategy in strategies]
207+
tournament1 = Tournament(players1, seed=seed, repetitions=2)
208+
results1 = tournament1.play(processes=2)
209+
210+
players2 = [Player(strategy) for strategy in strategies]
211+
tournament2 = Tournament(players2, seed=seed, repetitions=2)
212+
results2 = tournament2.play(processes=2)
213+
214+
assert (results1.ranked_names == results2.ranked_names)
215+

0 commit comments

Comments
 (0)