Final Commit
[progcomp2013.git] / qchess / qchess.py
index 0e03777..1279ec1 100755 (executable)
@@ -652,50 +652,144 @@ class Player():
        def base_player(self):
                return self
 
+
+       
+
+
+def open_fifo(name, mode, timeout=None):
+       if timeout == None:
+               return open(name, mode)
+       
+       
+       class Worker(threading.Thread):
+               def __init__(self):
+                       threading.Thread.__init__(self)
+                       self.result = None
+                       self.exception = None
+
+                       
+               def run(self):          
+                       try:
+                               self.result = open(name, mode)
+                       except Exception, e:
+                               self.exception = e
+                               self.result = None
+               
+
+       w = Worker()
+       w.start()
+       
+       start = time.time()
+       while time.time() - start < timeout:
+               if w.is_alive() == False:
+                       w.join()
+                       if w.exception != None:
+                               raise w.exception
+                       return w.result
+               time.sleep(0.1)
+       
+       
+       if w.is_alive():
+               #sys.stderr.write("FIFO_TIMEOUT!\n")
+               # Recursive to deal with possible race condition
+               try:
+                       if mode == "r":
+                               f = open_fifo(name, "w", 1)
+                       else:
+                               f = open_fifo(name, "r", 1)
+               except:
+                       pass
+                       
+               #sys.stderr.write("Opened other end!\n")
+               while w.is_alive():
+                       time.sleep(0.1)
+                       
+               w.join()
+               f.close()
+               w.result.close()
+               raise Exception("FIFO_TIMEOUT")
+       else:
+               w.join()
+               if w.exception != None:
+                       raise w.exception
+               return w.result
+       
+
 # Player that runs through a fifo
 class FifoPlayer(Player):
+       
+       timeout = 300
+       
        def __init__(self, name, colour):
                Player.__init__(self, name, colour)
                os.mkfifo(self.name+".in")
                os.mkfifo(self.name+".out")
-               
-               
-               
-               
-               
+                       
+
+               try:
+                       self.fifo_out = open_fifo(self.name+".out","w", FifoPlayer.timeout)
+               except:
+                       raise Exception("FIFO_TIMEOUT")
+               else:
+                       self.fifo_out.write("START "+colour+"\n")
+                       self.fifo_out.close()
+
+                               
        def update(self, result):
                sys.stderr.write("update fifo called\n")
-               self.fifo_out = open(self.name+".out", "w")
-               self.fifo_out.write(result +"\n")
-               self.fifo_out.close()
-               return result
+               try:
+                       self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
+               except:
+                       raise Exception("FIFO_TIMEOUT")
+               else:
+                       self.fifo_out.write(result +"\n")
+                       self.fifo_out.close()
+                       return result
                
        def select(self):
                sys.stderr.write("select fifo called\n")
-               self.fifo_out = open(self.name+".out", "w")
-               self.fifo_out.write("SELECT?\n")
-               self.fifo_out.close()
-               self.fifo_in = open(self.name+".in", "r")
-               s = map(int, self.fifo_in.readline().strip(" \r\n").split(" "))
-               self.fifo_in.close()
-               return s
+               try:
+                       self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
+               except:
+                       #sys.stderr.write("TIMEOUT\n")
+                       raise Exception("FIFO_TIMEOUT")
+               else:
+                       
+                       self.fifo_out.write("SELECT?\n")
+                       self.fifo_out.close()
+                       self.fifo_in = open_fifo(self.name+".in", "r", FifoPlayer.timeout)
+                       s = map(int, self.fifo_in.readline().strip(" \r\n").split(" "))
+                       self.fifo_in.close()
+                       return s
        
        def get_move(self):
                sys.stderr.write("get_move fifo called\n")
-               self.fifo_out = open(self.name+".out", "w")
-               self.fifo_out.write("MOVE?\n")
-               self.fifo_out.close()
-               self.fifo_in = open(self.name+".in", "r")
-               s = map(int, self.fifo_in.readline().strip(" \r\n").split(" "))
-               self.fifo_in.close()
-               return s
+               try:
+                       self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
+               except:
+                       raise Exception("FIFO_TIMEOUT")
+               else:
+                       self.fifo_out.write("MOVE?\n")
+                       self.fifo_out.close()
+                       self.fifo_in = open_fifo(self.name+".in", "r", FifoPlayer.timeout)
+                       s = map(int, self.fifo_in.readline().strip(" \r\n").split(" "))
+                       self.fifo_in.close()
+                       return s
        
        def quit(self, result):
-               self.fifo_out = open(self.name+".out", "w")
-               self.fifo_out.write(result + "\n")
-               self.fifo_out.close()
-               os.remove(self.name+".in")
-               os.remove(self.name+".out")
+               try:
+                       self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
+               except:
+                       pass
+               else:
+                       self.fifo_out.write(result + "\n")
+                       self.fifo_out.close()
+
+               try:
+                       os.remove(self.name+".in")
+                       os.remove(self.name+".out")
+               except OSError:
+                       pass
 
 # Player that runs from another process
 class ExternalAgent(Player):
