Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Refactored some classes into separate files. Added environment. Added…

… Strategy view plugin to show populations. Added per-iteration consumption and the concept of death if a critters food slips below 0
  • Loading branch information...
commit 51fd11226d075b78b5b2cec4bc86eafbb911662d 1 parent 5b02e5a
@bendavieshe3 authored
View
76 src/environment.py
@@ -0,0 +1,76 @@
+'''
+Created on May 25, 2011
+
+@author: bendavies
+'''
+import critters, strategies
+
+#constants
+
+ITERATION_FOOD_CONSUMPTION = 5
+
+
+class Environment(object):
+ '''Hosts all of the objects in the simulation'''
+
+ def __init__(self):
+ '''
+ Initialises the environment
+ Usage:
+ >>> env = Environment()
+ '''
+ self.iteration_no = 0
+ self.population = list()
+ self.strategy_counts = dict()
+
+ def start_iteration(self):
+ self.iteration_no += 1
+
+ def end_iteration(self):
+ '''
+ Ends the iteration and forces all critters to consume food
+ '''
+
+ #all critters consume food
+ for critter in self.population:
+ critter.remove_food(ITERATION_FOOD_CONSUMPTION)
+
+ #if a critter has 0 or less food, he dies :(
+ if critter.food <= 0:
+ self.population.remove(critter)
+
+ def add_critter(self, critter):
+ '''
+ Adds a critter to the environment
+
+ >>> e = Environment()
+ >>> sucker = critters.Critter('s1', strategies.SuckerStrategy())
+ >>> cheater = critters.Critter('c1', strategies.CheatStrategy())
+ >>> e.add_critter(sucker)
+ >>> len(e.population)
+ 1
+ '''
+
+ if critter not in self.population:
+ self.population.append(critter)
+
+ strategy = critter.strategy.short_name
+ if strategy not in self.strategy_counts:
+ self.strategy_counts[strategy] = 1
+ else:
+ self.strategy_counts[strategy] += 1
+
+ def add_critters(self, critter_list):
+ '''
+ Add multiple critters to the environment
+ '''
+ for critter in critter_list:
+ self.add_critter(critter)
+
+if __name__ == '__main__':
+ import doctest
+ doctest.testmod()
+
+
+
+
View
6 src/generations.py
@@ -4,7 +4,7 @@
@author: ben
'''
-import sys, getopt, world
+import sys, getopt, world, plugins
def main(argv):
world_to_run = 'PrisonersDilemma'
@@ -30,10 +30,10 @@ def main(argv):
print "running world %s" % world_to_run
xworld = instantiate_world(world_to_run)
- xworld.add_plugin(world.TabularReporter())
+ xworld.add_plugin(plugins.StrategyTabularReporter())
if track_critter:
- xworld.add_plugin(world.CritterTracker(track_critter))
+ xworld.add_plugin(plugins.CritterTracker(track_critter))
xworld.run(iterations)
View
200 src/plugins.py
@@ -0,0 +1,200 @@
+'''
+Created on May 24, 2011
+
+@author: bendavies
+'''
+import strategies
+
+
+class EventPlugin(object):
+ '''An archetype for plugin types'''
+
+ def on_environment_start(self, environment):
+ '''called prior to the start of iterations with the initial environment'''
+ pass
+
+ def on_iteration_start(self, environment):
+ '''called at the start of the iteration'''
+ pass
+
+ def on_iteration_end(self, environment):
+ '''called at the end of the iteration'''
+ pass
+
+ def on_environment_end(self, environment):
+ '''called at the end of the simulation when the environment will no longer
+ change'''
+ pass
+
+ def on_interaction_end(self, agent1, agent1_outcome, agent2, agent2_outcome):
+ '''called at the end of an interaction between two agents'''
+ pass
+
+class IndividualTabularReporter(EventPlugin):
+ '''Provides basic text output to the standard out'''
+
+ def on_environment_start(self, environment):
+ '''called prior to the start of iterations with the initial environment'''
+ self.print_headers(environment)
+
+ def on_iteration_start(self, environment):
+ '''called at the start of the iteration'''
+ pass
+
+ def on_iteration_end(self, environment):
+ '''called at the end of the iteration'''
+ if environment.iteration_no == 1 or environment.iteration_no % 5 == 0:
+ self.print_iteration_line(environment)
+
+ def on_environment_end(self, environment):
+ '''called at the end of the simulation when the environment will no longer
+ change'''
+ print 'Final Food Totals'
+ self.print_iteration_line(environment)
+ self.print_headers(environment)
+
+ population = environment.population
+ max_food = max([critter.food for critter in population])
+ winners = [critter.name + ' ' for critter in population
+ if (critter.food == max_food)]
+ print('winners are %s' % ''.join(winners))
+
+ def print_headers(self, environment):
+ '''Write the headers'''
+ header_line = 'p:\t'
+ for critter in environment.population:
+ header_line += '%s\t' % critter.name
+ print header_line
+
+ def print_iteration_line(self, environment):
+ '''Write a reporting line for the iteration with current food values'''
+ report_line = ('%d:\t' % environment.iteration_no)
+ for critter in environment.population:
+ report_line += '%d\t' % critter.food
+ print report_line
+
+class StrategyTabularReporter(EventPlugin):
+ '''Provides basic output summarising strategy level numbers to the std out'''
+
+ def on_environment_start(self, environment):
+ '''called prior to the start of iterations with the initial environment'''
+ self.print_headers(environment)
+
+ def on_iteration_start(self, environment):
+ '''called at the start of the iteration'''
+ pass
+
+ def on_iteration_end(self, environment):
+ '''called at the end of the iteration'''
+ if environment.iteration_no == 1 or environment.iteration_no % 5 == 0:
+ self.print_iteration_line(environment)
+
+ def on_environment_end(self, environment):
+ '''called at the end of the simulation when the environment will no longer
+ change'''
+ print 'Final Population Totals'
+ critter_count, food_count = self.calculate_strategy_totals(environment)
+
+ self.print_iteration_line(environment)
+
+ #print extra food totals line
+ header_line = 'p:\t'
+ for strategy in environment.strategy_counts.keys():
+ header_line += '%s\t' % food_count[strategy]
+ print header_line
+
+ self.print_headers(environment)
+
+ max_critters = max(critter_count.values())
+ winners = [strategy + ' ' for strategy in critter_count.keys()
+ if critter_count[strategy] == max_critters]
+
+ print('winners are %s' % ''.join(winners))
+
+ def print_headers(self, environment):
+ '''Write the headers'''
+ header_line = 'p:\t'
+ for strategy in environment.strategy_counts.keys():
+ header_line += '%s\t' % strategy
+ print header_line
+
+ def print_iteration_line(self, environment):
+ '''Write a reporting line for the iteration with current food values'''
+
+ #work out strategy totals
+ critter_count, food_count = self.calculate_strategy_totals(environment)
+
+ report_line = ('%d:\t' % environment.iteration_no)
+ for strategy in environment.strategy_counts.keys():
+ report_line += '%d\t' % critter_count[strategy]
+ print report_line
+
+ def calculate_strategy_totals(self, environment):
+ food_count = dict()
+ critter_count = dict()
+ for strategy in environment.strategy_counts.keys():
+ food_count[strategy] = 0
+ critter_count[strategy] = 0
+
+ for critter in environment.population:
+ food_count[critter.strategy.short_name] += critter.food
+ critter_count[critter.strategy.short_name] += 1
+
+ return critter_count, food_count
+
+class CritterTracker(EventPlugin):
+ '''Keeps track of an individual critter and summarises their interaction at
+ the conclusion of the environment'''
+
+ def __init__(self, critter_name):
+ self.critter_name = critter_name
+ self.interactions = list()
+ super(CritterTracker, self).__init__()
+
+ def on_environment_end(self, environment):
+ '''
+ Display a summary of what the tracked critter interacted
+ '''
+ UNCOOPERATE = strategies.UNCOOPERATE
+ COOPERATE = strategies.COOPERATE
+
+ print 'Detailed interaction tracking of %s:' % self.critter_name
+
+ for interaction in self.interactions:
+ our_action = interaction[1]
+ their_action = interaction[3]
+ their_name = interaction[2]
+
+ if our_action == UNCOOPERATE and their_action == UNCOOPERATE:
+ outcome_description = 'had no interaction with'
+ elif our_action == COOPERATE and their_action == COOPERATE:
+ outcome_description = 'cooperated with'
+ elif our_action == COOPERATE:
+ outcome_description = 'was suckered by'
+ else:
+ outcome_description = 'cheated'
+
+ print '%s\t%d %d\t%s\t%s %s %s' % (self.critter_name, our_action,
+ their_action, their_name,
+ self.critter_name, outcome_description,
+ their_name)
+
+
+ def on_interaction_end(self, agent1, agent1_outcome, agent2, agent2_outcome):
+ '''called at the end of an interaction between two agents'''
+ if agent1.name == self.critter_name:
+ self.interactions.append((agent1.name,
+ agent1_outcome,
+ agent2.name,
+ agent2_outcome))
+ elif agent2.name == self.critter_name:
+ self.interactions.append((agent2.name,
+ agent2_outcome,
+ agent1.name,
+ agent1_outcome))
+
+
+if __name__ == '__main__':
+ import doctest
+ doctest.testmod()
+
View
53 src/strategies.py
@@ -4,26 +4,31 @@
@author: bendavies
'''
+#constants
+UNCOOPERATE = 0
+COOPERATE = 1
+
class AbstractStrategy(object):
'''A strategy that provides a response to events'''
- UNCOOPERATE = 0
- COOPERATE = 1
-
+ short_name = 'ABS'
+
def interact(self, other_agent):
'''
perform an interaction with another agent and return a result code
if not implemented in subclass, this always returns 0 for the default response
+ >>> from critters import Critter
>>> strategy = AbstractStrategy()
>>> strategy.interact(Critter(None, None))
0
'''
- return AbstractStrategy.UNCOOPERATE
+ return UNCOOPERATE
def observe_interaction(self, me, agent1, agent1_action, agent2, agent2_action):
'''
provides information to the strategy about what transpired in an interaction.
Does not return anything
+ >>> from critters import Critter
>>> strategy = AbstractStrategy()
>>> strategy.observe_interaction(Critter(None, None), Critter(None, None), 1, Critter(None, None), 0)
@@ -33,6 +38,8 @@ def observe_interaction(self, me, agent1, agent1_action, agent2, agent2_action):
class CheatStrategy(AbstractStrategy):
'''A strategy that always fails to cooperate'''
+ short_name = 'CHT'
+
def interact(self, other_agent):
'''
Upon interacting this agent always fails to cooperate (ie return 0)
@@ -40,11 +47,13 @@ def interact(self, other_agent):
>>> taker_strategy.interact(None)
0
'''
- return CheatStrategy.UNCOOPERATE
+ return UNCOOPERATE
class SuckerStrategy(AbstractStrategy):
'''A strategy that never fails to cooperate'''
+ short_name = 'SCK'
+
def interact(self, other_agent):
'''
Upon interacting this agent never fails to cooperate (ie return 1)
@@ -52,11 +61,13 @@ def interact(self, other_agent):
>>> sucker_strategy.interact(None)
1
'''
- return SuckerStrategy.COOPERATE
+ return COOPERATE
class RandomStrategy(AbstractStrategy):
'''A strategy that randomly decides to cooperate or not each time'''
+ short_name = 'RND'
+
def interact(self, other_agent):
'''
Upon interacting this strategy advising a random outcome
@@ -69,17 +80,19 @@ def interact(self, other_agent):
'''
import random
- return random.choice((RandomStrategy.COOPERATE,
- RandomStrategy.UNCOOPERATE))
+ return random.choice((COOPERATE, UNCOOPERATE))
class GrudgerStrategy(AbstractStrategy):
'''
A strategy that never cooperates again with an agent that has failed
to cooperate with this agent in the past
+ >>> from critters import Critter
>>> grudger_strategy = GrudgerStrategy()
>>> grudger = Critter('g1', grudger_strategy)
'''
+ short_name = 'GRD'
+
def __init__(self):
''' constructor'''
super(GrudgerStrategy,self).__init__()
@@ -91,6 +104,7 @@ def interact(self, other_agent):
Upon interacting, cooperate if the other agent has never not cooperated
with this specific agent. If this is the first time of an interaction,
cooperate
+ >>> from critters import Critter
>>> g = Critter('g', GrudgerStrategy())
>>> c1 = Critter('c1', None)
>>> c2 = Critter('c1', None)
@@ -105,13 +119,15 @@ def interact(self, other_agent):
0
'''
if self.agents_to_grudge.count(other_agent.name):
- return GrudgerStrategy.UNCOOPERATE
- return GrudgerStrategy.COOPERATE
+ return UNCOOPERATE
+ else:
+ return COOPERATE
def observe_interaction(self, me, agent1, agent1_action, agent2, agent2_action):
'''
provides information to the strategy about what transpired in an interaction.
Does not return anything
+ >>> from critters import Critter
>>> grudger_strategy = GrudgerStrategy()
>>> grudger = Critter('g1', grudger_strategy)
>>> other_critter1 = Critter('c1', None)
@@ -124,11 +140,11 @@ def observe_interaction(self, me, agent1, agent1_action, agent2, agent2_action):
['c2']
'''
if agent1.name == me.name:
- if agent2_action == GrudgerStrategy.UNCOOPERATE:
+ if agent2_action == UNCOOPERATE:
if self.agents_to_grudge.count(agent2.name) == 0:
self.agents_to_grudge.append(agent2.name)
elif agent2.name == me.name:
- if agent1_action == GrudgerStrategy.UNCOOPERATE:
+ if agent1_action == UNCOOPERATE:
if self.agents_to_grudge.count(agent1.name) == 0:
self.agents_to_grudge.append(agent1.name)
@@ -136,10 +152,13 @@ class TitForTatStrategy(AbstractStrategy):
'''
A strategy that will initially coperate with another agent, but will
punish an agent whose last action was to try to cheat tit for tat
+ >>> from critters import Critter
>>> t4t_strategy = TitForTatStrategy()
>>> t4t = Critter('t1', t4t_strategy)
'''
+ short_name = 'T4T'
+
def __init__(self):
''' constructor'''
super(TitForTatStrategy,self).__init__()
@@ -151,6 +170,7 @@ def interact(self, other_agent):
Upon interacting, cooperate if the other agent was last known to cooperate.
If the other agent last cheated, cheat back
If this is the first time of an interaction cooperate
+ >>> from critters import Critter
>>> t = Critter('t', TitForTatStrategy())
>>> c1 = Critter('c1', None)
>>> c2 = Critter('c1', None)
@@ -167,12 +187,14 @@ def interact(self, other_agent):
'''
if self.last_agent_interaction.has_key(other_agent.name):
return self.last_agent_interaction[other_agent.name]
- return TitForTatStrategy.COOPERATE
+ else:
+ return COOPERATE
def observe_interaction(self, me, agent1, agent1_action, agent2, agent2_action):
'''
provides information to the strategy about what transpired in an interaction.
Does not return anything
+ >>> from critters import Critter
>>> t4t_strategy = TitForTatStrategy()
>>> t4t = Critter('t1', t4t_strategy)
>>> other_critter1 = Critter('c1', None)
@@ -188,3 +210,8 @@ def observe_interaction(self, me, agent1, agent1_action, agent2, agent2_action):
self.last_agent_interaction[agent2.name] = agent2_action
elif agent2.name == me.name:
self.last_agent_interaction[agent1.name] = agent1_action
+
+
+if __name__ == '__main__':
+ import doctest
+ doctest.testmod()
View
184 src/world.py
@@ -3,12 +3,11 @@
@author: bendavies
'''
-import critters, strategies
+import critters, strategies, environment as env
class World(object):
'''
- abstract class for defining an interface for world objects. World objects can be
- configured and run.
+ abstract class for defining the rules and actions of an environment
'''
def __init__(self):
@@ -19,10 +18,11 @@ def __init__(self):
def add_plugin(self, plugin):
'''Adds a plugin
+ >>> import plugins
>>> world = World()
>>> len(world.plugins)
0
- >>> world.add_plugin(EventPlugin())
+ >>> world.add_plugin(plugins.EventPlugin())
>>> len(world.plugins)
1
'''
@@ -36,25 +36,25 @@ def run(self):
return 0
- def send_population_start(self, population):
- '''triggers population start events in registered plugins'''
+ def send_environment_start(self, environment):
+ '''triggers environment start events in registered plugins'''
for plugin in self.plugins:
- plugin.on_population_start(population)
+ plugin.on_environment_start(environment)
- def send_population_end(self, population):
- '''triggers population end events in registered plugins'''
+ def send_environment_end(self, environment):
+ '''triggers environment end events in registered plugins'''
for plugin in self.plugins:
- plugin.on_population_end(population)
+ plugin.on_environment_end(environment)
- def send_iteration_start(self, iteration_no, population):
+ def send_iteration_start(self, environment):
'''sends iteration start events in registered plugins'''
for plugin in self.plugins:
- plugin.on_iteration_start(iteration_no, population)
+ plugin.on_iteration_start(environment)
- def send_iteration_end(self, iteration_no, population):
+ def send_iteration_end(self, environment):
'''sends iteration end events in registered plugins'''
for plugin in self.plugins:
- plugin.on_iteration_end(iteration_no, population)
+ plugin.on_iteration_end(environment)
def send_interaction_end(self, agent1, agent1_outcome, agent2, agent2_outcome):
'''send interaction end events to registered plugins'''
@@ -81,44 +81,53 @@ def run(self, iterations=1500):
print 'simulation is commencing.'
- #create critter population
+ #create environment and critters to populate it
+ environment = env.Environment()
+
sucker = critters.Critter('s1', strategies.SuckerStrategy())
cheater = critters.Critter('c1', strategies.CheatStrategy())
random = critters.Critter('r1', strategies.RandomStrategy())
grudger = critters.Critter('g1', strategies.GrudgerStrategy())
titter = critters.Critter('t1', strategies.TitForTatStrategy())
- population = (sucker, cheater, random, grudger, titter)
- self.send_population_start(population)
+ environment.add_critters((sucker, cheater, random, grudger, titter))
+
+ self.send_environment_start(environment)
#execute iterations
for i in range(1, iterations + 1):
- self.send_iteration_start(i, population)
- self.run_iteration(population)
+ environment.start_iteration()
+
+ self.send_iteration_start(environment)
+
+ self.run_iteration(environment)
- self.send_iteration_end(i, population)
+ self.send_iteration_end(environment)
+
+ environment.end_iteration()
- self.send_population_end(population)
+ self.send_environment_end(environment)
#finish simulation
print('simulation has finished.')
- def run_iteration(self, population):
+ def run_iteration(self, environment):
'''
- runs a single iteration for population. The population itself is updated
+ runs a single iteration for the provided environment. The population itself is updated
in the course of the exection. A population is a list of critters
>>> world = PrisonersDilemmaWorld()
>>> c1 = critters.Critter('c1', strategies.CheatStrategy())
>>> c2 = critters.Critter('c2', strategies.CheatStrategy())
- >>> population = (c1,c2)
- >>> world.run_iteration(population)
- >>> population[1].food
+ >>> environment = env.Environment()
+ >>> environment.add_critters((c1,c2))
+ >>> world.run_iteration(environment)
+ >>> environment.population[1].food
5
'''
- interaction_list = self.determine_interactions(population)
+ interaction_list = self.determine_interactions(environment.population)
#print [(c1.name,c2.name) for (c1,c2) in interaction_list]
for (c1, c2) in interaction_list:
@@ -150,8 +159,8 @@ def interact_critters(self, critter1, critter2):
CHEATER_FOOD = 5
SUCKER_FOOD = -1
- COOPERATE = strategies.AbstractStrategy.COOPERATE
- UNCOOPERATE = strategies.AbstractStrategy.UNCOOPERATE
+ COOPERATE = strategies.COOPERATE
+ UNCOOPERATE = strategies.UNCOOPERATE
critter1_interaction = critter1.interact(critter2)
@@ -187,124 +196,7 @@ def interact_critters(self, critter1, critter2):
#So do plugins!
self.send_interaction_end(critter1,critter1_interaction,
critter2,critter2_interaction)
-
-
-class EventPlugin(object):
- '''An archetype for plugin types'''
-
- def on_population_start(self, population):
- '''called prior to the start of iterations with the initial population'''
- pass
-
- def on_iteration_start(self, iteration_no, population):
- '''called at the start of the iteration'''
- pass
-
- def on_iteration_end(self, iteration_no, population):
- '''called at the end of the iteration'''
- pass
-
- def on_population_end(self, population):
- '''called at the end of the simulation when the population will no longer
- change'''
- pass
-
- def on_interaction_end(self, agent1, agent1_outcome, agent2, agent2_outcome):
- '''called at the end of an interaction between two agents'''
- pass
-
-class TabularReporter(EventPlugin):
- '''Provides basic text output to the standard out'''
- def on_population_start(self, population):
- '''called prior to the start of iterations with the initial population'''
- self.print_headers(population)
-
- def on_iteration_start(self, iteration_no, population):
- '''called at the start of the iteration'''
- pass
-
- def on_iteration_end(self, iteration_no, population):
- '''called at the end of the iteration'''
- if iteration_no == 1 or iteration_no % 5 == 0:
- self.print_iteration_line(iteration_no, population)
-
- def on_population_end(self, population):
- '''called at the end of the simulation when the population will no longer
- change'''
- print 'Final Food Totals'
- self.print_iteration_line(0, population)
- self.print_headers(population)
- max_food = max([critter.food for critter in population])
- winners = [critter.name + ' ' for critter in population
- if (critter.food == max_food)]
- print('winners are %s' % ''.join(winners))
-
- def print_headers(self, population):
- '''Write the headers'''
- header_line = 'p:\t'
- for critter in population:
- header_line += '%s\t' % critter.name
- print header_line
-
- def print_iteration_line(self, iteration_no, population):
- '''Write a reporting line for the iteration with current food values'''
- report_line = ('%d:\t' % iteration_no)
- for critter in population:
- report_line += '%d\t' % critter.food
- print report_line
-
-class CritterTracker(EventPlugin):
- '''Keeps track of an individual critter and summarises their interaction at
- the conclusion of the population'''
-
- def __init__(self, critter_name):
- self.critter_name = critter_name
- self.interactions = list()
- super(CritterTracker, self).__init__()
-
- def on_population_end(self, population):
- '''
- Display a summary of what the tracked critter interacted
- '''
- UNCOOPERATE = strategies.AbstractStrategy.UNCOOPERATE
- COOPERATE = strategies.AbstractStrategy.COOPERATE
-
- print 'Detailed interaction tracking of %s:' % self.critter_name
-
- for interaction in self.interactions:
- our_action = interaction[1]
- their_action = interaction[3]
- their_name = interaction[2]
-
- if our_action == UNCOOPERATE and their_action == UNCOOPERATE:
- outcome_description = 'had no interaction with'
- elif our_action == COOPERATE and their_action == COOPERATE:
- outcome_description = 'cooperated with'
- elif our_action == COOPERATE:
- outcome_description = 'was suckered by'
- else:
- outcome_description = 'cheated'
-
- print '%s\t%d %d\t%s\t%s %s %s' % (self.critter_name, our_action,
- their_action, their_name,
- self.critter_name, outcome_description,
- their_name)
-
-
- def on_interaction_end(self, agent1, agent1_outcome, agent2, agent2_outcome):
- '''called at the end of an interaction between two agents'''
- if agent1.name == self.critter_name:
- self.interactions.append((agent1.name,
- agent1_outcome,
- agent2.name,
- agent2_outcome))
- elif agent2.name == self.critter_name:
- self.interactions.append((agent2.name,
- agent2_outcome,
- agent1.name,
- agent1_outcome))
-
if __name__ == '__main__':
import doctest
Please sign in to comment.
Something went wrong with that request. Please try again.