Fixed segfault in manager program
[progcomp2012.git] / samples / 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 move(x, y, direction):
24         """ Moves point (x,y) in direction, returns a pair """
25         if direction == "UP":
26                 return (x,y-1)
27         elif direction == "DOWN":
28                 return (x,y+1)
29         elif direction == "LEFT":
30                 return (x-1, y)
31         elif direction == "RIGHT":
32                 return (x+1, y)
33         return (x,y)
34
35
36
37 def oppositeColour(colour):
38         """ Returns the opposite colour to that given """
39         if colour == "RED":
40                 return "BLUE"
41         elif colour == "BLUE":
42                 return "RED"
43         else:
44                 return "NONE"
45
46 class Piece:
47         """ Class representing a piece 
48                 Pieces have colour, rank and co-ordinates       
49         """
50         def __init__(self, colour, rank, x, y):
51                 self.colour = colour
52                 self.rank = rank
53                 self.x = x
54                 self.y = y
55                 self.lastMoved = -1
56
57         def mobile(self):
58                 return self.rank != 'F' and self.rank != 'B' and self.rank != '?' and self.rank != '+'
59
60         def valuedRank(self):
61                 if ranks.count(self.rank) > 0:
62                         return len(ranks) - 2 - ranks.index(self.rank)
63                 else:
64                         return 0
65         
66
67
68
69 class BasicAI:
70         """
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
75         """
76         def __init__(self):     
77                 """ Constructs the BasicAI agent, and starts it playing the game """
78                 #sys.stderr.write("BasicAI __init__ here...\n");
79                 self.turn = 0
80                 self.board = []
81                 self.units = []
82                 self.enemyUnits = []
83
84                 
85
86         def Setup(self):
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                 if len(setup) != 4:
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):
97                         self.board.append([])
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"
104                 return True
105
106         def MoveCycle(self):
107                 #sys.stderr.write("BasicAI MakeMove here...\n");
108                 if self.InterpretResult() == False or self.ReadBoard() == False or self.MakeMove() == False:
109                         return False
110                 self.turn += 1
111                 return self.InterpretResult()
112
113         def MakeMove(self):
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
116
117                 #sys.stderr.write("BasicAI MakeMove here...\n")
118                 #self.debugPrintBoard()
119
120                 if len(self.units) <= 0:
121                         return False
122
123                 index = random.randint(0, len(self.units)-1)
124                 startIndex = index
125
126                 directions = ("UP", "DOWN", "LEFT", "RIGHT")
127                 while True:
128                         piece = self.units[index]
129                         if piece != None and piece.mobile():
130                                 dirIndex = random.randint(0, len(directions)-1)
131                                 startDirIndex = dirIndex
132                                 
133                                 while True:
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]
140                                                         return True
141                                         dirIndex = (dirIndex + 1) % len(directions)
142                                         if startDirIndex == dirIndex:
143                                                 break
144
145                         index = (index + 1) % len(self.units)
146                         if startIndex == index:
147                                 print "NO_MOVE"
148                                 return True
149                                                         
150                         
151         def ReadBoard(self):
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.
155                 """
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")
161                                 return False
162                         for x in range(0,self.width):
163                                 if self.turn == 0:
164                                         if row[x] == '.':
165                                                 pass
166                                         elif row[x] == '#':
167                                                 self.board[x][y] = Piece(oppositeColour(self.colour), '?',x,y)
168                                                 self.enemyUnits.append(self.board[x][y])
169                                         elif row[x] == '+':
170                                                 self.board[x][y] = Piece("NONE", '+', x, y)
171                                         else:
172                                                 self.board[x][y] = Piece(self.colour, row[x],x,y)
173                                                 self.units.append(self.board[x][y])
174                                 else:
175                                         pass
176                 return True
177                 
178
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
183                 """
184                 #sys.stderr.write("BasicAI InterpretResult here...\n")
185                 result = sys.stdin.readline().split(' ')
186                 #sys.stderr.write("     Read status line \"" + str(result) + "\"\n")
187                 if self.turn == 0:
188                         return True
189
190                 if result[0].strip() == "QUIT": #Make sure we exit when the manager tells us to!
191                         return False
192
193                 if result[0].strip() == "NO_MOVE": #No move was made, don't need to update anything
194                         return True
195
196                 if len(result) < 4: #Should be at least 4 tokens (X Y DIRECTION OUTCOME) in any other case
197                         return False
198
199                 x = int(result[0].strip())
200                 y = int(result[1].strip())
201
202
203                 #sys.stderr.write("     Board position " + str(x) + " " + str(y) + " is OK!\n")         
204
205                 direction = result[2].strip()
206                 outcome = result[3].strip()
207                 
208                 p = move(x,y,direction)
209
210                 
211
212                 if outcome == "OK":
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]
216
217                         self.board[x][y] = None
218                 elif outcome == "KILLS":
219                         if self.board[p[0]][p[1]] == None:
220                                 return False
221
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]])
226
227                         self.board[x][y].x = p[0]
228                         self.board[x][y].y = p[1]
229
230
231                         self.board[p[0]][p[1]] = self.board[x][y]
232                         self.board[x][y].rank = result[4].strip()
233
234                         self.board[x][y] = None
235                         
236                 elif outcome == "DIES":
237                         if self.board[p[0]][p[1]] == None:
238                                 return False
239
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])
244
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:
249                                 return False
250
251
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]])
260
261
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")
266                         return False
267                 elif outcome == "ILLEGAL":
268                         #sys.stderr.write("     Illegal move!\n")
269                         return False
270                 else:
271                         #sys.stderr.write("     Don't understand outcome \"" + outcome + "\"!\n");
272                         return False
273
274                 #sys.stderr.write("     Completed interpreting move!\n");               
275                 return True
276
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 '?'
281                 """
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(".");
286                                 else:
287                                         sys.stderr.write(str(self.board[x][y].rank));
288                         sys.stderr.write("\n")
289
290 #basicAI = BasicAI()
291 #if basicAI.Setup():
292 #       while basicAI.MoveCycle():
293 #               pass
294

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