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

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