@@ -703,6 +797,7 @@ class ExternalAgent(Player):
 
        def __init__(self, name, colour):
                Player.__init__(self, name, colour)
+               #raise Exception("waht")
                self.p = subprocess.Popen(name,bufsize=0,stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True,universal_newlines=True)
                
                self.send_message(colour)
@@ -784,6 +879,7 @@ class HumanPlayer(Player):
        def __init__(self, name, colour):
                Player.__init__(self, name, colour)
                
+
        # Select your preferred account
        def select(self):
                if isinstance(graphics, GraphicsThread):
@@ -805,9 +901,11 @@ class HumanPlayer(Player):
                                sys.stdout.write("SELECTION?\n")
                                try:
                                        p = map(int, sys.stdin.readline().strip("\r\n ").split(" "))
+                                       return p
                                except:
                                        sys.stderr.write("ILLEGAL GIBBERISH\n")
                                        continue
+
        # It's your move captain
        def get_move(self):
                if isinstance(graphics, GraphicsThread):
@@ -823,6 +921,7 @@ class HumanPlayer(Player):
                                sys.stdout.write("MOVE?\n")
                                try:
                                        p = map(int, sys.stdin.readline().strip("\r\n ").split(" "))
+                                       return p
                                except:
                                        sys.stderr.write("ILLEGAL GIBBERISH\n")
                                        continue
@@ -849,7 +948,8 @@ class InternalAgent(Player):
 
                self.board = Board(style = "agent")
 
-
+       def argForm(self):
+               return "@internal:"+self.name
 
        def update(self, result):
                
@@ -1136,8 +1236,11 @@ class Worker(multiprocessing.Process):
                self.q = q
 
        def run(self):
-               #print str(self) + " runs " + str(self.function) + " with args " + str(self.args) 
+               #print str(self) + " runs " + str(self.function) + " with args " + str(self.args)
+               #try:
                self.q.put(self.function(*self.args))
+               #except IOError:
+               #       pass
                
                
 
@@ -1158,7 +1261,7 @@ def TimeoutFunction(function, args, timeout):
                        w.terminate()
                        s.join()
                        raise Exception("TIMEOUT")
-
+               time.sleep(0.1)
        
                
 
@@ -1335,8 +1438,10 @@ class Network():
                if self.src in ready:
                        s = self.src.recv(1)
                else:
-                       raise Exception("UNRESPONSIVE")
+                       raise Exception("NET_UNRESPONSIVE")
+
 
+               debug("Network get_response s = " + str(s))
 
                while s[len(s)-1] != '\n':
                        # Timeout on each character in the message
@@ -1347,7 +1452,7 @@ class Network():
                        if self.src in ready:
                                s += self.src.recv(1) 
                        else:
-                               raise Exception("UNRESPONSIVE")
+                               raise Exception("NET_UNRESPONSIVE")
 
                
                return s.strip(" \r\n")
@@ -1361,7 +1466,7 @@ class Network():
                if self.src in ready:
                        self.src.send(s + "\n")
                else:
-                       raise Exception("UNRESPONSIVE")
+                       raise Exception("NET_UNRESPONSIVE")
                
                
 
@@ -1383,8 +1488,7 @@ class StoppableThread(threading.Thread):
                self._stop.set()
 
        def stopped(self):
-               return self._stop.isSet()
-# --- thread_util.py --- #
+               return self._stop.isSet()# --- thread_util.py --- #
 log_files = []
 import datetime
 import urllib2
@@ -1586,11 +1690,10 @@ class GameThread(StoppableThread):
                self.cond = threading.Condition() # conditional for some reason, I forgot
                self.final_result = ""
                self.server = server
+               self.retry_illegal = False
                
                
-                       
-               
-               
+       
 
        # Run the game (run in new thread with start(), run in current thread with run())
        def run(self):
@@ -1600,8 +1703,8 @@ class GameThread(StoppableThread):
                        for p in self.players:
                                with self.lock:
                                        self.state["turn"] = p.base_player()
-                               #try:
-                               if True:
+                               try:
+                               #if True:
                                        [x,y] = p.select() # Player selects a square
                                        if self.stopped():
                                                #debug("Quitting in select")
@@ -1703,26 +1806,32 @@ class GameThread(StoppableThread):
                                                        graphics.state["dest"] = None
                                                        graphics.state["moves"] = None
 
-                       # Commented out exception stuff for now, because it makes it impossible to tell if I made an IndentationError somewhere
-                       #       except Exception,e:
-                       #               result = e.message
-                       #               #sys.stderr.write(result + "\n")
-                       #               
-                       #               self.stop()
-                       #               with self.lock:
-                       #                       self.final_result = self.state["turn"].colour + " " + e.message
-
-                               end = self.board.end_condition()
-                               if end != None:         
-                                       with self.lock:
-                                               if end == "DRAW":
-                                                       self.final_result = self.state["turn"].colour + " " + end
-                                               else:
-                                                       self.final_result = end
-                                       self.stop()
+                       
+                                       end = self.board.end_condition()
+                                       if end != None:         
+                                               with self.lock:
+                                                       if end == "DRAW":
+                                                               self.final_result = self.state["turn"].colour + " " + end
+                                                       else:
+                                                               self.final_result = end
+                                               self.stop()
                                
