Modified manager output/protocol, added "basic" AI, made Asmodeus better
[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                 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):
95                         self.board.append([])
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"
102                 return True
103
104         def MoveCycle(self):
105                 #sys.stderr.write("BasicAI MakeMove here...\n");
106                 if self.InterpretResult() == False or self.ReadBoard() == False or self.MakeMove() == False:
107                         return False
108                 self.turn += 1
109                 return self.InterpretResult()
110
111         def MakeMove(self):
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
114
115                 #sys.stderr.write("BasicAI MakeMove here...\n")
116                 #self.debugPrintBoard()
117
118                 if len(self.units) <= 0:
119                         return False
120
121                 index = random.randint(0, len(self.units)-1)
122                 startIndex = index
123
124                 directions = ("UP", "DOWN", "LEFT", "RIGHT")
125                 while True:
126                         piece = self.units[index]
127                         if piece != None and piece.mobile():
128                                 dirIndex = random.randint(0, len(directions)-1)
129                                 startDirIndex = dirIndex
130                                 
131                                 while True:
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]
138                                                         return True
139                                         dirIndex = (dirIndex + 1) % len(directions)
140                                         if startDirIndex == dirIndex:
141                                                 break
142
143                         index = (index + 1) % len(self.units)
144                         if startIndex == index:
145                                 print "NO_MOVE"
146                                 return True
147                                                         
148                         
149         def ReadBoard(self):
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.
153                 """
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")
159                                 return False
160                         for x in range(0,self.width):
161                                 if self.turn == 0:
162                                         if row[x] == '.':
163                                                 pass
164                                         elif row[x] == '#':
165                                                 self.board[x][y] = Piece(oppositeColour(self.colour), '?',x,y)
166                                                 self.enemyUnits.append(self.board[x][y])
167                                         elif row[x] == '+':
168                                                 self.board[x][y] = Piece("NONE", '+', x, y)
169                                         else:
170                                                 self.board[x][y] = Piece(self.colour, row[x],x,y)
171                                                 self.units.append(self.board[x][y])
172                                 else:
173                                         pass
174                 return True
175                 
176
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
181                 """
182                 #sys.stderr.write("BasicAI InterpretResult here...\n")
183                 result = sys.stdin.readline().split(' ')
184                 #sys.stderr.write("     Read status line \"" + str(result) + "\"\n")
185                 if self.turn == 0:
186                         return True
187
188                 if result[0].strip() == "QUIT": #Make sure we exit when the manager tells us to!
189                         return False
190
191                 if result[0].strip() == "NO_MOVE": #No move was made, don't need to update anything
192                         return True
193
194                 if len(result) < 4: #Should be at least 4 tokens (X Y DIRECTION OUTCOME) in any other case
195                         return False
196
197                 x = int(result[0].strip())
198                 y = int(result[1].strip())
199
200
201                 #sys.stderr.write("     Board position " + str(x) + " " + str(y) + " is OK!\n")         
202
203                 direction = result[2].strip()
204                 outcome = result[3].strip()
205                 
206                 p = move(x,y,direction)
207
208                 
209
210                 if outcome == "OK":
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]
214
215                         self.board[x][y] = None
216                 elif outcome == "KILLS":
217                         if self.board[p[0]][p[1]] == None:
218                                 return False
219
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]])
224
225                         self.board[x][y].x = p[0]
226                         self.board[x][y].y = p[1]
227
228
229                         self.board[p[0]][p[1]] = self.board[x][y]
230                         self.board[x][y].rank = result[4].strip()
231
232                         self.board[x][y] = None
233                         
234                 elif outcome == "DIES":
235                         if self.board[p[0]][p[1]] == None:
236                                 return False
237
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])
242
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:
247                                 return False
248
249
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]])
258
259
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")
264                         return False
265                 elif outcome == "ILLEGAL":
266                         #sys.stderr.write("     Illegal move!\n")
267                         return False
268                 else:
269                         #sys.stderr.write("     Don't understand outcome \"" + outcome + "\"!\n");
270                         return False
271
272                 #sys.stderr.write("     Completed interpreting move!\n");               
273                 return True
274
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 '?'
279                 """
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(".");
284                                 else:
285                                         sys.stderr.write(str(self.board[x][y].rank));
286                         sys.stderr.write("\n")
287
288 #basicAI = BasicAI()
289 #if basicAI.Setup():
290 #       while basicAI.MoveCycle():
291 #               pass
292

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