1 '''uccProgComp.py - A supervisor candidate for Rock Paper Scissors.
2 Written by Luke Williams <
[email protected]> for the UCC Programming Competition in 2008.
5 Licensed under an MIT-style license: see the LICENSE file for details.
12 from rpsconst import *
18 print f.__name__, args[1].__class__.__name__, args[1].GetID ()
24 self.id = uuid.uuid4().int
25 self.__points = DEFAULT_HEALTH
26 # The index will be changing all the time. It can go stale as soon as something dies.
27 # So use it cautiously.
28 self.__currentIndex = 0
29 self.__reproduced = False
32 def GetCurrentIndex (self):
33 return self.__currentIndex
35 def SetCurrentIndex (self, index):
36 self.__currentIndex = index
44 def SetPoints (self, points):
45 self.__points = points
47 def Defend (self, foe, bluff):
50 def Attack (self, foe):
54 return self.__points <= DIE_HEALTH
56 def Reproduced (self):
57 self.__points = DEFAULT_HEALTH
58 self.__reproduced = True
60 def HasReproduced (self):
61 return self.__reproduced
63 def SetReproduced (self, repro):
64 self.__reproduced = repro
66 def Results (self, foeName, wasAttacker, winner, attItem, defItem, bluffItem, pointDelta):
67 self.__points += pointDelta
69 if self.__age > MAX_AGE: self.__points = DIE_HEALTH
71 class LearningAgent (BaseAgent):
73 BaseAgent.__init__ (self)
76 def Results (self, foeName, wasAttacker, winner, attItem, defItem, bluffItem, pointDelta):
77 BaseAgent.Results (self, foeName, wasAttacker, winner, attItem, defItem, bluffItem, pointDelta)
79 if winner == Attacker: result = Win
80 elif winner == Tie: result = Tie
83 if winner == Attacker: result = Loss
84 elif winner == Tie: result = Tie
87 if foeName in self.winHistory: self.winHistory [foeName].append (result)
88 else: self.winHistory [foeName] = [result]
90 def GetWinHistory (self, foeName):
91 if foeName in self.winHistory: return self.winHistory [foeName]
96 # The full list of living agents
98 # A list of classes for each agent type
100 # The current iteration
103 self.pendingDeaths = []
105 def RegisterAgent (self, agent):
106 self.agentTypes.append (agent)
108 def GeneratePopulation (self, nAgentsPerClass):
109 for Agent in self.agentTypes:
110 for i in range (0,nAgentsPerClass): self.population.append (Agent ())
111 self.agentStats [str(Agent)] = [nAgentsPerClass,0,0]
112 if DEBUG: print "Created " + str(nAgentsPerClass) + " instances of " + Agent.__name__
116 self.UpdateIndexes ()
118 for attacker, defender in self.Select ():
119 attack, bluff = attacker.Attack (defender.GetID ())
120 defense = defender.Defend (attacker.GetID (), bluff)
121 winner = resultTable [attack] [defense]
122 attPoints, defPoints = pointsTable [winner][attack == bluff]
123 attacker.Results (defender.GetID (), True, winner, attack, defense, bluff, attPoints)
124 defender.Results (attacker.GetID (), False, winner, attack, defense, bluff, defPoints)
125 if attacker.IsDead (): self.KillAgent (attacker)
126 elif attacker.GetPoints () >= REPRODUCE_HEALTH: self.SpawnAgent (attacker)
127 if defender.IsDead (): self.KillAgent (defender)
128 elif defender.GetPoints () >= REPRODUCE_HEALTH: self.SpawnAgent (defender)
130 def IsGameOver (self):
131 if self.population == []: return True
132 liveAgents = [id for id,stats in self.agentStats.iteritems () if stats[0] > 0]
134 if len(liveAgents) < 2: return True
137 # This is needed because when we pick the players we also need a way of identifying them in the
138 # population list without manually searching each time. O(n) each iteration is better than O(n)
139 # each death. It also resets the check for if the agent has reproduced this round.
140 def UpdateIndexes (self):
141 for agentID in reversed(sorted(self.pendingDeaths)): del self.population [agentID]
142 for index, agent in enumerate(self.population):
143 agent.SetCurrentIndex (index)
144 agent.SetReproduced (False)
145 self.pendingDeaths = []
148 def KillAgent (self, agent):
149 self.pendingDeaths.append (agent.GetCurrentIndex ())
150 stat = self.agentStats [str(agent.__class__)]
155 def SpawnAgent (self, agent):
156 child = agent.__class__ ()
157 self.population.append (child)
159 stat = self.agentStats [str(agent.__class__)]
164 # This approach causes agents to keep fighting until they've either died or reproduced.
165 remaining = self.population[:]
166 attackerID = defenderID = random.randint (0,len(remaining)-1)
167 attacker = defender = remaining [attackerID]
168 while len (remaining) >= 2:
169 # Check to see if the attacker from last round needs to be dropped.
170 if attacker.IsDead () or attacker.HasReproduced ():
171 remaining.pop (attackerID)
172 if not len(remaining) >= 2: continue
173 if defenderID > attackerID: defenderID -= 1
174 # Check to see if the defender from last round is up for some attacking.
175 if defender.IsDead () or defender.HasReproduced ():
176 remaining.pop (defenderID)
177 if not len(remaining) >= 2: continue
178 attackerID = random.randint (0,len(remaining)-1)
179 attacker = remaining [attackerID]
182 attackerID = defenderID
184 defenderID = random.randint (0,len(remaining)-2)
185 if defenderID >= attackerID: defenderID += 1
186 defender = remaining [defenderID]
188 yield attacker, defender
191 return self.agentStats
193 def ClearStats (self):
194 for agent in self.agentTypes: self.agentStats [str(agent)] = self.agentStats [str(agent)] [:1] + [0,0]
197 return random.randint (0,2)