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

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