vixen.py 5.47 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/python -u

#NOTE: The -u option is required for unbuffered stdin/stdout.
#	If stdin/stdout are buffered, the manager program will not recieve any messages and assume that the agent has timed out.

'''
 vixen.py - A sample Stratego AI for the UCC Programming Competition 2012

 Written in python, the slithery language 

 author Sam Moore (matches) [SZM]
 website http://matches.ucc.asn.au/stratego
 email [email protected] or [email protected]
 git git.ucc.asn.au/progcomp2012.git
'''

from basic_python import *
from path import *



class Vixen(BasicAI):
	" Python based AI, improves upon Asmodeus by taking into account probabilities, and common paths "
	def __init__(self):
		#sys.stderr.write("Vixen initialised...\n")
		BasicAI.__init__(self)
		
		
29
30
31
32
		#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}
		#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}
		self.suicideScores = {'1' : -0.5 , '2' : -0.4 , '3' : -0.35, '4' : -0.25, '5' : -0.2, '6' : 0.0, '7' : 0.1, '8' : -1.0 , '9' : 0.0, 's' : -0.4}
		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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
		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}



			

	def MakeMove(self):
		#sys.stderr.write("Vixen MakingMove...\n")
		" Over-rides the default BasicAI.MakeMove function "

		moveList = []
		for unit in self.units:
			if unit.mobile() == False:
				continue

			scores = {"LEFT":0, "RIGHT":0, "UP":0, "DOWN":0}
			

			for target in self.enemyUnits:
				if target == unit:
					continue
				path = PathFinder().pathFind((unit.x, unit.y), (target.x, target.y), self.board)
				if path == False or len(path) == 0:
					continue
57
58
				#moveList.append({"unit":unit, "direction":path[0], "score":self.CalculateScore(unit, target, path)})
				scores[path[0]] += self.CalculateScore(unit, target, path)
59

60
61
			bestScore = sorted(scores.items(), key = lambda e : e[1], reverse=True)[0]
			moveList.append({"unit":unit, "direction":bestScore[0], "score":bestScore[1]})
62
63
64
65
66
67
68
			

		if len(moveList) == 0:
			print "NO_MOVE"
			return True

		moveList.sort(key = lambda e : e["score"], reverse=True)
69
		#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")
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
		if moveList[0]["score"] == 0:
			print "NO_MOVE"
			return True

		
		print str(moveList[0]["unit"].x) + " " + str(moveList[0]["unit"].y) + " " + moveList[0]["direction"]
		return True
				
			
	def tailFactor(self, pathLength):
		#if pathLength >= len(self.tailFactors) or pathLength <= 0:
		#	return 0.0
		#return self.tailFactors[pathLength]
		#return 0.5 * (1.0 + pow(pathLength, 0.75))
		return 1.0 / pathLength


	def CalculateScore(self, attacker, defender, path):
88
89
90
		p = move(attacker.x, attacker.y, path[0], 1)
		

91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
		total = 0.0
		count = 0.0
		for rank in ranks:
			prob = self.rankProbability(defender, rank)			
			if prob > 0.0:
				#sys.stderr.write("	" + str(attacker.rank) + " vs. " + str(rank) + " [" + str(prob) + "] score " + str(self.combatScore(attacker.rank, rank, len(path))) + "\n")
				total += prob * self.combatScore(attacker.rank, rank, len(path))
				count += 1
				
		
		#if count > 1:
		#	total = total / count + self.riskScore(attacker.rank)


		total = total * self.tailFactor(len(path))
106
107
108
		#HACK - Prevent "oscillating" by decreasing the value of backtracks
		if len(path) > 1 and len(attacker.positions) > 1 and attacker.positions[1][0] == p[0] and attacker.positions[1][1] == p[1]:
			total = total / 100
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
		#sys.stderr.write("Total score for " + str(attacker) + " vs. " + str(defender) + " is " + str(total) + "\n")
		return total

	def combatScore(self, attackerRank, defenderRank, pathLength):
		if defenderRank == 'F':
			return 1.0
		elif defenderRank == 'B':
			return self.bombScore(attackerRank)
		elif defenderRank == 's' and attackerRank == '1' and pathLength == 2:
			return self.suicideScore(attackerRank)
		elif defenderRank == '1' and attackerRank == 's' and pathLength != 2:
			return self.killScore(attackerRank)

		if valuedRank(attackerRank) > valuedRank(defenderRank):
			return self.killScore(defenderRank)
		elif valuedRank(attackerRank) < valuedRank(defenderRank):
			return self.suicideScore(attackerRank)
		return self.killScore(defenderRank) + self.suicideScore(attackerRank)

	def killScore(self, defenderRank):
		return self.killScores[defenderRank]

	def bombScore(self, attackerRank):
132
133
134
135
		if attackerRank == '8':
			return 1.0
		else:
			return 0.0
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

	def suicideScore(self, attackerRank):
		return self.suicideScores[attackerRank]

	def riskScore(self, attackerRank):
		return self.riskScores[attackerRank]

	def rankProbability(self, target, targetRank):
		if targetRank == '+' or targetRank == '?':
			return 0.0
		if target.rank == targetRank:
			return 1.0
		elif target.rank != '?':
			return 0.0

		total = 0.0
		for rank in ranks:
			if rank == '+' or rank == '?':
				continue
			elif rank == 'F' or rank == 'B':
				if target.lastMoved < 0:
					total += self.hiddenEnemies[rank]
			else:
				total += self.hiddenEnemies[rank]

		if total == 0.0:
			return 0.0
		return float(float(self.hiddenEnemies[targetRank]) / float(total))
	
165

166
167
168
169
170
171
172
173
174
175
176
	

				
				
		
if __name__ == "__main__":
	vixen = Vixen()
	if vixen.Setup():
		while vixen.MoveCycle():
			pass