fc13328e207279a37aeb75f1cce3565aacebdb79
[progcomp2012.git] / progcomp / agents / vixen / vixen.py
1 #!/usr/bin/python -u
2
3 #NOTE: The -u option is required for unbuffered stdin/stdout.
4 #       If stdin/stdout are buffered, the manager program will not recieve any messages and assume that the agent has timed out.
5
6 '''
7  vixen.py - A sample Stratego AI for the UCC Programming Competition 2012
8
9  Written in python, the slithery language 
10
11  author Sam Moore (matches) [SZM]
12  website http://matches.ucc.asn.au/stratego
13  email [email protected] or [email protected]
14  git git.ucc.asn.au/progcomp2012.git
15 '''
16
17 from basic_python import *
18 from path import *
19
20
21
22 class Vixen(BasicAI):
23         " Python based AI, improves upon Asmodeus by taking into account probabilities, and common paths "
24         def __init__(self):
25                 #sys.stderr.write("Vixen initialised...\n")
26                 BasicAI.__init__(self)
27                 
28                 
29                 self.bombScores = {'1' : -0.9 , '2' : -0.8 , '3' : -0.5 , '4' : 0.1, '5' : 0.1, '6' : 0.3, '7' : 0.7, '8' : 1 , '9' : 0.6, 's' : 0}
30                 self.suicideScores = {'1' : -0.5 , '2' : -0.4 , '3' : -0.35, '4' : -0.25, '5' : -0.2, '6' : 0.0, '7' : 0.1, '8' : -0.4 , '9' : 0.0, 's' : -0.4}
31                 self.killScores = {'1' : 1.0 , '2' : 0.9 , '3' : 0.9 , '4' : 0.8, '5' : 0.8, '6' : 0.8, '7' : 0.8, '8' : 0.9 , '9' : 0.7, 's' : 0.9}    
32                 self.riskScores = {'1' : 0.0, '2' : 0.1, '3' : 0.2, '4': 0.4, '5': 0.6, '6': 0.7, '7':0.8, '8': 0.0, '9' : 1.0, 's' : 0.1}
33
34
35
36                         
37
38         def MakeMove(self):
39                 #sys.stderr.write("Vixen MakingMove...\n")
40                 " Over-rides the default BasicAI.MakeMove function "
41
42                 moveList = []
43                 for unit in self.units:
44                         if unit.mobile() == False:
45                                 continue
46
47                         scores = {"LEFT":0, "RIGHT":0, "UP":0, "DOWN":0}
48                         
49
50                         for target in self.enemyUnits:
51                                 if target == unit:
52                                         continue
53                                 path = PathFinder().pathFind((unit.x, unit.y), (target.x, target.y), self.board)
54                                 if path == False or len(path) == 0:
55                                         continue
56                                 moveList.append({"unit":unit, "direction":path[0], "score":self.CalculateScore(unit, target, path)})
57                                 #scores[path[0]] += self.CalculateScore(unit, target, path)
58
59                         #bestScore = sorted(scores.items(), key = lambda e : e[1], reverse=True)[0]
60                         #moveList.append({"unit":unit, "direction":bestScore[0], "score":bestScore[1]})
61                         
62
63                 if len(moveList) == 0:
64                         print "NO_MOVE"
65                         return True
66
67                 moveList.sort(key = lambda e : e["score"], reverse=True)
68                 sys.stderr.write("vixen - best move: " + str(moveList[0]["unit"].x) + " " + str(moveList[0]["unit"].y) + " " + moveList[0]["direction"] + " [ score = " + str(moveList[0]["score"]) + " ]\n")
69                 if moveList[0]["score"] == 0:
70                         print "NO_MOVE"
71                         return True
72
73                 
74                 print str(moveList[0]["unit"].x) + " " + str(moveList[0]["unit"].y) + " " + moveList[0]["direction"]
75                 return True
76                                 
77                         
78         def tailFactor(self, pathLength):
79                 #if pathLength >= len(self.tailFactors) or pathLength <= 0:
80                 #       return 0.0
81                 #return self.tailFactors[pathLength]
82                 #return 0.5 * (1.0 + pow(pathLength, 0.75))
83                 return 1.0 / pathLength
84
85
86         def CalculateScore(self, attacker, defender, path):
87                 total = 0.0
88                 count = 0.0
89                 for rank in ranks:
90                         prob = self.rankProbability(defender, rank)                     
91                         if prob > 0.0:
92                                 #sys.stderr.write("     " + str(attacker.rank) + " vs. " + str(rank) + " [" + str(prob) + "] score " + str(self.combatScore(attacker.rank, rank, len(path))) + "\n")
93                                 total += prob * self.combatScore(attacker.rank, rank, len(path))
94                                 count += 1
95                                 
96                 
97                 #if count > 1:
98                 #       total = total / count + self.riskScore(attacker.rank)
99
100
101                 total = total * self.tailFactor(len(path))
102                 #sys.stderr.write("Total score for " + str(attacker) + " vs. " + str(defender) + " is " + str(total) + "\n")
103                 return total
104
105         def combatScore(self, attackerRank, defenderRank, pathLength):
106                 if defenderRank == 'F':
107                         return 1.0
108                 elif defenderRank == 'B':
109                         return self.bombScore(attackerRank)
110                 elif defenderRank == 's' and attackerRank == '1' and pathLength == 2:
111                         return self.suicideScore(attackerRank)
112                 elif defenderRank == '1' and attackerRank == 's' and pathLength != 2:
113                         return self.killScore(attackerRank)
114
115                 if valuedRank(attackerRank) > valuedRank(defenderRank):
116                         return self.killScore(defenderRank)
117                 elif valuedRank(attackerRank) < valuedRank(defenderRank):
118                         return self.suicideScore(attackerRank)
119                 return self.killScore(defenderRank) + self.suicideScore(attackerRank)
120
121         def killScore(self, defenderRank):
122                 return self.killScores[defenderRank]
123
124         def bombScore(self, attackerRank):
125                 return self.bombScores[attackerRank]
126
127         def suicideScore(self, attackerRank):
128                 return self.suicideScores[attackerRank]
129
130         def riskScore(self, attackerRank):
131                 return self.riskScores[attackerRank]
132
133         def rankProbability(self, target, targetRank):
134                 if targetRank == '+' or targetRank == '?':
135                         return 0.0
136                 if target.rank == targetRank:
137                         return 1.0
138                 elif target.rank != '?':
139                         return 0.0
140
141                 total = 0.0
142                 for rank in ranks:
143                         if rank == '+' or rank == '?':
144                                 continue
145                         elif rank == 'F' or rank == 'B':
146                                 if target.lastMoved < 0:
147                                         total += self.hiddenEnemies[rank]
148                         else:
149                                 total += self.hiddenEnemies[rank]
150
151                 if total == 0.0:
152                         return 0.0
153                 return float(float(self.hiddenEnemies[targetRank]) / float(total))
154         
155         def InterpretResult(self):
156                 """ Over-ride the basic AI interpret result so we can update probabilities """
157                 if BasicAI.InterpretResult(self) == False:
158                         return False
159                 
160                 return True
161         
162
163                                 
164                                 
165                 
166 if __name__ == "__main__":
167         vixen = Vixen()
168         if vixen.Setup():
169                 while vixen.MoveCycle():
170                         pass
171

UCC git Repository :: git.ucc.asn.au