9b427c73a23d9475f7890adedf2921c6b75db921
[progcomp2012.git] / progcomp / agents / basic_python / basic_python.py
1 #!/usr/bin/python -u
2
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.
5
6 """
7  basic_python.py - A sample Stratego AI for the UCC Programming Competition 2012
8
9  Written in python, the slithery language 
10  Simply makes random moves, as long as possible
11
12  author Sam Moore (matches) [SZM]
13  website http://matches.ucc.asn.au/stratego
14  email [email protected] or [email protected]
15  git git.ucc.asn.au/progcomp2012.git
16 """
17
18 import sys
19 import random
20
21 ranks = ['B','1','2','3','4','5','6','7','8','9','s','F', '?', '+']
22
23 def is_integer(s):
24         """ Using exceptions for this feels... wrong..."""
25         try:
26                 int(s)
27                 return True
28         except ValueError:
29                 return False
30
31 def move(x, y, direction, multiplier):
32         """ Moves point (x,y) in direction, returns a pair """
33         if direction == "UP":
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)
41         return (x,y)
42
43
44
45 def oppositeColour(colour):
46         """ Returns the opposite colour to that given """
47         if colour == "RED":
48                 return "BLUE"
49         elif colour == "BLUE":
50                 return "RED"
51         else:
52                 return "NONE"
53
54 class Piece:
55         """ Class representing a piece 
56                 Pieces have colour, rank and co-ordinates       
57         """
58         def __init__(self, colour, rank, x, y):
59                 self.colour = colour
60                 self.rank = rank
61                 self.x = x
62                 self.y = y
63                 self.lastMoved = -1
64
65         def mobile(self):
66                 return self.rank != 'F' and self.rank != 'B' and self.rank != '?' and self.rank != '+'
67
68         def valuedRank(self):
69                 if ranks.count(self.rank) > 0:
70                         return len(ranks) - 2 - ranks.index(self.rank)
71                 else:
72                         return 0
73         
74
75
76
77 class BasicAI:
78         """
79                 BasicAI class to play a game of stratego
80                 Implements the protocol correctly. Stores the state of the board in self.board
81                 Only makes random moves.
82                 Override method "MakeMove" for more complex moves
83         """
84         def __init__(self):     
85                 """ Constructs the BasicAI agent, and starts it playing the game """
86                 #sys.stderr.write("BasicAI __init__ here...\n");
87                 self.turn = 0
88                 self.board = []
89                 self.units = []
90                 self.enemyUnits = []
91
92                 
93
94         def Setup(self):
95                 """ Implements Setup part of protocol. Always uses the same setup. Override to create custom setups """
96                 #sys.stderr.write("BasicAI Setup here...\n");
97                 setup = sys.stdin.readline().split(' ')
98                 if len(setup) != 4:
99                         sys.stderr.write("BasicAI setup fails, expected 4 tokens, got " + str(len(setup)) + " "+str(setup) + "\n")
100                 self.colour = setup[0]
101                 self.opponentName = setup[1]
102                 self.width = int(setup[2])
103                 self.height = int(setup[3])
104                 for x in range(0, self.width):
105                         self.board.append([])
106                         for y in range(0, self.height):         
107                                 self.board[x].append(None)
108                 if self.colour == "RED":
109                         print "FB8sB479B8\nBB31555583\n6724898974\n967B669999"
110                 elif self.colour == "BLUE":
111                         print "967B669999\n6724898974\nBB31555583\nFB8sB479B8"
112                 return True
113
114         def MoveCycle(self):
115                 #sys.stderr.write("BasicAI MakeMove here...\n");
116                 if self.InterpretResult() == False or self.ReadBoard() == False or self.MakeMove() == False:
117                         return False
118                 self.turn += 1
119                 return self.InterpretResult()
120
121         def MakeMove(self):
122                 """ Randomly moves any moveable piece, or prints "NO_MOVE" if there are none """
123                 #TODO: Over-ride this function in base classes with more complex move behaviour
124
125                 #sys.stderr.write("BasicAI MakeMove here...\n")
126                 #self.debugPrintBoard()
127
128                 if len(self.units) <= 0:
129                         return False
130
131                 index = random.randint(0, len(self.units)-1)
132                 startIndex = index
133
134                 directions = ("UP", "DOWN", "LEFT", "RIGHT")
135                 while True:
136                         piece = self.units[index]
137                         if piece != None and piece.mobile():
138                                 dirIndex = random.randint(0, len(directions)-1)
139                                 startDirIndex = dirIndex
140                                 
141                                 while True:
142                                         #sys.stderr.write("Trying index " + str(dirIndex) + "\n")
143                                         p = move(piece.x, piece.y, directions[dirIndex])
144                                         if p[0] >= 0 and p[0] < self.width and p[1] >= 0 and p[1] < self.height:
145                                                 target = self.board[p[0]][p[1]]
146                                                 if target == None or (target.colour != piece.colour and target.colour != "NONE" and target.colour != "BOTH"):   
147                                                         print str(piece.x) + " " + str(piece.y) + " "+directions[dirIndex]
148                                                         return True
149                                         dirIndex = (dirIndex + 1) % len(directions)
150                                         if startDirIndex == dirIndex:
151                                                 break
152
153                         index = (index + 1) % len(self.units)
154                         if startIndex == index:
155                                 print "NO_MOVE"
156                                 return True
157                                                         
158                         
159         def ReadBoard(self):
160                 """ Reads in the board. 
161                         On the very first turn, sets up the self.board structure
162                         On subsequent turns, the board is simply read, but the self.board structure is not updated here.
163                 """
164                 #sys.stderr.write("BasicAI ReadBoard here...\n");
165                 for y in range(0,self.height):
166                         row = sys.stdin.readline().strip()
167                         if len(row) < self.width:
168                                 sys.stderr.write("Row has length " + str(len(row)) + " vs " + str(self.width) + "\n")
169                                 return False
170                         for x in range(0,self.width):
171                                 if self.turn == 0:
172                                         if row[x] == '.':
173                                                 pass
174                                         elif row[x] == '#':
175                                                 self.board[x][y] = Piece(oppositeColour(self.colour), '?',x,y)
176                                                 self.enemyUnits.append(self.board[x][y])
177                                         elif row[x] == '+':
178                                                 self.board[x][y] = Piece("NONE", '+', x, y)
179                                         else:
180                                                 self.board[x][y] = Piece(self.colour, row[x],x,y)
181                                                 self.units.append(self.board[x][y])
182                                 else:
183                                         pass
184                 return True
185                 
186
187         def InterpretResult(self):
188                 """ Interprets the result of a move, and updates the board. 
189                         The very first move is ignored. 
190                         On subsequent moves, the self.board structure is updated
191                 """
192                 #sys.stderr.write("BasicAI InterpretResult here...\n")
193                 result = sys.stdin.readline().split(' ')
194                 #sys.stderr.write("     Read status line \"" + str(result) + "\"\n")
195                 if self.turn == 0:
196                         return True
197
198                 if result[0].strip() == "QUIT": #Make sure we exit when the manager tells us to!
199                         return False
200
201                 if result[0].strip() == "NO_MOVE": #No move was made, don't need to update anything
202                         return True
203
204                 if len(result) < 4: #Should be at least 4 tokens (X Y DIRECTION OUTCOME) in any other case
205                         return False
206
207                 x = int(result[0].strip())
208                 y = int(result[1].strip())
209
210
211                 #sys.stderr.write("     Board position " + str(x) + " " + str(y) + " is OK!\n")         
212
213                 direction = result[2].strip()
214
215                 multiplier = 1
216                 outcome = result[3].strip()
217                 outIndex = 3
218                 if is_integer(outcome):
219                         multiplier = int(outcome)
220                         outcome = result[4].strip()
221                         outIndex = 4
222                 
223                 p = move(x,y,direction, multiplier)
224
225                 
226
227                 if outcome == "OK":
228                         self.board[p[0]][p[1]] = self.board[x][y]
229                         self.board[x][y].x = p[0]
230                         self.board[x][y].y = p[1]
231
232                         self.board[x][y] = None
233                 elif outcome == "KILLS":
234                         if self.board[p[0]][p[1]] == None:
235                                 return False
236
237                         if self.board[p[0]][p[1]].colour == self.colour:
238                                 self.units.remove(self.board[p[0]][p[1]])
239                         elif self.board[p[0]][p[1]].colour == oppositeColour(self.colour):
240                                 self.enemyUnits.remove(self.board[p[0]][p[1]])
241
242                         self.board[x][y].x = p[0]
243                         self.board[x][y].y = p[1]
244
245
246                         self.board[p[0]][p[1]] = self.board[x][y]
247                         self.board[x][y].rank = result[outIndex+1].strip()
248
249                         self.board[x][y] = None
250                         
251                 elif outcome == "DIES":
252                         if self.board[p[0]][p[1]] == None:
253                                 return False
254
255                         if self.board[x][y].colour == self.colour:
256                                 self.units.remove(self.board[x][y])
257                         elif self.board[x][y].colour == oppositeColour(self.colour):
258                                 self.enemyUnits.remove(self.board[x][y])
259
260                         self.board[p[0]][p[1]].rank = result[outIndex+2].strip()
261                         self.board[x][y] = None
262                 elif outcome == "BOTHDIE":
263                         if self.board[p[0]][p[1]] == None:
264                                 return False
265
266
267                         if self.board[x][y].colour == self.colour:
268                                 self.units.remove(self.board[x][y])
269                         elif self.board[x][y].colour == oppositeColour(self.colour):
270                                 self.enemyUnits.remove(self.board[x][y])
271                         if self.board[p[0]][p[1]].colour == self.colour:
272                                 self.units.remove(self.board[p[0]][p[1]])
273                         elif self.board[p[0]][p[1]].colour == oppositeColour(self.colour):
274                                 self.enemyUnits.remove(self.board[p[0]][p[1]])
275
276
277                         self.board[p[0]][p[1]] = None
278                         self.board[x][y] = None
279                 elif outcome == "FLAG":
280                         #sys.stderr.write("     Game over!\n")
281                         return False
282                 elif outcome == "ILLEGAL":
283                         #sys.stderr.write("     Illegal move!\n")
284                         return False
285                 else:
286                         #sys.stderr.write("     Don't understand outcome \"" + outcome + "\"!\n");
287                         return False
288
289                 #sys.stderr.write("     Completed interpreting move!\n");               
290                 return True
291
292         def debugPrintBoard(self):
293                 """ For debug purposes only. Prints the board to stderr.
294                         Does not indicate difference between allied and enemy pieces
295                         Unknown (enemy) pieces are shown as '?'
296                 """
297                 for y in range(0, self.height):
298                         for x in range(0, self.width):
299                                 if self.board[x][y] == None:
300                                         sys.stderr.write(".");
301                                 else:
302                                         sys.stderr.write(str(self.board[x][y].rank));
303                         sys.stderr.write("\n")
304
305 if __name__ == "__main__":
306         basicAI = BasicAI()
307         if basicAI.Setup():
308                 while basicAI.MoveCycle():
309                         pass
310

UCC git Repository :: git.ucc.asn.au