-                               if self.stopped():
-                                       break
+                                       if self.stopped():
+                                               break
+                               except Exception,e:
+                               #if False:
+
+                                       
+                                       result = e.message
+                                       if self.retry_illegal:
+                                               self.state["turn"].update(result);
+                                       else:
+                                               sys.stderr.write("qchess.py exception: "+result + "\n")
+                                               self.stop()
+                                               with self.lock:
+                                                       self.final_result = self.state["turn"].colour + " " + e.message
+                                               break
+
 
 
                for p2 in self.players:
@@ -2501,26 +2610,27 @@ def dedicated_server():
                                                        
        return 0
        
-def client(addr):
-       
+def client(addr, player="@human"):
        
        
+       debug("Client " + player + " starts")
        s = socket.socket()
        s.connect((addr, 4562))
        
        [colour,port] = s.recv(1024).strip(" \r\n").split(" ")
        
-       #debug("Colour: " + colour + ", port: " + port)
+       debug("Colour: " + colour + ", port: " + port)
        
        s.shutdown(socket.SHUT_RDWR)
        s.close()
        
        if colour == "white":
-               p = subprocess.Popen(["python", "qchess.py", "@human", "@network:"+addr+":"+port])
+               p = subprocess.Popen(["python", "qchess.py", player, "@network:"+addr+":"+port])
        else:
-               p = subprocess.Popen(["python", "qchess.py", "@network:"+addr+":"+port, "@human"])
+               p = subprocess.Popen(["python", "qchess.py", "@network:"+addr+":"+port, player])
        p.wait()
-       return 0# --- server.py --- #
+       return 0
+# --- server.py --- #
 #!/usr/bin/python -u
 
 # Do you know what the -u does? It unbuffers stdin and stdout
@@ -2543,6 +2653,7 @@ sleep_timeout = None
 [game, graphics] = [None, None]
 
 def make_player(name, colour):
+       debug(name)
        if name[0] == '@':
                if name[1:] == "human":
                        return HumanPlayer(name, colour)
@@ -2611,6 +2722,7 @@ def main(argv):
        global sleep_timeout
 
 
+       retry_illegal = False
        server_addr = None
 
        max_moves = None
@@ -2632,17 +2744,7 @@ def main(argv):
                i += 1
                arg = argv[i]
                if arg[0] != '-':
-                       p = make_player(arg, colour)
-                       if not isinstance(p, Player):
-                               sys.stderr.write(sys.argv[0] + " : Fatal error creating " + colour + " player\n")
-                               return 100
-                       players.append(p)
-                       if colour == "white":
-                               colour = "black"
-                       elif colour == "black":
-                               pass
-                       else:
-                               sys.stderr.write(sys.argv[0] + " : Too many players (max 2)\n")
+                       players.append(arg)
                        continue
 
                # Option parsing goes here
@@ -2708,7 +2810,8 @@ def main(argv):
                                sleep_timeout = -1
                        else:
                                sleep_timeout = float(arg[2:].split("=")[1])
-                               
+               elif (arg[1] == '-' and arg[2:] == "retry-illegal"):
+                       retry_illegal = not retry_illegal
                elif (arg[1] == '-' and arg[2:] == "help"):
                        # Help
                        os.system("less data/help.txt") # The best help function
@@ -2722,9 +2825,27 @@ def main(argv):
                if server_addr == True:
                        return dedicated_server()
                else:
-                       return client(server_addr)
+                       if len(players) > 1:
+                               sys.stderr.write("Only a single player may be provided when --server is used\n")
+                               return 1
+                       if len(players) == 1:
+                               return client(server_addr, players[0])
+                       else:
+                               return client(server_addr)
+               
+       for i in xrange(len(players)):
+               p = make_player(players[i], colour)
+               if not isinstance(p, Player):
+                       sys.stderr.write(sys.argv[0] + " : Fatal error creating " + colour + " player\n")
+                       return 100
+               players[i] = p
+               if colour == "white":
+                       colour = "black"
+               elif colour == "black":
+                       pass
+               else:
+                       sys.stderr.write(sys.argv[0] + " : Too many players (max 2)\n")
                
-
        # Create the board
        
        # Construct a GameThread! Make it global! Damn the consequences!
@@ -2749,6 +2870,7 @@ def main(argv):
                board = Board(style)
                board.max_moves = max_moves
                game = GameThread(board, players) 
+               game.retry_illegal = retry_illegal
 
 
 
@@ -2891,4 +3013,4 @@ if __name__ == "__main__":
                
 
 # --- main.py --- #
-# EOF - created from make on Thu May 16 23:54:28 WST 2013
+# EOF - created from make on Monday 24 June  23:55:46 WST 2013

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