+++ /dev/null
-'''uccProgComp.py - A supervisor candidate for Rock Paper Scissors.
-Written by Luke Williams <
[email protected]> for the UCC Programming Competition in 2008.
-Requires Python 2.5.
-
-Licensed under an MIT-style license: see the LICENSE file for details.
-'''
-
-
-import random, uuid
-random.seed ()
-
-from rpsconst import *
-
-DEFAULT_HEALTH = 50
-REPRODUCE_HEALTH = 100
-DIE_HEALTH = 0
-MAX_AGE = 100
-
-DEBUG_MODE = False
-
-# Game dynamics - these are not final:
-# WINNER TRUTH ATTPoints, DEFPoints
-pointsTable [Attacker] [False] = (2, -2)
-pointsTable [Attacker] [True] = (2, -2)
-pointsTable [Defender] [False] = (-2, 2)
-pointsTable [Defender] [True] = (-2, 2)
-pointsTable [Tie] [False] = (0, 0)
-pointsTable [Tie] [True] = (1, 1)
-
-def Debug (f):
- def g (*args):
- if DEBUG_MODE:
- print f.__name__, args[1].__class__.__name__, args[1].GetID ()
- return f (*args)
- return g
-
-class BaseAgent:
- def __init__ (self):
- self.id = uuid.uuid4().int
- self.__points = DEFAULT_HEALTH
- # The index will be changing all the time. It can go stale as soon as something dies.
- # So use it cautiously.
- self.__currentIndex = 0
- self.__reproduced = False
- self.__age = 0
-
- def GetCurrentIndex (self):
- return self.__currentIndex
-
- def SetCurrentIndex (self, index):
- self.__currentIndex = index
-
- def GetID (self):
- return self.id
-
- def GetPoints (self):
- return self.__points
-
- def SetPoints (self, points):
- self.__points = points
-
- def Defend (self, foe, bluff):
- return Rock
-
- def Attack (self, foe):
- return Rock
-
- def IsDead (self):
- return self.__points <= DIE_HEALTH
-
- def Reproduced (self):
- self.__points = DEFAULT_HEALTH
- self.__reproduced = True
-
- def HasReproduced (self):
- return self.__reproduced
-
- def SetReproduced (self, repro):
- self.__reproduced = repro
-
- def Results (self, foeName, wasAttacker, winner, attItem, defItem, bluffItem, pointDelta):
- self.__points += pointDelta
- self.__age += 1
- if self.__age > MAX_AGE: self.__points = DIE_HEALTH
-
-class LearningAgent (BaseAgent):
- def __init__ (self):
- BaseAgent.__init__ (self)
- self.winHistory = {}
-
- def Results (self, foeName, wasAttacker, winner, attItem, defItem, bluffItem, pointDelta):
- BaseAgent.Results (self, foeName, wasAttacker, winner, attItem, defItem, bluffItem, pointDelta)
- if wasAttacker:
- if winner == Attacker: result = Win
- elif winner == Tie: result = Tie
- else: result = Loss
- else:
- if winner == Attacker: result = Loss
- elif winner == Tie: result = Tie
- else: result = Win
-
- if foeName in self.winHistory: self.winHistory [foeName].append (result)
- else: self.winHistory [foeName] = [result]
-
- def GetWinHistory (self, foeName):
- if foeName in self.winHistory: return self.winHistory [foeName]
- else: return []
-
-class Supervisor:
- def __init__ (self):
- # The full list of living agents
- self.population = []
- # A list of classes for each agent type
- self.agentTypes = []
- # The current iteration
- self.iteration = 0
- self.agentStats = {}
- self.pendingDeaths = []
-
- def RegisterAgent (self, agent):
- self.agentTypes.append (agent)
-
- def GeneratePopulation (self, nAgentsPerClass):
- for Agent in self.agentTypes:
- for i in range (0,nAgentsPerClass): self.population.append (Agent ())
- self.agentStats [str(Agent)] = [nAgentsPerClass,0,0]
-
- def Iterate (self):
- self.ClearStats ()
- self.UpdateIndexes ()
- self.iteration += 1
- for attacker, defender in self.Select ():
- attack, bluff = attacker.Attack (defender.GetID ())
- defense = defender.Defend (attacker.GetID (), bluff)
- winner = resultTable [attack] [defense]
- attPoints, defPoints = pointsTable [winner][attack == bluff]
- attacker.Results (defender.GetID (), True, winner, attack, defense, bluff, attPoints)
- defender.Results (attacker.GetID (), False, winner, attack, defense, bluff, defPoints)
- if attacker.IsDead (): self.KillAgent (attacker)
- elif attacker.GetPoints () >= REPRODUCE_HEALTH: self.SpawnAgent (attacker)
- if defender.IsDead (): self.KillAgent (defender)
- elif defender.GetPoints () >= REPRODUCE_HEALTH: self.SpawnAgent (defender)
-
- def IsGameOver (self):
- if self.population == []: return True
- liveAgents = [id for id,stats in self.agentStats.iteritems () if stats[0] > 0]
- print liveAgents
- if len(liveAgents) < 2: return True
- return False
-
- # This is needed because when we pick the players we also need a way of identifying them in the
- # population list without manually searching each time. O(n) each iteration is better than O(n)
- # each death. It also resets the check for if the agent has reproduced this round.
- def UpdateIndexes (self):
- for agentID in reversed(sorted(self.pendingDeaths)): del self.population [agentID]
- for index, agent in enumerate(self.population):
- agent.SetCurrentIndex (index)
- agent.SetReproduced (False)
- self.pendingDeaths = []
-
- @Debug
- def KillAgent (self, agent):
- self.pendingDeaths.append (agent.GetCurrentIndex ())
- stat = self.agentStats [str(agent.__class__)]
- stat [0] -= 1
- stat [2] += 1
-
- @Debug
- def SpawnAgent (self, agent):
- child = agent.__class__ ()
- self.population.append (child)
- agent.Reproduced ()
- stat = self.agentStats [str(agent.__class__)]
- stat [0] += 1
- stat [1] += 1
-
- def Select (self):
- # This approach causes agents to keep fighting until they've either died or reproduced.
- remaining = self.population[:]
- attackerID = defenderID = random.randint (0,len(remaining)-1)
- attacker = defender = remaining [attackerID]
- while len (remaining) >= 2:
- # Check to see if the attacker from last round needs to be dropped.
- if attacker.IsDead () or attacker.HasReproduced ():
- remaining.pop (attackerID)
- if not len(remaining) >= 2: continue
- if defenderID > attackerID: defenderID -= 1
- # Check to see if the defender from last round is up for some attacking.
- if defender.IsDead () or defender.HasReproduced ():
- remaining.pop (defenderID)
- if not len(remaining) >= 2: continue
- attackerID = random.randint (0,len(remaining)-1)
- attacker = remaining [attackerID]
- else:
- attacker = defender
- attackerID = defenderID
- defender = None
- defenderID = random.randint (0,len(remaining)-2)
- if defenderID >= attackerID: defenderID += 1
- defender = remaining [defenderID]
-
- yield attacker, defender
-
- def GetStats (self):
- return self.agentStats
-
- def ClearStats (self):
- for agent in self.agentTypes: self.agentStats [str(agent)] = self.agentStats [str(agent)] [:1] + [0,0]
-
-def RandomAttack ():
- return random.randint (0,2)