3c7738e540bc9d17f2496d6ff6e137089e07998a
[progcomp2013.git] / agents / bishop.py
1 #!/usr/bin/python -u
2
3 from qchess import *
4
5 """
6         Agent Bishop
7         ( an agent, not an implementation of a bishop chess piece!)
8 """
9
10
11
12
13 # Skeleton class for your agent
14 class Agent(AgentRandom): # Inherits from AgentRandom (in qchess.py)
15         def __init__(self, name, colour):
16                 AgentRandom.__init__(self, name, colour)
17                 self.value = {"pawn" : 1, "bishop" : 3, "knight" : 3, "rook" : 5, "queen" : 9, "king" : 100, "unknown" : 4}
18
19                 self.aggression = 2.0 # Multiplier for scoring due to aggressive actions
20                 self.defence = 1.0 # Multiplier for scoring due to defensive actions
21                 
22                 self.depth = 0 # Current depth
23                 self.max_depth = 2 # Recurse this many times (for some reason, makes more mistakes when this is increased???)
24                 self.recurse_for = -1 # Recurse for the best few moves each times (less than 0 = all moves)
25
26                 for p in self.board.pieces["white"] + self.board.pieces["black"]:
27                         p.last_moves = None
28                         p.selected_moves = None
29
30                 
31
32         def get_value(self, piece):
33                 if piece == None:
34                         return 0.0
35                 return float(self.value[piece.types[0]] + self.value[piece.types[1]]) / 2.0
36                 
37         # Score possible moves for the piece
38         
39         def prioritise_moves(self, piece):
40
41                 #sys.stderr.write(sys.argv[0] + ": prioritise called for " + str(piece) + "\n")
42
43                 
44                 
45                 grid = self.board.probability_grid(piece)
46                 #sys.stderr.write("\t Probability grid " + str(grid) + "\n")
47                 moves = []
48                 for x in range(w):
49                         for y in range(h):
50                                 if grid[x][y] < 0.3: # Throw out moves with < 30% probability
51                                         #sys.stderr.write("\tReject " + str(x) + "," + str(y) + " (" + str(grid[x][y]) + ")\n")
52                                         continue
53
54                                 target = self.board.grid[x][y]
55                         
56                                 
57                                 
58                                 
59                                 # Get total probability that the move is protected
60                                 [xx,yy] = [piece.x, piece.y]
61                                 [piece.x, piece.y] = [x, y]
62                                 self.board.grid[x][y] = piece
63                                 self.board.grid[xx][yy] = None
64                                 
65                                 defenders = self.board.coverage(x, y, piece.colour, reject_allied = False)
66                                 d_prob = 0.0
67                                 for d in defenders.keys():
68                                         d_prob += defenders[d]
69                                 if len(defenders.keys()) > 0:
70                                         d_prob /= float(len(defenders.keys()))
71
72                                 if (d_prob > 1.0):
73                                         d_prob = 1.0
74
75                                 # Get total probability that the move is threatened
76                                 attackers = self.board.coverage(x, y, opponent(piece.colour), reject_allied = False)
77                                 a_prob = 0.0
78                                 for a in attackers.keys():
79                                         a_prob += attackers[a]
80                                 if len(attackers.keys()) > 0:
81                                         a_prob /= float(len(attackers.keys()))
82
83                                 if (a_prob > 1.0):
84                                         a_prob = 1.0
85
86                                 self.board.grid[x][y] = target
87                                 self.board.grid[xx][yy] = piece
88                                 [piece.x, piece.y] = [xx, yy]
89
90                                 
91                                 # Score of the move
92                                 value = self.aggression * (1.0 + d_prob) * self.get_value(target) - self.defence * (1.0 - d_prob) * a_prob * self.get_value(piece)
93
94                                 # Adjust score based on movement of piece out of danger
95                                 attackers = self.board.coverage(piece.x, piece.y, opponent(piece.colour))
96                                 s_prob = 0.0
97                                 for a in attackers.keys():
98                                         s_prob += attackers[a]
99                                 if len(attackers.keys()) > 0:
100                                         s_prob /= float(len(attackers.keys()))
101
102                                 if (s_prob > 1.0):
103                                         s_prob = 1.0
104                                 value += self.defence * s_prob * self.get_value(piece)
105                                 
106                                 # Adjust score based on probability that the move is actually possible
107                                 moves.append([[x, y], grid[x][y] * value])
108
109                 moves.sort(key = lambda e : e[1], reverse = True)
110                 #sys.stderr.write(sys.argv[0] + ": Moves for " + str(piece) + " are " + str(moves) + "\n")
111
112                 piece.last_moves = moves
113                 piece.selected_moves = None
114
115                 
116
117                 
118                 return moves
119
120         def select_best(self, colour):
121
122                 self.depth += 1
123                 all_moves = {}
124                 for p in self.board.pieces[colour]:
125                         self.choice = p # Temporarily pick that piece
126                         m = self.prioritise_moves(p)
127                         if len(m) > 0:
128                                 all_moves.update({p : m[0]})
129
130                 if len(all_moves.items()) <= 0:
131                         return None
132                 
133                 
134                 opts = all_moves.items()
135                 opts.sort(key = lambda e : e[1][1], reverse = True)
136
137                 if self.depth >= self.max_depth:
138                         self.depth -= 1
139                         return list(opts[0])
140
141                 if self.recurse_for >= 0:
142                         opts = opts[0:self.recurse_for]
143                 #sys.stderr.write(sys.argv[0] + " : Before recurse, options are " + str(opts) + "\n")
144
145                 # Take the best few moves, and recurse
146                 for choice in opts[0:self.recurse_for]:
147                         [xx,yy] = [choice[0].x, choice[0].y] # Remember position
148                         [nx,ny] = choice[1][0] # Target
149                         [choice[0].x, choice[0].y] = [nx, ny] # Set position
150                         target = self.board.grid[nx][ny] # Remember piece in spot
151                         self.board.grid[xx][yy] = None # Remove piece
152                         self.board.grid[nx][ny] = choice[0] # Replace with moving piece
153                         
154                         # Recurse
155                         best_enemy_move = self.select_best(opponent(choice[0].colour))
156                         choice[1][1] -= best_enemy_move[1][1] / float(self.depth + 1.0)
157                         
158                         [choice[0].x, choice[0].y] = [xx, yy] # Restore position
159                         self.board.grid[nx][ny] = target # Restore taken piece
160                         self.board.grid[xx][yy] = choice[0] # Restore moved piece
161                         
162                 
163
164                 opts.sort(key = lambda e : e[1][1], reverse = True)
165                 #sys.stderr.write(sys.argv[0] + " : After recurse, options are " + str(opts) + "\n")
166
167                 self.depth -= 1
168                 return list(opts[0])
169
170                 
171
172         # Returns [x,y] of selected piece
173         def select(self):
174                 
175                 self.choice = self.select_best(self.colour)[0]
176                 return [self.choice.x, self.choice.y]
177         
178         # Returns [x,y] of square to move selected piece into
179         def get_move(self):
180                 self.choice.selected_moves = self.choice.last_moves
181                 moves = self.prioritise_moves(self.choice)
182                 if len(moves) > 0:
183                         return moves[0][0]
184                 else:
185                         return AgentRandom.get_move(self)
186
187                 
188                 
189                 
190 # Horrible messy graphics class that draws what the agent is doing, kind of useful for testing
191 class AgentGraphics(GraphicsThread):
192         def __init__(self, board, title):
193                 GraphicsThread.__init__(self, board, title, grid_sz = [64,64])
194                 self.choice = None
195                 self.moves = None
196
197         def run(self):
198                 square_img = pygame.Surface((self.grid_sz[0], self.grid_sz[1]),pygame.SRCALPHA) # A square image
199                 while not self.stopped():
200                 
201                         self.board.display_grid(window = self.window, grid_sz = self.grid_sz)   
202
203                         # Draw choice of the AI
204                         if agent.choice != None:
205                                 mp = [self.grid_sz[i] * [agent.choice.x, agent.choice.y][i] for i in range(2)]
206                                 square_img.fill(pygame.Color(0,255,0,64))
207                                 self.window.blit(square_img, mp)
208
209                         # Draw calculated choices for the piece clicked on
210                         if self.choice != None:
211                                 mp = [self.grid_sz[i] * [self.choice.x, self.choice.y][i] for i in range(2)]
212                                 square_img.fill(pygame.Color(0,0,255,128))
213                                 self.window.blit(square_img, mp)
214
215                         # Draw the choices the AI calculated from the selection of the chosen piece
216                         if agent.choice != None and agent.choice.selected_moves != None:
217                                 for m in agent.choice.selected_moves:
218                                         mp = [m[0][i] * self.grid_sz[i] for i in range(2)]
219                                         square_img.fill(pygame.Color(128,128,255,128))
220                                         self.window.blit(square_img, mp)
221                                         font = pygame.font.Font(None, 14)
222                                         text = font.render("{0:.2f}".format(round(m[1],2)), 1, pygame.Color(255,0,0))
223                                         mp[0] = mp[0] + self.grid_sz[0] - text.get_width()
224                                         mp[1] = mp[1] + self.grid_sz[1] - text.get_height()
225                                         self.window.blit(text, mp)
226
227
228                         # Draw the choice the AI's chosen piece could have actually made
229                         if agent.choice != None and agent.choice.last_moves != None:
230                                 for m in agent.choice.last_moves:
231                                         mp = [m[0][i] * self.grid_sz[i] for i in range(2)]
232                                         square_img.fill(pygame.Color(255,0,0,128))
233                                         self.window.blit(square_img, mp)
234                                         font = pygame.font.Font(None, 14)
235                                         text = font.render("{0:.2f}".format(round(m[1],2)), 1, pygame.Color(0,0,255))
236                                         mp[0] = mp[0] + self.grid_sz[0] - text.get_width()
237                                         self.window.blit(text, mp)
238
239                         
240
241
242                         if self.moves != None:
243                                 for m in self.moves:
244                                         mp = [m[0][i] * self.grid_sz[i] for i in range(2)]
245                                         square_img.fill(pygame.Color(255,0,255,128))
246                                         self.window.blit(square_img, mp)
247                                         font = pygame.font.Font(None, 14)
248                                         text = font.render("{0:.2f}".format(round(m[1],2)), 1, pygame.Color(0,0,0))
249                                         self.window.blit(text, mp)
250                         
251                         
252         
253                         self.board.display_pieces(window = self.window, grid_sz = self.grid_sz)
254
255                         pygame.display.flip()
256                         
257                         for event in pygame.event.get():
258                                 if event.type == pygame.QUIT:
259                                         self.stop()
260                                         break
261                                 elif event.type == pygame.MOUSEBUTTONDOWN:
262                                         m = [event.pos[i] / self.grid_sz[i] for i in range(len(event.pos))]
263                                         p = agent.board.grid[m[0]][m[1]]
264                                         if p == None:
265                                                 continue
266                                         self.choice = p
267                                         self.last_moves = self.choice.last_moves
268                                         self.selected_moves = self.choice.selected_moves
269                                         if event.button == 3 or self.choice.last_moves == None:
270                                                 self.moves = agent.prioritise_moves(self.choice)
271                                         else:
272                                                 self.moves = self.choice.last_moves
273                                         
274                                 elif event.type == pygame.MOUSEBUTTONUP:
275                                         if self.choice == None:
276                                                 continue
277                                         self.choice.last_moves = self.last_moves
278                                         self.choice.selected_moves = self.selected_moves
279                                         self.choice = None
280                                         self.moves = None
281                                         
282                 pygame.display.quit()                           
283                 
284
285
286 # Main function; don't alter
287 def main(argv):
288
289         global agent
290         colour = sys.stdin.readline().strip("\n") # Gets the colour of the agent from stdin
291         
292         agent = Agent(argv[0], colour) # Creates your agent
293
294         #graphics = AgentGraphics(agent.board, title="Agent Bishop (" + str(colour) + ") - DEBUG VIEW")
295         #graphics.start()
296
297         # Plays quantum chess using your agent
298         while True:
299                 line = sys.stdin.readline().strip(" \r\n")
300                 #sys.stderr.write(argv[0] + ": gets line \"" + str(line) + "\"\n")
301                 if line == "SELECTION?":
302                         [x,y] = agent.select() # Gets your agent's selection
303                         #print "Select " + str(x) + "," + str(y)
304                         sys.stdout.write(str(x) + " " + str(y) + "\n")                          
305                 elif line == "MOVE?":
306                         [x,y] = agent.get_move() # Gets your agent's move
307                         sys.stdout.write(str(x) + " " + str(y) + "\n")
308                 elif line.split(" ")[0] == "QUIT":
309                         agent.quit(" ".join(line.split(" ")[1:])) # Quits the game
310 #                       graphics.stop()
311                         break
312                 else:
313                         agent.update(line) # Updates agent.board
314
315         #graphics.stop()
316         #graphics.join()
317         return 0
318
319 # Don't touch this
320 if __name__ == "__main__":
321         sys.exit(main(sys.argv))

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