CGI script working; hopefully
[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                 
102                 
103                 
104                 
105         def update(self, result):
106                 sys.stderr.write("update fifo called\n")
107                 try:
108                         self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
109                 except:
110                         raise Exception("FIFO_TIMEOUT")
111                 else:
112                         self.fifo_out.write(result +"\n")
113                         self.fifo_out.close()
114                         return result
115                 
116         def select(self):
117                 sys.stderr.write("select fifo called\n")
118                 try:
119                         self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
120                 except:
121                         #sys.stderr.write("TIMEOUT\n")
122                         raise Exception("FIFO_TIMEOUT")
123                 else:
124                         
125                         self.fifo_out.write("SELECT?\n")
126                         self.fifo_out.close()
127                         self.fifo_in = open_fifo(self.name+".in", "r", FifoPlayer.timeout)
128                         s = map(int, self.fifo_in.readline().strip(" \r\n").split(" "))
129                         self.fifo_in.close()
130                         return s
131         
132         def get_move(self):
133                 sys.stderr.write("get_move fifo called\n")
134                 try:
135                         self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
136                 except:
137                         raise Exception("FIFO_TIMEOUT")
138                 else:
139                         self.fifo_out.write("MOVE?\n")
140                         self.fifo_out.close()
141                         self.fifo_in = open_fifo(self.name+".in", "r", FifoPlayer.timeout)
142                         s = map(int, self.fifo_in.readline().strip(" \r\n").split(" "))
143                         self.fifo_in.close()
144                         return s
145         
146         def quit(self, result):
147                 try:
148                         self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
149                 except:
150                         os.remove(self.name+".in")
151                         os.remove(self.name+".out")
152                         #raise Exception("FIFO_TIMEOUT")
153                         
154                 else:
155                         self.fifo_out.write(result + "\n")
156                         self.fifo_out.close()
157                         os.remove(self.name+".in")
158                         os.remove(self.name+".out")
159
160 # Player that runs from another process
161 class ExternalAgent(Player):
162
163
164         def __init__(self, name, colour):
165                 Player.__init__(self, name, colour)
166                 self.p = subprocess.Popen(name,bufsize=0,stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True,universal_newlines=True)
167                 
168                 self.send_message(colour)
169
170         def send_message(self, s):
171                 if agent_timeout > 0.0:
172                         ready = select.select([], [self.p.stdin], [], agent_timeout)[1]
173                 else:
174                         ready = [self.p.stdin]
175                 if self.p.stdin in ready:
176                         #sys.stderr.write("Writing \'" + s + "\' to " + str(self.p) + "\n")
177                         try:
178                                 self.p.stdin.write(s + "\n")
179                         except:
180                                 raise Exception("UNRESPONSIVE")
181                 else:
182                         raise Exception("TIMEOUT")
183
184         def get_response(self):
185                 if agent_timeout > 0.0:
186                         ready = select.select([self.p.stdout], [], [], agent_timeout)[0]
187                 else:
188                         ready = [self.p.stdout]
189                 if self.p.stdout in ready:
190                         #sys.stderr.write("Reading from " + str(self.p) + " 's stdout...\n")
191                         try:
192                                 result = self.p.stdout.readline().strip(" \t\r\n")
193                                 #sys.stderr.write("Read \'" + result + "\' from " + str(self.p) + "\n")
194                                 return result
195                         except: # Exception, e:
196                                 raise Exception("UNRESPONSIVE")
197                 else:
198                         raise Exception("TIMEOUT")
199
200         def select(self):
201
202                 self.send_message("SELECTION?")
203                 line = self.get_response()
204                 
205                 try:
206                         m = re.match("\s*(\d+)\s+(\d+)\s*", line)
207                         result = map(int, [m.group(1), m.group(2)])
208                 except:
209                         raise Exception("GIBBERISH \"" + str(line) + "\"")
210                 return result
211
212         def update(self, result):
213                 #print "Update " + str(result) + " called for AgentPlayer"
214                 self.send_message(result)
215                 return result
216
217         def get_move(self):
218                 
219                 self.send_message("MOVE?")
220                 line = self.get_response()
221                 
222                 try:
223                         m = re.match("\s*(\d+)\s+(\d+)\s*", line)
224                         result = map(int, [m.group(1), m.group(2)])
225
226                 except:
227                         raise Exception("GIBBERISH \"" + str(line) + "\"")
228                 return result
229
230         def reset_board(self, s):
231                 self.send_message("BOARD")
232                 for line in s.split("\n"):
233                         self.send_message(line.strip(" \r\n"))
234                 self.send_message("END BOARD")
235
236         def quit(self, final_result):
237                 try:
238                         self.send_message("QUIT " + final_result)
239                 except:
240                         self.p.kill()
241
242 # So you want to be a player here?
243 class HumanPlayer(Player):
244         def __init__(self, name, colour):
245                 Player.__init__(self, name, colour)
246                 
247         # Select your preferred account
248         def select(self):
249                 if isinstance(graphics, GraphicsThread):
250                         # Basically, we let the graphics thread do some shit and then return that information to the game thread
251                         graphics.cond.acquire()
252                         # We wait for the graphics thread to select a piece
253                         while graphics.stopped() == False and graphics.state["select"] == None:
254                                 graphics.cond.wait() # The difference between humans and machines is that humans sleep
255                         select = graphics.state["select"]
256                         
257                         
258                         graphics.cond.release()
259                         if graphics.stopped():
260                                 return [-1,-1]
261                         return [select.x, select.y]
262                 else:
263                         # Since I don't display the board in this case, I'm not sure why I filled it in...
264                         while True:
265                                 sys.stdout.write("SELECTION?\n")
266                                 try:
267                                         p = map(int, sys.stdin.readline().strip("\r\n ").split(" "))
268                                 except:
269                                         sys.stderr.write("ILLEGAL GIBBERISH\n")
270                                         continue
271         # It's your move captain
272         def get_move(self):
273                 if isinstance(graphics, GraphicsThread):
274                         graphics.cond.acquire()
275                         while graphics.stopped() == False and graphics.state["dest"] == None:
276                                 graphics.cond.wait()
277                         graphics.cond.release()
278                         
279                         return graphics.state["dest"]
280                 else:
281
282                         while True:
283                                 sys.stdout.write("MOVE?\n")
284                                 try:
285                                         p = map(int, sys.stdin.readline().strip("\r\n ").split(" "))
286                                 except:
287                                         sys.stderr.write("ILLEGAL GIBBERISH\n")
288                                         continue
289
290         # Are you sure you want to quit?
291         def quit(self, final_result):
292                 if graphics == None:            
293                         sys.stdout.write("QUIT " + final_result + "\n")
294
295         # Completely useless function
296         def update(self, result):
297                 if isinstance(graphics, GraphicsThread):
298                         pass
299                 else:
300                         sys.stdout.write(result + "\n") 
301                 return result
302
303
304 # Default internal player (makes random moves)
305 class InternalAgent(Player):
306         def __init__(self, name, colour):
307                 Player.__init__(self, name, colour)
308                 self.choice = None
309
310                 self.board = Board(style = "agent")
311
312
313
314         def update(self, result):
315                 
316                 self.board.update(result)
317                 #self.board.verify()
318                 return result
319
320         def reset_board(self, s):
321                 self.board.reset_board(s)
322
323         def quit(self, final_result):
324                 pass
325
326 class AgentRandom(InternalAgent):
327         def __init__(self, name, colour):
328                 InternalAgent.__init__(self, name, colour)
329
330         def select(self):
331                 while True:
332                         self.choice = self.board.pieces[self.colour][random.randint(0, len(self.board.pieces[self.colour])-1)]
333                         all_moves = []
334                         # Check that the piece has some possibility to move
335                         tmp = self.choice.current_type
336                         if tmp == "unknown": # For unknown pieces, try both types
337                                 for t in self.choice.types:
338                                         if t == "unknown":
339                                                 continue
340                                         self.choice.current_type = t
341                                         all_moves += self.board.possible_moves(self.choice)
342                         else:
343                                 all_moves = self.board.possible_moves(self.choice)
344                         self.choice.current_type = tmp
345                         if len(all_moves) > 0:
346                                 break
347                 return [self.choice.x, self.choice.y]
348
349         def get_move(self):
350                 moves = self.board.possible_moves(self.choice)
351                 move = moves[random.randint(0, len(moves)-1)]
352                 return move
353
354
355 # Terrible, terrible hacks
356
357 def run_agent(agent):
358         #sys.stderr.write(sys.argv[0] + " : Running agent " + str(agent) + "\n")
359         while True:
360                 line = sys.stdin.readline().strip(" \r\n")
361                 if line == "SELECTION?":
362                         #sys.stderr.write(sys.argv[0] + " : Make selection\n")
363                         [x,y] = agent.select() # Gets your agent's selection
364                         #sys.stderr.write(sys.argv[0] + " : Selection was " + str(agent.choice) + "\n")
365                         sys.stdout.write(str(x) + " " + str(y) + "\n")                          
366                 elif line == "MOVE?":
367                         #sys.stderr.write(sys.argv[0] + " : Make move\n")
368                         [x,y] = agent.get_move() # Gets your agent's move
369                         sys.stdout.write(str(x) + " " + str(y) + "\n")
370                 elif line.split(" ")[0] == "QUIT":
371                         #sys.stderr.write(sys.argv[0] + " : Quitting\n")
372                         agent.quit(" ".join(line.split(" ")[1:])) # Quits the game
373                         break
374                 elif line.split(" ")[0] == "BOARD":
375                         s = ""
376                         line = sys.stdin.readline().strip(" \r\n")
377                         while line != "END BOARD":
378                                 s += line + "\n"
379                                 line = sys.stdin.readline().strip(" \r\n")
380                         agent.board.reset_board(s)
381                         
382                 else:
383                         agent.update(line) # Updates agent.board
384         return 0
385
386
387 # Sort of works?
388
389 class ExternalWrapper(ExternalAgent):
390         def __init__(self, agent):
391                 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))\""
392                 # str(run)
393                 ExternalAgent.__init__(self, run, agent.colour)
394
395         
396

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