fffa73855df09f31f61bfd967764214e47541c78
[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                                         return p
269                                 except:
270                                         sys.stderr.write("ILLEGAL GIBBERISH\n")
271                                         continue
272
273         # It's your move captain
274         def get_move(self):
275                 if isinstance(graphics, GraphicsThread):
276                         graphics.cond.acquire()
277                         while graphics.stopped() == False and graphics.state["dest"] == None:
278                                 graphics.cond.wait()
279                         graphics.cond.release()
280                         
281                         return graphics.state["dest"]
282                 else:
283
284                         while True:
285                                 sys.stdout.write("MOVE?\n")
286                                 try:
287                                         p = map(int, sys.stdin.readline().strip("\r\n ").split(" "))
288                                         return p
289                                 except:
290                                         sys.stderr.write("ILLEGAL GIBBERISH\n")
291                                         continue
292
293         # Are you sure you want to quit?
294         def quit(self, final_result):
295                 if graphics == None:            
296                         sys.stdout.write("QUIT " + final_result + "\n")
297
298         # Completely useless function
299         def update(self, result):
300                 if isinstance(graphics, GraphicsThread):
301                         pass
302                 else:
303                         sys.stdout.write(result + "\n") 
304                 return result
305
306
307 # Default internal player (makes random moves)
308 class InternalAgent(Player):
309         def __init__(self, name, colour):
310                 Player.__init__(self, name, colour)
311                 self.choice = None
312
313                 self.board = Board(style = "agent")
314
315
316
317         def update(self, result):
318                 
319                 self.board.update(result)
320                 #self.board.verify()
321                 return result
322
323         def reset_board(self, s):
324                 self.board.reset_board(s)
325
326         def quit(self, final_result):
327                 pass
328
329 class AgentRandom(InternalAgent):
330         def __init__(self, name, colour):
331                 InternalAgent.__init__(self, name, colour)
332
333         def select(self):
334                 while True:
335                         self.choice = self.board.pieces[self.colour][random.randint(0, len(self.board.pieces[self.colour])-1)]
336                         all_moves = []
337                         # Check that the piece has some possibility to move
338                         tmp = self.choice.current_type
339                         if tmp == "unknown": # For unknown pieces, try both types
340                                 for t in self.choice.types:
341                                         if t == "unknown":
342                                                 continue
343                                         self.choice.current_type = t
344                                         all_moves += self.board.possible_moves(self.choice)
345                         else:
346                                 all_moves = self.board.possible_moves(self.choice)
347                         self.choice.current_type = tmp
348                         if len(all_moves) > 0:
349                                 break
350                 return [self.choice.x, self.choice.y]
351
352         def get_move(self):
353                 moves = self.board.possible_moves(self.choice)
354                 move = moves[random.randint(0, len(moves)-1)]
355                 return move
356
357
358 # Terrible, terrible hacks
359
360 def run_agent(agent):
361         #sys.stderr.write(sys.argv[0] + " : Running agent " + str(agent) + "\n")
362         while True:
363                 line = sys.stdin.readline().strip(" \r\n")
364                 if line == "SELECTION?":
365                         #sys.stderr.write(sys.argv[0] + " : Make selection\n")
366                         [x,y] = agent.select() # Gets your agent's selection
367                         #sys.stderr.write(sys.argv[0] + " : Selection was " + str(agent.choice) + "\n")
368                         sys.stdout.write(str(x) + " " + str(y) + "\n")                          
369                 elif line == "MOVE?":
370                         #sys.stderr.write(sys.argv[0] + " : Make move\n")
371                         [x,y] = agent.get_move() # Gets your agent's move
372                         sys.stdout.write(str(x) + " " + str(y) + "\n")
373                 elif line.split(" ")[0] == "QUIT":
374                         #sys.stderr.write(sys.argv[0] + " : Quitting\n")
375                         agent.quit(" ".join(line.split(" ")[1:])) # Quits the game
376                         break
377                 elif line.split(" ")[0] == "BOARD":
378                         s = ""
379                         line = sys.stdin.readline().strip(" \r\n")
380                         while line != "END BOARD":
381                                 s += line + "\n"
382                                 line = sys.stdin.readline().strip(" \r\n")
383                         agent.board.reset_board(s)
384                         
385                 else:
386                         agent.update(line) # Updates agent.board
387         return 0
388
389
390 # Sort of works?
391
392 class ExternalWrapper(ExternalAgent):
393         def __init__(self, agent):
394                 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))\""
395                 # str(run)
396                 ExternalAgent.__init__(self, run, agent.colour)
397
398         
399

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