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.
7 vixen.py - A sample Stratego AI for the UCC Programming Competition 2012
9 Written in python, the slithery language
11 author Sam Moore (matches) [SZM]
12 website http://matches.ucc.asn.au/stratego
14 git git.ucc.asn.au/progcomp2012.git
17 from basic_python import *
23 " Python based AI, improves upon Asmodeus by taking into account probabilities, and common paths "
25 BasicAI.__init__(self)
28 #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}
29 #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}
30 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}
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' : 1.0}
32 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}
39 #sys.stderr.write("Vixen MakingMove...\n")
40 " Over-rides the default BasicAI.MakeMove function "
43 for unit in self.units:
44 if unit.mobile() == False:
47 scores = {"LEFT":None, "RIGHT":None, "UP":None, "DOWN":None}
49 for target in self.enemyUnits:
52 path = PathFinder().pathFind((unit.x, unit.y), (target.x, target.y), self.board)
53 if path == False or len(path) == 0:
55 if scores[path[0]] == None:
58 scores[path[0]] += self.CalculateScore(unit, target, path)
60 for d in scores.keys():
64 if len(scores.items()) > 0:
65 bestScore = sorted(scores.items(), key = lambda e : e[1], reverse=True)[0]
66 moveList.append({"unit":unit, "direction":bestScore[0], "score":bestScore[1]})
70 if len(moveList) <= 0:
74 moveList.sort(key = lambda e : e["score"], reverse=True)
75 #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")
76 #if moveList[0]["score"] == 0:
81 print str(moveList[0]["unit"].x) + " " + str(moveList[0]["unit"].y) + " " + moveList[0]["direction"]
85 def tailFactor(self, pathLength):
86 #if pathLength >= len(self.tailFactors) or pathLength <= 0:
88 #return self.tailFactors[pathLength]
89 #return 0.5 * (1.0 + pow(pathLength, 0.75))
90 return 1.0 / pathLength
93 def CalculateScore(self, attacker, defender, path):
94 p = move(attacker.x, attacker.y, path[0], 1)
95 if p[0] < 0 or p[0] >= len(self.board) or p[1] < 0 or p[1] >= len(self.board[p[0]]):
101 prob = self.rankProbability(defender, rank)
103 #sys.stderr.write(" " + str(attacker.rank) + " vs. " + str(rank) + " [" + str(prob) + "] score " + str(self.combatScore(attacker.rank, rank, len(path))) + "\n")
104 total += prob * self.combatScore(attacker.rank, rank, len(path))
109 # total = total / count + self.riskScore(attacker.rank)
112 total = total * self.tailFactor(len(path))
113 #HACK - Prevent "oscillating" by decreasing the value of backtracks
114 if len(path) > 1 and len(attacker.positions) > 1 and attacker.positions[1][0] == p[0] and attacker.positions[1][1] == p[1]:
116 #sys.stderr.write("Total score for " + str(attacker) + " vs. " + str(defender) + " is " + str(total) + "\n")
119 def combatScore(self, attackerRank, defenderRank, pathLength):
120 if defenderRank == 'F':
122 elif defenderRank == 'B':
123 return self.bombScore(attackerRank)
124 elif defenderRank == 's' and attackerRank == '1' and pathLength == 2:
125 return self.suicideScore(attackerRank)
126 elif defenderRank == '1' and attackerRank == 's' and pathLength != 2:
127 return self.killScore(attackerRank)
129 if valuedRank(attackerRank) > valuedRank(defenderRank):
130 return self.killScore(defenderRank)
131 elif valuedRank(attackerRank) < valuedRank(defenderRank):
132 return self.suicideScore(attackerRank)
133 return self.killScore(defenderRank) + self.suicideScore(attackerRank)
135 def killScore(self, defenderRank):
136 return self.killScores[defenderRank]
138 def bombScore(self, attackerRank):
139 if attackerRank == '8':
142 return self.suicideScore(attackerRank)
144 def suicideScore(self, attackerRank):
145 return self.suicideScores[attackerRank]
147 def riskScore(self, attackerRank):
148 return self.riskScores[attackerRank]
150 def rankProbability(self, target, targetRank):
151 if targetRank == '+' or targetRank == '?':
153 if target.rank == targetRank:
155 elif target.rank != '?':
160 if rank == '+' or rank == '?':
162 elif rank == 'F' or rank == 'B':
163 if target.lastMoved < 0:
164 total += self.hiddenEnemies[rank]
166 total += self.hiddenEnemies[rank]
170 return float(float(self.hiddenEnemies[targetRank]) / float(total))
178 if __name__ == "__main__":
181 while vixen.MoveCycle():