37cca6797ca0f750391846703a943390c28b75e0
[progcomp2013.git] / qchess / src / player.py
1 import subprocess
2 import select
3 import platform
4 import re
5
6 agent_timeout = -1.0 # Timeout in seconds for AI players to make moves
7                         # WARNING: Won't work for windows based operating systems
8
9 if platform.system() == "Windows":
10         agent_timeout = -1 # Hence this
11
12 # A player who can't play
13 class Player():
14         def __init__(self, name, colour):
15                 self.name = name
16                 self.colour = colour
17
18         def update(self, result):
19                 return result
20
21         def reset_board(self, s):
22                 pass
23         
24         def __str__(self):
25                 return self.name + "<"+str(self.colour)+">"
26
27         def base_player(self):
28                 return self
29         
30
31
32 def open_fifo(name, mode, timeout=None):
33         if timeout == None:
34                 return open(name, mode)
35         
36         
37         class Worker(threading.Thread):
38                 def __init__(self):
39                         threading.Thread.__init__(self)
40                         self.result = None
41                         self.exception = None
42
43                         
44                 def run(self):          
45                         try:
46                                 self.result = open(name, mode)
47                         except Exception, e:
48                                 self.exception = e
49                                 self.result = None
50                 
51
52         w = Worker()
53         w.start()
54         
55         start = time.time()
56         while time.time() - start < timeout:
57                 if w.is_alive() == False:
58                         w.join()
59                         if w.exception != None:
60                                 raise w.exception
61                         return w.result
62                 time.sleep(0.1)
63         
64         
65         if w.is_alive():
66                 #sys.stderr.write("FIFO_TIMEOUT!\n")
67                 # Recursive to deal with possible race condition
68                 try:
69                         if mode == "r":
70                                 f = open_fifo(name, "w", 1)
71                         else:
72                                 f = open_fifo(name, "r", 1)
73                 except:
74                         pass
75                         
76                 #sys.stderr.write("Opened other end!\n")
77                 while w.is_alive():
78                         time.sleep(0.1)
79                         
80                 w.join()
81                 f.close()
82                 w.result.close()
83                 raise Exception("FIFO_TIMEOUT")
84         else:
85                 w.join()
86                 if w.exception != None:
87                         raise w.exception
88                 return w.result
89         
90
91 # Player that runs through a fifo
92 class FifoPlayer(Player):
93         
94         timeout = 300
95         
96         def __init__(self, name, colour):
97                 Player.__init__(self, name, colour)
98                 os.mkfifo(self.name+".in")
99                 os.mkfifo(self.name+".out")
100
101                 try:
102                         self.fifo_out = open_fifo(self.name+".out","w", FifoPlayer.timeout)
103                 except:
104                         raise Exception("FIFO_TIMEOUT")
105                 else:
106                         self.fifo_out.write("START "+colour+"\n")
107                         self.fifo_out.close()
108
109                 
110                 
111                 
112                 
113         def update(self, result):
114                 sys.stderr.write("update fifo called\n")
115                 try:
116                         self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
117                 except:
118                         raise Exception("FIFO_TIMEOUT")
119                 else:
120                         self.fifo_out.write(result +"\n")
121                         self.fifo_out.close()
122                         return result
123                 
124         def select(self):
125                 sys.stderr.write("select fifo called\n")
126                 try:
127                         self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
128                 except:
129                         #sys.stderr.write("TIMEOUT\n")
130                         raise Exception("FIFO_TIMEOUT")
131                 else:
132                         
133                         self.fifo_out.write("SELECT?\n")
134                         self.fifo_out.close()
135                         self.fifo_in = open_fifo(self.name+".in", "r", FifoPlayer.timeout)
136                         s = map(int, self.fifo_in.readline().strip(" \r\n").split(" "))
137                         self.fifo_in.close()
138                         return s
139         
140         def get_move(self):
141                 sys.stderr.write("get_move fifo called\n")
142                 try:
143                         self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
144                 except:
145                         raise Exception("FIFO_TIMEOUT")
146                 else:
147                         self.fifo_out.write("MOVE?\n")
148                         self.fifo_out.close()
149                         self.fifo_in = open_fifo(self.name+".in", "r", FifoPlayer.timeout)
150                         s = map(int, self.fifo_in.readline().strip(" \r\n").split(" "))
151                         self.fifo_in.close()
152                         return s
153         
154         def quit(self, result):
155                 try:
156                         self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
157                 except:
158                         os.remove(self.name+".in")
159                         os.remove(self.name+".out")
160                         #raise Exception("FIFO_TIMEOUT")
161                         
162                 else:
163                         self.fifo_out.write(result + "\n")
164                         self.fifo_out.close()
165                         os.remove(self.name+".in")
166                         os.remove(self.name+".out")
167
168 # Player that runs from another process
169 class ExternalAgent(Player):
170
171
172         def __init__(self, name, colour):
173                 Player.__init__(self, name, colour)
174                 self.p = subprocess.Popen(name,bufsize=0,stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True,universal_newlines=True)
175                 
176                 self.send_message(colour)
177
178         def send_message(self, s):
179                 if agent_timeout > 0.0:
180                         ready = select.select([], [self.p.stdin], [], agent_timeout)[1]
181                 else:
182                         ready = [self.p.stdin]
183                 if self.p.stdin in ready:
184                         #sys.stderr.write("Writing \'" + s + "\' to " + str(self.p) + "\n")
185                         try:
186                                 self.p.stdin.write(s + "\n")
187                         except:
188                                 raise Exception("UNRESPONSIVE")
189                 else:
190                         raise Exception("TIMEOUT")
191
192         def get_response(self):
193                 if agent_timeout > 0.0:
194                         ready = select.select([self.p.stdout], [], [], agent_timeout)[0]
195                 else:
196                         ready = [self.p.stdout]
197                 if self.p.stdout in ready:
198                         #sys.stderr.write("Reading from " + str(self.p) + " 's stdout...\n")
199                         try:
200                                 result = self.p.stdout.readline().strip(" \t\r\n")
201                                 #sys.stderr.write("Read \'" + result + "\' from " + str(self.p) + "\n")
202                                 return result
203                         except: # Exception, e:
204                                 raise Exception("UNRESPONSIVE")
205                 else:
206                         raise Exception("TIMEOUT")
207
208         def select(self):
209
210                 self.send_message("SELECTION?")
211                 line = self.get_response()
212                 
213                 try:
214                         m = re.match("\s*(\d+)\s+(\d+)\s*", line)
215                         result = map(int, [m.group(1), m.group(2)])
216                 except:
217                         raise Exception("GIBBERISH \"" + str(line) + "\"")
218                 return result
219
220         def update(self, result):
221                 #print "Update " + str(result) + " called for AgentPlayer"
222                 self.send_message(result)
223                 return result
224
225         def get_move(self):
226                 
227                 self.send_message("MOVE?")
228                 line = self.get_response()
229                 
230                 try:
231                         m = re.match("\s*(\d+)\s+(\d+)\s*", line)
232                         result = map(int, [m.group(1), m.group(2)])
233
234                 except:
235                         raise Exception("GIBBERISH \"" + str(line) + "\"")
236                 return result
237
238         def reset_board(self, s):
239                 self.send_message("BOARD")
240                 for line in s.split("\n"):
241                         self.send_message(line.strip(" \r\n"))
242                 self.send_message("END BOARD")
243
244         def quit(self, final_result):
245                 try:
246                         self.send_message("QUIT " + final_result)
247                 except:
248                         self.p.kill()
249
250 # So you want to be a player here?
251 class HumanPlayer(Player):
252         def __init__(self, name, colour):
253                 Player.__init__(self, name, colour)
254                 
255         # Select your preferred account
256         def select(self):
257                 if isinstance(graphics, GraphicsThread):
258                         # Basically, we let the graphics thread do some shit and then return that information to the game thread
259                         graphics.cond.acquire()
260                         # We wait for the graphics thread to select a piece
261                         while graphics.stopped() == False and graphics.state["select"] == None:
262                                 graphics.cond.wait() # The difference between humans and machines is that humans sleep
263                         select = graphics.state["select"]
264                         
265                         
266                         graphics.cond.release()
267                         if graphics.stopped():
268                                 return [-1,-1]
269                         return [select.x, select.y]
270                 else:
271                         # Since I don't display the board in this case, I'm not sure why I filled it in...
272                         while True:
273                                 sys.stdout.write("SELECTION?\n")
274                                 try:
275                                         p = map(int, sys.stdin.readline().strip("\r\n ").split(" "))
276                                         return p
277                                 except:
278                                         sys.stderr.write("ILLEGAL GIBBERISH\n")
279                                         continue
280
281         # It's your move captain
282         def get_move(self):
283                 if isinstance(graphics, GraphicsThread):
284                         graphics.cond.acquire()
285                         while graphics.stopped() == False and graphics.state["dest"] == None:
286                                 graphics.cond.wait()
287                         graphics.cond.release()
288                         
289                         return graphics.state["dest"]
290                 else:
291
292                         while True:
293                                 sys.stdout.write("MOVE?\n")
294                                 try:
295                                         p = map(int, sys.stdin.readline().strip("\r\n ").split(" "))
296                                         return p
297                                 except:
298                                         sys.stderr.write("ILLEGAL GIBBERISH\n")
299                                         continue
300
301         # Are you sure you want to quit?
302         def quit(self, final_result):
303                 if graphics == None:            
304                         sys.stdout.write("QUIT " + final_result + "\n")
305
306         # Completely useless function
307         def update(self, result):
308                 if isinstance(graphics, GraphicsThread):
309                         pass
310                 else:
311                         sys.stdout.write(result + "\n") 
312                 return result
313
314
315 # Default internal player (makes random moves)
316 class InternalAgent(Player):
317         def __init__(self, name, colour):
318                 Player.__init__(self, name, colour)
319                 self.choice = None
320
321                 self.board = Board(style = "agent")
322
323
324
325         def update(self, result):
326                 
327                 self.board.update(result)
328                 #self.board.verify()
329                 return result
330
331         def reset_board(self, s):
332                 self.board.reset_board(s)
333
334         def quit(self, final_result):
335                 pass
336
337 class AgentRandom(InternalAgent):
338         def __init__(self, name, colour):
339                 InternalAgent.__init__(self, name, colour)
340
341         def select(self):
342                 while True:
343                         self.choice = self.board.pieces[self.colour][random.randint(0, len(self.board.pieces[self.colour])-1)]
344                         all_moves = []
345                         # Check that the piece has some possibility to move
346                         tmp = self.choice.current_type
347                         if tmp == "unknown": # For unknown pieces, try both types
348                                 for t in self.choice.types:
349                                         if t == "unknown":
350                                                 continue
351                                         self.choice.current_type = t
352                                         all_moves += self.board.possible_moves(self.choice)
353                         else:
354                                 all_moves = self.board.possible_moves(self.choice)
355                         self.choice.current_type = tmp
356                         if len(all_moves) > 0:
357                                 break
358                 return [self.choice.x, self.choice.y]
359
360         def get_move(self):
361                 moves = self.board.possible_moves(self.choice)
362                 move = moves[random.randint(0, len(moves)-1)]
363                 return move
364
365
366 # Terrible, terrible hacks
367
368 def run_agent(agent):
369         #sys.stderr.write(sys.argv[0] + " : Running agent " + str(agent) + "\n")
370         while True:
371                 line = sys.stdin.readline().strip(" \r\n")
372                 if line == "SELECTION?":
373                         #sys.stderr.write(sys.argv[0] + " : Make selection\n")
374                         [x,y] = agent.select() # Gets your agent's selection
375                         #sys.stderr.write(sys.argv[0] + " : Selection was " + str(agent.choice) + "\n")
376                         sys.stdout.write(str(x) + " " + str(y) + "\n")                          
377                 elif line == "MOVE?":
378                         #sys.stderr.write(sys.argv[0] + " : Make move\n")
379                         [x,y] = agent.get_move() # Gets your agent's move
380                         sys.stdout.write(str(x) + " " + str(y) + "\n")
381                 elif line.split(" ")[0] == "QUIT":
382                         #sys.stderr.write(sys.argv[0] + " : Quitting\n")
383                         agent.quit(" ".join(line.split(" ")[1:])) # Quits the game
384                         break
385                 elif line.split(" ")[0] == "BOARD":
386                         s = ""
387                         line = sys.stdin.readline().strip(" \r\n")
388                         while line != "END BOARD":
389                                 s += line + "\n"
390                                 line = sys.stdin.readline().strip(" \r\n")
391                         agent.board.reset_board(s)
392                         
393                 else:
394                         agent.update(line) # Updates agent.board
395         return 0
396
397
398 # Sort of works?
399
400 class ExternalWrapper(ExternalAgent):
401         def __init__(self, agent):
402                 run = "python -u -c \"import sys;import os;from qchess import *;agent = " + agent.__class__.__name__ + "('" + agent.name + "','"+agent.colour+"');sys.stdin.readline();sys.exit(run_agent(agent))\""
403                 # str(run)
404                 ExternalAgent.__init__(self, run, agent.colour)
405
406         
407

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