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 basic_python.py - A sample Stratego AI for the UCC Programming Competition 2012
9 Written in python, the slithery language
10 Simply makes random moves, as long as possible
12 author Sam Moore (matches) [SZM]
13 website http://matches.ucc.asn.au/stratego
15 git git.ucc.asn.au/progcomp2012.git
21 ranks = ['B','1','2','3','4','5','6','7','8','9','s','F', '?', '+']
24 """ Using exceptions for this feels... wrong..."""
31 def move(x, y, direction, multiplier):
32 """ Moves point (x,y) in direction, returns a pair """
34 return (x,y-multiplier)
35 elif direction == "DOWN":
36 return (x,y+multiplier)
37 elif direction == "LEFT":
38 return (x-multiplier, y)
39 elif direction == "RIGHT":
40 return (x+multiplier, y)
45 def oppositeColour(colour):
46 """ Returns the opposite colour to that given """
49 elif colour == "BLUE":
55 """ Class representing a piece
56 Pieces have colour, rank and co-ordinates
58 def __init__(self, colour, rank, x, y):
64 self.beenRevealed = False
67 return self.rank != 'F' and self.rank != 'B' and self.rank != '?' and self.rank != '+'
70 if ranks.count(self.rank) > 0:
71 return len(ranks) - 2 - ranks.index(self.rank)
77 if ranks.count(rank) > 0:
78 return len(ranks) - 2 - ranks.index(rank)
86 BasicAI class to play a game of stratego
87 Implements the protocol correctly. Stores the state of the board in self.board
88 Only makes random moves.
89 Override method "MakeMove" for more complex moves
92 """ Constructs the BasicAI agent, and starts it playing the game """
93 #sys.stderr.write("BasicAI __init__ here...\n");
99 self.totalAllies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1}
100 self.totalEnemies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1}
101 self.hiddenEnemies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1}
102 self.hiddenAllies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1}
103 self.lastMoved = None
108 """ Implements Setup part of protocol. Always uses the same setup. Override to create custom setups """
109 #sys.stderr.write("BasicAI Setup here...\n");
110 setup = sys.stdin.readline().split(' ')
112 sys.stderr.write("BasicAI setup fails, expected 4 tokens, got " + str(len(setup)) + " "+str(setup) + "\n")
113 self.colour = setup[0]
114 self.opponentName = setup[1]
115 self.width = int(setup[2])
116 self.height = int(setup[3])
117 for x in range(0, self.width):
118 self.board.append([])
119 for y in range(0, self.height):
120 self.board[x].append(None)
121 if self.colour == "RED":
122 print "FB8sB479B8\nBB31555583\n6724898974\n967B669999"
123 elif self.colour == "BLUE":
124 print "967B669999\n6724898974\nBB31555583\nFB8sB479B8"
128 #sys.stderr.write("BasicAI MakeMove here...\n");
129 if self.InterpretResult() == False or self.ReadBoard() == False or self.MakeMove() == False:
132 return self.InterpretResult()
135 """ Randomly moves any moveable piece, or prints "NO_MOVE" if there are none """
136 #TODO: Over-ride this function in base classes with more complex move behaviour
138 #sys.stderr.write("BasicAI MakeMove here...\n")
139 #self.debugPrintBoard()
141 if len(self.units) <= 0:
144 index = random.randint(0, len(self.units)-1)
147 directions = ("UP", "DOWN", "LEFT", "RIGHT")
149 piece = self.units[index]
150 if piece != None and piece.mobile():
151 dirIndex = random.randint(0, len(directions)-1)
152 startDirIndex = dirIndex
155 #sys.stderr.write("Trying index " + str(dirIndex) + "\n")
156 p = move(piece.x, piece.y, directions[dirIndex],1)
157 if p[0] >= 0 and p[0] < self.width and p[1] >= 0 and p[1] < self.height:
158 target = self.board[p[0]][p[1]]
159 if target == None or (target.colour != piece.colour and target.colour != "NONE" and target.colour != "BOTH"):
160 print str(piece.x) + " " + str(piece.y) + " "+directions[dirIndex]
162 dirIndex = (dirIndex + 1) % len(directions)
163 if startDirIndex == dirIndex:
166 index = (index + 1) % len(self.units)
167 if startIndex == index:
173 """ Reads in the board.
174 On the very first turn, sets up the self.board structure
175 On subsequent turns, the board is simply read, but the self.board structure is not updated here.
177 #sys.stderr.write("BasicAI ReadBoard here...\n");
178 for y in range(0,self.height):
179 row = sys.stdin.readline().strip()
180 if len(row) < self.width:
181 sys.stderr.write("Row has length " + str(len(row)) + " vs " + str(self.width) + "\n")
183 for x in range(0,self.width):
188 self.board[x][y] = Piece(oppositeColour(self.colour), '?',x,y)
189 self.enemyUnits.append(self.board[x][y])
191 self.board[x][y] = Piece("NONE", '+', x, y)
193 self.board[x][y] = Piece(self.colour, row[x],x,y)
194 self.units.append(self.board[x][y])
200 def InterpretResult(self):
201 """ Interprets the result of a move, and updates the board.
202 The very first move is ignored.
203 On subsequent moves, the self.board structure is updated
205 #sys.stderr.write("BasicAI InterpretResult here...\n")
206 result = sys.stdin.readline().split(' ')
207 #sys.stderr.write(" Read status line \"" + str(result) + "\"\n")
211 if result[0].strip() == "QUIT": #Make sure we exit when the manager tells us to!
214 if result[0].strip() == "NO_MOVE": #No move was made, don't need to update anything
217 if len(result) < 4: #Should be at least 4 tokens (X Y DIRECTION OUTCOME) in any other case
220 x = int(result[0].strip())
221 y = int(result[1].strip())
224 #sys.stderr.write(" Board position " + str(x) + " " + str(y) + " is OK!\n")
226 direction = result[2].strip()
229 outcome = result[3].strip()
231 if is_integer(outcome):
232 multiplier = int(outcome)
233 outcome = result[4].strip()
236 p = move(x,y,direction, multiplier)
238 #Determine attacking piece
239 attacker = self.board[x][y]
240 self.board[x][y] = None
247 defender = self.board[p[0]][p[1]]
249 #Update attacker's position (Don't overwrite the board yet though)
254 #Determine ranks of pieces if supplied
255 if len(result) >= outIndex + 3:
258 attacker.rank = result[outIndex+1].strip()
259 if attacker.beenRevealed == False:
260 if attacker.colour == self.colour:
261 self.hiddenAllies[attacker.rank] -= 1
262 elif attacker.colour == oppositeColour(self.colour):
263 self.hiddenEnemies[attacker.rank] -= 1
264 attacker.beenRevealed = True
265 defender.rank = result[outIndex+2].strip()
266 if defender.beenRevealed == False:
267 if defender.colour == self.colour:
268 self.hiddenAllies[defender.rank] -= 1
269 elif defender.colour == oppositeColour(self.colour):
270 self.hiddenEnemies[defender.rank] -= 1
272 defender.beenRevealed = True
277 self.board[p[0]][p[1]] = attacker
279 elif outcome == "KILLS":
280 self.board[p[0]][p[1]] = attacker
282 if defender.colour == self.colour:
283 self.totalAllies[defender.rank] -= 1
284 self.units.remove(defender)
285 elif defender.colour == oppositeColour(self.colour):
286 self.totalEnemies[defender.rank] -= 1
287 self.enemyUnits.remove(defender)
289 elif outcome == "DIES":
290 if attacker.colour == self.colour:
291 self.totalAllies[attacker.rank] -= 1
292 self.units.remove(attacker)
293 elif attacker.colour == oppositeColour(self.colour):
294 self.totalEnemies[attacker.rank] -= 1
295 self.enemyUnits.remove(attacker)
297 elif outcome == "BOTHDIE":
298 self.board[p[0]][p[1]] = None
300 if defender.colour == self.colour:
301 self.totalAllies[defender.rank] -= 1
302 self.units.remove(defender)
303 elif defender.colour == oppositeColour(self.colour):
304 self.totalEnemies[defender.rank] -= 1
305 self.enemyUnits.remove(defender)
307 if attacker.colour == self.colour:
308 self.totalAllies[attacker.rank] -= 1
309 self.units.remove(attacker)
310 elif attacker.colour == oppositeColour(self.colour):
311 self.totalEnemies[attacker.rank] -= 1
312 self.enemyUnits.remove(attacker)
314 elif outcome == "FLAG":
315 #sys.stderr.write(" Game over!\n")
317 elif outcome == "ILLEGAL":
318 #sys.stderr.write(" Illegal move!\n")
321 #sys.stderr.write(" Don't understand outcome \"" + outcome + "\"!\n");
324 #sys.stderr.write(" Completed interpreting move!\n");
327 def debugPrintBoard(self):
328 """ For debug purposes only. Prints the board to stderr.
329 Does not indicate difference between allied and enemy pieces
330 Unknown (enemy) pieces are shown as '?'
332 for y in range(0, self.height):
333 for x in range(0, self.width):
334 if self.board[x][y] == None:
335 sys.stderr.write(".");
337 sys.stderr.write(str(self.board[x][y].rank));
338 sys.stderr.write("\n")
340 if __name__ == "__main__":
343 while basicAI.MoveCycle():