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(' ')
90 self.colour = setup[0]
91 self.opponentName = setup[1]
92 self.width = int(setup[2])
93 self.height = int(setup[3])
94 for x in range(0, self.width):
96 for y in range(0, self.height):
97 self.board[x].append(None)
98 if self.colour == "RED":
99 print "FB8sB479B8\nBB31555583\n6724898974\n967B669999"
100 elif self.colour == "BLUE":
101 print "967B669999\n6724898974\nBB31555583\nFB8sB479B8"
105 #sys.stderr.write("BasicAI MakeMove here...\n");
106 if self.InterpretResult() == False or self.ReadBoard() == False or self.MakeMove() == False:
109 return self.InterpretResult()
112 """ Randomly moves any moveable piece, or prints "NO_MOVE" if there are none """
113 #TODO: Over-ride this function in base classes with more complex move behaviour
115 #sys.stderr.write("BasicAI MakeMove here...\n")
116 #self.debugPrintBoard()
118 if len(self.units) <= 0:
121 index = random.randint(0, len(self.units)-1)
124 directions = ("UP", "DOWN", "LEFT", "RIGHT")
126 piece = self.units[index]
127 if piece != None and piece.mobile():
128 dirIndex = random.randint(0, len(directions)-1)
129 startDirIndex = dirIndex
132 #sys.stderr.write("Trying index " + str(dirIndex) + "\n")
133 p = move(piece.x, piece.y, directions[dirIndex])
134 if p[0] >= 0 and p[0] < self.width and p[1] >= 0 and p[1] < self.height:
135 target = self.board[p[0]][p[1]]
136 if target == None or (target.colour != piece.colour and target.colour != "NONE" and target.colour != "BOTH"):
137 print str(piece.x) + " " + str(piece.y) + " "+directions[dirIndex]
139 dirIndex = (dirIndex + 1) % len(directions)
140 if startDirIndex == dirIndex:
143 index = (index + 1) % len(self.units)
144 if startIndex == index:
150 """ Reads in the board.
151 On the very first turn, sets up the self.board structure
152 On subsequent turns, the board is simply read, but the self.board structure is not updated here.
154 #sys.stderr.write("BasicAI ReadBoard here...\n");
155 for y in range(0,self.height):
156 row = sys.stdin.readline().strip()
157 if len(row) < self.width:
158 sys.stderr.write("Row has length " + str(len(row)) + " vs " + str(self.width) + "\n")
160 for x in range(0,self.width):
165 self.board[x][y] = Piece(oppositeColour(self.colour), '?',x,y)
166 self.enemyUnits.append(self.board[x][y])
168 self.board[x][y] = Piece("NONE", '+', x, y)
170 self.board[x][y] = Piece(self.colour, row[x],x,y)
171 self.units.append(self.board[x][y])
177 def InterpretResult(self):
178 """ Interprets the result of a move, and updates the board.
179 The very first move is ignored.
180 On subsequent moves, the self.board structure is updated
182 #sys.stderr.write("BasicAI InterpretResult here...\n")
183 result = sys.stdin.readline().split(' ')
184 #sys.stderr.write(" Read status line \"" + str(result) + "\"\n")
188 if result[0].strip() == "QUIT": #Make sure we exit when the manager tells us to!
191 if result[0].strip() == "NO_MOVE": #No move was made, don't need to update anything
194 if len(result) < 4: #Should be at least 4 tokens (X Y DIRECTION OUTCOME) in any other case
197 x = int(result[0].strip())
198 y = int(result[1].strip())
201 #sys.stderr.write(" Board position " + str(x) + " " + str(y) + " is OK!\n")
203 direction = result[2].strip()
204 outcome = result[3].strip()
206 p = move(x,y,direction)
211 self.board[p[0]][p[1]] = self.board[x][y]
212 self.board[x][y].x = p[0]
213 self.board[x][y].y = p[1]
215 self.board[x][y] = None
216 elif outcome == "KILLS":
217 if self.board[p[0]][p[1]] == None:
220 if self.board[p[0]][p[1]].colour == self.colour:
221 self.units.remove(self.board[p[0]][p[1]])
222 elif self.board[p[0]][p[1]].colour == oppositeColour(self.colour):
223 self.enemyUnits.remove(self.board[p[0]][p[1]])
225 self.board[x][y].x = p[0]
226 self.board[x][y].y = p[1]
229 self.board[p[0]][p[1]] = self.board[x][y]
230 self.board[x][y].rank = result[4].strip()
232 self.board[x][y] = None
234 elif outcome == "DIES":
235 if self.board[p[0]][p[1]] == None:
238 if self.board[x][y].colour == self.colour:
239 self.units.remove(self.board[x][y])
240 elif self.board[x][y].colour == oppositeColour(self.colour):
241 self.enemyUnits.remove(self.board[x][y])
243 self.board[p[0]][p[1]].rank = result[5].strip()
244 self.board[x][y] = None
245 elif outcome == "BOTHDIE":
246 if self.board[p[0]][p[1]] == None:
250 if self.board[x][y].colour == self.colour:
251 self.units.remove(self.board[x][y])
252 elif self.board[x][y].colour == oppositeColour(self.colour):
253 self.enemyUnits.remove(self.board[x][y])
254 if self.board[p[0]][p[1]].colour == self.colour:
255 self.units.remove(self.board[p[0]][p[1]])
256 elif self.board[p[0]][p[1]].colour == oppositeColour(self.colour):
257 self.enemyUnits.remove(self.board[p[0]][p[1]])
260 self.board[p[0]][p[1]] = None
261 self.board[x][y] = None
262 elif outcome == "FLAG":
263 #sys.stderr.write(" Game over!\n")
265 elif outcome == "ILLEGAL":
266 #sys.stderr.write(" Illegal move!\n")
269 #sys.stderr.write(" Don't understand outcome \"" + outcome + "\"!\n");
272 #sys.stderr.write(" Completed interpreting move!\n");
275 def debugPrintBoard(self):
276 """ For debug purposes only. Prints the board to stderr.
277 Does not indicate difference between allied and enemy pieces
278 Unknown (enemy) pieces are shown as '?'
280 for y in range(0, self.height):
281 for x in range(0, self.width):
282 if self.board[x][y] == None:
283 sys.stderr.write(".");
285 sys.stderr.write(str(self.board[x][y].rank));
286 sys.stderr.write("\n")
290 # while basicAI.MoveCycle():