Oh god, I was assuming scores were integers
[progcomp2012.git] / 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 import random
21
22
23 class Vixen(BasicAI):
24         " Python based AI, improves upon Asmodeus by taking into account probabilities, and common paths "
25         def __init__(self):
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.bombScores = {'1' : -0.9 , '2' : -0.8 , '3' : -0.5 , '4' : -0.5, '5' : -0.4, '6' : -0.5, '7' : -0.2, '8' : 1.0 , '9' : -0.1, 's' : -0.2}
31                 self.suicideScores = {'1' : -0.8 , '2' : -0.6 , '3' : -0.5, '4' : -0.25, '5' : -0.2, '6' : 0.0, '7' : 0.1, '8' : -1.0 , '9' : 0.0, 's' : -0.4}
32                 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' : 1.0}    
33                 self.riskScores = {'1' : -0.3, '2' : -0.3, '3' : 0.0, '4': 0.4, '5': 0.6, '6': 0.7, '7':0.8, '8': 0.0, '9' : 1.0, 's' : 0.1}
34
35
36         def Setup(self):
37                 """ Implements Setup part of protocol. Always uses the same setup. Override to create custom setups """
38                 #sys.stderr.write("BasicAI Setup here...\n");
39                 setup = sys.stdin.readline().split(' ')
40                 if len(setup) != 4:
41                         sys.stderr.write("BasicAI setup fails, expected 4 tokens, got " + str(len(setup)) + " "+str(setup) + "\n")
42                 self.colour = setup[0]
43                 self.opponentName = setup[1]
44                 self.width = int(setup[2])
45                 self.height = int(setup[3])
46                 for x in range(0, self.width):
47                         self.board.append([])
48                         for y in range(0, self.height):         
49                                 self.board[x].append(None)
50
51                 #flagPosition = random.choice((
52                 #fakeFlag = random.choice((
53                 if self.colour == "RED":
54                         print "FB8sB479B8\nBB31555583\n6724898974\n967B669999"
55                 elif self.colour == "BLUE":
56                         print "967B669999\n6724898974\nBB31555583\nFB8sB479B8"
57                 return True
58                         
59
60         def MakeMove(self):
61                 #sys.stderr.write("Vixen MakingMove...\n")
62                 " Over-rides the default BasicAI.MakeMove function "
63
64                 moveList = []
65                 for unit in self.units:
66                         if unit.mobile() == False:
67                                 continue
68
69                         scores = {"LEFT":None, "RIGHT":None, "UP":None, "DOWN":None}
70                         
71                         for target in self.enemyUnits:
72                                 if target == unit:
73                                         continue
74                                 path = PathFinder().pathFind((unit.x, unit.y), (target.x, target.y), self.board)
75                                 if path == False or len(path) == 0:
76                                         continue
77                                 if scores[path[0]] == None:
78                                         scores[path[0]] = 0 
79
80                                 scores[path[0]] += self.CalculateScore(unit, target, path)
81
82                         for d in scores.keys():
83                                 if scores[d] == None:
84                                         del scores[d]
85
86                         if len(scores.items()) > 0: 
87                                 bestScore = sorted(scores.items(), key = lambda e : e[1], reverse=True)[0]
88                                 moveList.append({"unit":unit, "direction":bestScore[0], "score":bestScore[1]})
89                         
90                         
91
92                 if len(moveList) <= 0:
93                         print "NO_MOVE"
94                         return True
95
96                 moveList.sort(key = lambda e : e["score"], reverse=True)
97                 #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")
98                 #if moveList[0]["score"] == 0:
99                 #       print "NO_MOVE"
100                 #       return True
101
102                 
103                 print str(moveList[0]["unit"].x) + " " + str(moveList[0]["unit"].y) + " " + moveList[0]["direction"]
104                 return True
105                                 
106                         
107         def tailFactor(self, pathLength):
108                 #if pathLength >= len(self.tailFactors) or pathLength <= 0:
109                 #       return 0.0
110                 #return self.tailFactors[pathLength]
111                 #return 0.5 * (1.0 + pow(pathLength, 0.75))
112                 return 1.0 / pathLength
113
114
115         def CalculateScore(self, attacker, defender, path):
116                 p = move(attacker.x, attacker.y, path[0], 1)
117                 if p[0] < 0 or p[0] >= len(self.board) or p[1] < 0 or p[1] >= len(self.board[p[0]]):
118                         return -1000.0
119
120                 total = 0.0
121                 count = 0.0
122                 for rank in ranks:
123                         prob = self.rankProbability(defender, rank)                     
124                         if prob > 0.0:
125                                 #sys.stderr.write("     " + str(attacker.rank) + " vs. " + str(rank) + " [" + str(prob) + "] score " + str(self.combatScore(attacker.rank, rank, len(path))) + "\n")
126                                 total += prob * self.combatScore(attacker.rank, rank, len(path))
127                                 count += 1
128                                 
129                 
130                 #if count > 1:
131                 #       total = total / count + self.riskScore(attacker.rank)
132
133
134                 total = total * self.tailFactor(len(path))
135                 #HACK - Prevent "oscillating" by decreasing the value of backtracks
136                 if len(path) > 1 and len(attacker.positions) > 1 and attacker.positions[1][0] == p[0] and attacker.positions[1][1] == p[1]:
137                         total = total / 100
138                 #sys.stderr.write("Total score for " + str(attacker) + " vs. " + str(defender) + " is " + str(total) + "\n")
139                 return total
140
141         def combatScore(self, attackerRank, defenderRank, pathLength):
142                 if defenderRank == 'F':
143                         return 1.0
144                 elif defenderRank == 'B':
145                         return self.bombScore(attackerRank)
146                 elif defenderRank == 's' and attackerRank == '1' and pathLength == 2:
147                         return self.suicideScore(attackerRank)
148                 elif defenderRank == '1' and attackerRank == 's' and pathLength != 2:
149                         return self.killScore(attackerRank)
150
151                 if valuedRank(attackerRank) > valuedRank(defenderRank):
152                         return self.killScore(defenderRank)
153                 elif valuedRank(attackerRank) < valuedRank(defenderRank):
154                         return self.suicideScore(attackerRank)
155                 return self.killScore(defenderRank) + self.suicideScore(attackerRank)
156
157         def killScore(self, defenderRank):
158                 return self.killScores[defenderRank]
159
160         def bombScore(self, attackerRank):
161                 if attackerRank == '8':
162                         return 1.0
163                 else:
164                         return self.suicideScore(attackerRank)
165
166         def suicideScore(self, attackerRank):
167                 return self.suicideScores[attackerRank]
168
169         def riskScore(self, attackerRank):
170                 return self.riskScores[attackerRank]
171
172         def rankProbability(self, target, targetRank):
173                 if targetRank == '+' or targetRank == '?':
174                         return 0.0
175                 if target.rank == targetRank:
176                         return 1.0
177                 elif target.rank != '?':
178                         return 0.0
179
180                 total = 0.0
181                 for rank in ranks:
182                         if rank == '+' or rank == '?':
183                                 continue
184                         elif rank == 'F' or rank == 'B':
185                                 if target.lastMoved < 0:
186                                         total += self.hiddenEnemies[rank]
187                         else:
188                                 total += self.hiddenEnemies[rank]
189
190                 if total == 0.0:
191                         return 0.0
192                 return float(float(self.hiddenEnemies[targetRank]) / float(total))
193         
194
195         
196
197                                 
198                                 
199                 
200 if __name__ == "__main__":
201         vixen = Vixen()
202         if vixen.Setup():
203                 while vixen.MoveCycle():
204                         pass
205

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