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', '?', '+']
23 def move(x, y, direction):
24 """ Moves point (x,y) in direction, returns a pair """
27 elif direction == "DOWN":
29 elif direction == "LEFT":
31 elif direction == "RIGHT":
37 def oppositeColour(colour):
38 """ Returns the opposite colour to that given """
41 elif colour == "BLUE":
47 """ Class representing a piece
48 Pieces have colour, rank and co-ordinates
50 def __init__(self, colour, rank, x, y):
58 return self.rank != 'F' and self.rank != 'B' and self.rank != '?' and self.rank != '+'
61 if ranks.count(self.rank) > 0:
62 return len(ranks) - 2 - ranks.index(self.rank)
71 BasicAI class to play a game of stratego
72 Implements the protocol correctly. Stores the state of the board in self.board
73 Only makes random moves.
74 Override method "MakeMove" for more complex moves
77 """ Constructs the BasicAI agent, and starts it playing the game """
78 #sys.stderr.write("BasicAI __init__ here...\n");
87 """ Implements Setup part of protocol. Always uses the same setup. Override to create custom setups """
88 #sys.stderr.write("BasicAI Setup here...\n");
89 setup = sys.stdin.readline().split(' ')
91 sys.stderr.write("BasicAI setup fails, expected 4 tokens, got " + str(len(setup)) + " "+str(setup) + "\n")
92 self.colour = setup[0]
93 self.opponentName = setup[1]
94 self.width = int(setup[2])
95 self.height = int(setup[3])
96 for x in range(0, self.width):
98 for y in range(0, self.height):
99 self.board[x].append(None)
100 if self.colour == "RED":
101 print "FB8sB479B8\nBB31555583\n6724898974\n967B669999"
102 elif self.colour == "BLUE":
103 print "967B669999\n6724898974\nBB31555583\nFB8sB479B8"
107 #sys.stderr.write("BasicAI MakeMove here...\n");
108 if self.InterpretResult() == False or self.ReadBoard() == False or self.MakeMove() == False:
111 return self.InterpretResult()
114 """ Randomly moves any moveable piece, or prints "NO_MOVE" if there are none """
115 #TODO: Over-ride this function in base classes with more complex move behaviour
117 #sys.stderr.write("BasicAI MakeMove here...\n")
118 #self.debugPrintBoard()
120 if len(self.units) <= 0:
123 index = random.randint(0, len(self.units)-1)
126 directions = ("UP", "DOWN", "LEFT", "RIGHT")
128 piece = self.units[index]
129 if piece != None and piece.mobile():
130 dirIndex = random.randint(0, len(directions)-1)
131 startDirIndex = dirIndex
134 #sys.stderr.write("Trying index " + str(dirIndex) + "\n")
135 p = move(piece.x, piece.y, directions[dirIndex])
136 if p[0] >= 0 and p[0] < self.width and p[1] >= 0 and p[1] < self.height:
137 target = self.board[p[0]][p[1]]
138 if target == None or (target.colour != piece.colour and target.colour != "NONE" and target.colour != "BOTH"):
139 print str(piece.x) + " " + str(piece.y) + " "+directions[dirIndex]
141 dirIndex = (dirIndex + 1) % len(directions)
142 if startDirIndex == dirIndex:
145 index = (index + 1) % len(self.units)
146 if startIndex == index:
152 """ Reads in the board.
153 On the very first turn, sets up the self.board structure
154 On subsequent turns, the board is simply read, but the self.board structure is not updated here.
156 #sys.stderr.write("BasicAI ReadBoard here...\n");
157 for y in range(0,self.height):
158 row = sys.stdin.readline().strip()
159 if len(row) < self.width:
160 sys.stderr.write("Row has length " + str(len(row)) + " vs " + str(self.width) + "\n")
162 for x in range(0,self.width):
167 self.board[x][y] = Piece(oppositeColour(self.colour), '?',x,y)
168 self.enemyUnits.append(self.board[x][y])
170 self.board[x][y] = Piece("NONE", '+', x, y)
172 self.board[x][y] = Piece(self.colour, row[x],x,y)
173 self.units.append(self.board[x][y])
179 def InterpretResult(self):
180 """ Interprets the result of a move, and updates the board.
181 The very first move is ignored.
182 On subsequent moves, the self.board structure is updated
184 #sys.stderr.write("BasicAI InterpretResult here...\n")
185 result = sys.stdin.readline().split(' ')
186 #sys.stderr.write(" Read status line \"" + str(result) + "\"\n")
190 if result[0].strip() == "QUIT": #Make sure we exit when the manager tells us to!
193 if result[0].strip() == "NO_MOVE": #No move was made, don't need to update anything
196 if len(result) < 4: #Should be at least 4 tokens (X Y DIRECTION OUTCOME) in any other case
199 x = int(result[0].strip())
200 y = int(result[1].strip())
203 #sys.stderr.write(" Board position " + str(x) + " " + str(y) + " is OK!\n")
205 direction = result[2].strip()
206 outcome = result[3].strip()
208 p = move(x,y,direction)
213 self.board[p[0]][p[1]] = self.board[x][y]
214 self.board[x][y].x = p[0]
215 self.board[x][y].y = p[1]
217 self.board[x][y] = None
218 elif outcome == "KILLS":
219 if self.board[p[0]][p[1]] == None:
222 if self.board[p[0]][p[1]].colour == self.colour:
223 self.units.remove(self.board[p[0]][p[1]])
224 elif self.board[p[0]][p[1]].colour == oppositeColour(self.colour):
225 self.enemyUnits.remove(self.board[p[0]][p[1]])
227 self.board[x][y].x = p[0]
228 self.board[x][y].y = p[1]
231 self.board[p[0]][p[1]] = self.board[x][y]
232 self.board[x][y].rank = result[4].strip()
234 self.board[x][y] = None
236 elif outcome == "DIES":
237 if self.board[p[0]][p[1]] == None:
240 if self.board[x][y].colour == self.colour:
241 self.units.remove(self.board[x][y])
242 elif self.board[x][y].colour == oppositeColour(self.colour):
243 self.enemyUnits.remove(self.board[x][y])
245 self.board[p[0]][p[1]].rank = result[5].strip()
246 self.board[x][y] = None
247 elif outcome == "BOTHDIE":
248 if self.board[p[0]][p[1]] == None:
252 if self.board[x][y].colour == self.colour:
253 self.units.remove(self.board[x][y])
254 elif self.board[x][y].colour == oppositeColour(self.colour):
255 self.enemyUnits.remove(self.board[x][y])
256 if self.board[p[0]][p[1]].colour == self.colour:
257 self.units.remove(self.board[p[0]][p[1]])
258 elif self.board[p[0]][p[1]].colour == oppositeColour(self.colour):
259 self.enemyUnits.remove(self.board[p[0]][p[1]])
262 self.board[p[0]][p[1]] = None
263 self.board[x][y] = None
264 elif outcome == "FLAG":
265 #sys.stderr.write(" Game over!\n")
267 elif outcome == "ILLEGAL":
268 #sys.stderr.write(" Illegal move!\n")
271 #sys.stderr.write(" Don't understand outcome \"" + outcome + "\"!\n");
274 #sys.stderr.write(" Completed interpreting move!\n");
277 def debugPrintBoard(self):
278 """ For debug purposes only. Prints the board to stderr.
279 Does not indicate difference between allied and enemy pieces
280 Unknown (enemy) pieces are shown as '?'
282 for y in range(0, self.height):
283 for x in range(0, self.width):
284 if self.board[x][y] == None:
285 sys.stderr.write(".");
287 sys.stderr.write(str(self.board[x][y].rank));
288 sys.stderr.write("\n")
292 # while basicAI.MoveCycle():