+
+
+def TimeoutFunction(function, args, timeout):
+ q = multiprocessing.Queue()
+ w = Worker(function, args, q)
+ s = Sleeper(timeout)
+ w.start()
+ s.start()
+ while True: # Busy loop of crappyness
+ if not w.is_alive():
+ s.terminate()
+ result = q.get()
+ w.join()
+ #print "TimeoutFunction gets " + str(result)
+ return result
+ elif not s.is_alive():
+ w.terminate()
+ s.join()
+ raise Exception("TIMEOUT")
+
+
+
+
+# A player that wraps another player and times out its moves
+# Uses threads
+# A (crappy) alternative to the use of select()
+class TimeoutPlayer(Player):
+ def __init__(self, base_player, timeout):
+ Player.__init__(self, base_player.name, base_player.colour)
+ self.base_player = base_player
+ self.timeout = timeout
+
+ def select(self):
+ return TimeoutFunction(self.base_player.select, [], self.timeout)
+
+
+ def get_move(self):
+ return TimeoutFunction(self.base_player.get_move, [], self.timeout)
+
+ def update(self, result):
+ return TimeoutFunction(self.base_player.update, [result], self.timeout)
+
+ def quit(self, final_result):
+ return TimeoutFunction(self.base_player.quit, [final_result], self.timeout)
+# --- timeout_player.py --- #
+import socket
+import select
+
+network_timeout_start = -1.0 # Timeout in seconds to wait for the start of a message
+network_timeout_delay = 1.0 # Maximum time between two characters being received
+
+class Network():
+ def __init__(self, colour, address = None):
+ self.socket = socket.socket()
+ #self.socket.setblocking(0)
+
+ if colour == "white":
+ self.port = 4562
+ else:
+ self.port = 4563
+
+ self.src = None
+
+ # print str(self) + " listens on port " + str(self.port)
+
+ if address == None:
+ self.host = socket.gethostname()
+ self.socket.bind((self.host, self.port))
+ self.socket.listen(5)
+
+ self.src, self.address = self.socket.accept()
+ self.src.send("ok\n")
+ if self.get_response() == "QUIT":
+ self.src.close()
+ else:
+ self.host = address
+ self.socket.connect((address, self.port))
+ self.src = self.socket
+ self.src.send("ok\n")
+ if self.get_response() == "QUIT":
+ self.src.close()
+
+ def get_response(self):
+ # Timeout the start of the message (first character)
+ if network_timeout_start > 0.0:
+ ready = select.select([self.src], [], [], network_timeout_start)[0]
+ else:
+ ready = [self.src]
+ if self.src in ready:
+ s = self.src.recv(1)
+ else:
+ raise Exception("UNRESPONSIVE")
+
+
+ while s[len(s)-1] != '\n':
+ # Timeout on each character in the message
+ if network_timeout_delay > 0.0:
+ ready = select.select([self.src], [], [], network_timeout_delay)[0]
+ else:
+ ready = [self.src]
+ if self.src in ready:
+ s += self.src.recv(1)
+ else:
+ raise Exception("UNRESPONSIVE")
+
+ return s.strip(" \r\n")
+
+ def send_message(self,s):
+ if network_timeout_start > 0.0:
+ ready = select.select([], [self.src], [], network_timeout_start)[1]
+ else:
+ ready = [self.src]
+
+ if self.src in ready:
+ self.src.send(s + "\n")
+ else:
+ raise Exception("UNRESPONSIVE")
+
+ def check_quit(self, s):
+ s = s.split(" ")
+ if s[0] == "QUIT":
+ with game.lock:
+ game.final_result = " ".join(s[1:]) + " " + str(opponent(self.colour))
+ game.stop()
+ return True
+
+
+
+class NetworkSender(Player,Network):
+ def __init__(self, base_player, address = None):
+ self.base_player = base_player
+ Player.__init__(self, base_player.name, base_player.colour)
+
+ self.address = address
+
+ def connect(self):
+ Network.__init__(self, self.base_player.colour, self.address)
+
+
+
+ def select(self):
+ [x,y] = self.base_player.select()
+ choice = self.board.grid[x][y]
+ s = str(x) + " " + str(y)
+ #print str(self) + ".select sends " + s
+ self.send_message(s)
+ return [x,y]
+
+ def get_move(self):
+ [x,y] = self.base_player.get_move()
+ s = str(x) + " " + str(y)
+ #print str(self) + ".get_move sends " + s
+ self.send_message(s)
+ return [x,y]
+
+ def update(self, s):
+ self.base_player.update(s)
+ s = s.split(" ")
+ [x,y] = map(int, s[0:2])
+ selected = self.board.grid[x][y]
+ if selected != None and selected.colour == self.colour and len(s) > 2 and not "->" in s:
+ s = " ".join(s[0:3])
+ for i in range(2):
+ if selected.types_revealed[i] == True:
+ s += " " + str(selected.types[i])
+ else:
+ s += " unknown"
+ #print str(self) + ".update sends " + s
+ self.send_message(s)
+
+
+ def quit(self, final_result):
+ self.base_player.quit(final_result)
+ #self.src.send("QUIT " + str(final_result) + "\n")
+ self.src.close()
+
+class NetworkReceiver(Player,Network):
+ def __init__(self, colour, address=None):
+
+ Player.__init__(self, address, colour)
+
+ self.address = address
+
+ self.board = None
+
+ def connect(self):
+ Network.__init__(self, self.colour, self.address)
+
+
+ def select(self):
+
+ s = self.get_response()
+ #print str(self) + ".select gets " + s
+ [x,y] = map(int,s.split(" "))
+ if x == -1 and y == -1:
+ #print str(self) + ".select quits the game"
+ with game.lock:
+ game.final_state = "network terminated " + self.colour
+ game.stop()
+ return [x,y]
+ def get_move(self):
+ s = self.get_response()
+ #print str(self) + ".get_move gets " + s
+ [x,y] = map(int,s.split(" "))
+ if x == -1 and y == -1:
+ #print str(self) + ".get_move quits the game"
+ with game.lock:
+ game.final_state = "network terminated " + self.colour
+ game.stop()
+ return [x,y]
+
+ def update(self, result):
+
+ result = result.split(" ")
+ [x,y] = map(int, result[0:2])
+ selected = self.board.grid[x][y]
+ if selected != None and selected.colour == self.colour and len(result) > 2 and not "->" in result:
+ s = self.get_response()
+ #print str(self) + ".update - receives " + str(s)
+ s = s.split(" ")
+ selected.choice = int(s[2])
+ for i in range(2):
+ selected.types[i] = str(s[3+i])
+ if s[3+i] == "unknown":
+ selected.types_revealed[i] = False
+ else:
+ selected.types_revealed[i] = True
+ selected.current_type = selected.types[selected.choice]
+ else:
+ pass
+ #print str(self) + ".update - ignore result " + str(result)
+
+
+ def quit(self, final_result):
+ self.src.close()
+
+# --- network.py --- #
+import threading
+
+# A thread that can be stopped!
+# Except it can only be stopped if it checks self.stopped() periodically
+# So it can sort of be stopped
+class StoppableThread(threading.Thread):
+ def __init__(self):
+ threading.Thread.__init__(self)
+ self._stop = threading.Event()
+
+ def stop(self):
+ self._stop.set()
+
+ def stopped(self):
+ return self._stop.isSet()
+# --- thread_util.py --- #
+log_files = []
+import datetime
+import urllib2
+
+class LogFile():
+ def __init__(self, log):
+
+ self.log = log
+ self.logged = []
+ self.log.write("# Log starts " + str(datetime.datetime.now()) + "\n")
+
+ def write(self, s):
+ now = datetime.datetime.now()
+ self.log.write(str(now) + " : " + s + "\n")
+ self.logged.append((now, s))
+
+ def setup(self, board, players):
+
+ for p in players:
+ self.log.write("# " + p.colour + " : " + p.name + "\n")
+
+ self.log.write("# Initial board\n")
+ for x in range(0, w):
+ for y in range(0, h):
+ if board.grid[x][y] != None:
+ self.log.write(str(board.grid[x][y]) + "\n")
+
+ self.log.write("# Start game\n")
+
+ def close(self):
+ self.log.write("# EOF\n")
+ if self.log != sys.stdout:
+ self.log.close()
+
+class ShortLog(LogFile):
+ def __init__(self, file_name):
+ if file_name == "":
+ self.log = sys.stdout
+ else:
+ self.log = open(file_name, "w", 0)
+ LogFile.__init__(self, self.log)
+ self.file_name = file_name
+ self.phase = 0
+
+ def write(self, s):
+ now = datetime.datetime.now()
+ self.logged.append((now, s))
+
+ if self.phase == 0:
+ if self.log != sys.stdout:
+ self.log.close()
+ self.log = open(self.file_name, "w", 0)
+ self.log.write("# Short log updated " + str(datetime.datetime.now()) + "\n")
+ LogFile.setup(self, game.board, game.players)
+
+ elif self.phase == 1:
+ for message in self.logged[len(self.logged)-2:]:
+ self.log.write(str(message[0]) + " : " + message[1] + "\n")
+
+ self.phase = (self.phase + 1) % 2
+
+ def close(self):
+ if self.phase == 1:
+ ending = self.logged[len(self.logged)-1]
+ self.log.write(str(ending[0]) + " : " + ending[1] + "\n")
+ self.log.write("# EOF\n")
+ if self.log != sys.stdout:
+ self.log.close()
+
+
+class HeadRequest(urllib2.Request):
+ def get_method(self):
+ return "HEAD"
+
+class HttpGetter(StoppableThread):
+ def __init__(self, address):
+ StoppableThread.__init__(self)
+ self.address = address
+ self.log = urllib2.urlopen(address)
+ self.lines = []
+ self.lock = threading.RLock() #lock for access of self.state
+ self.cond = threading.Condition() # conditional
+
+ def run(self):
+ while not self.stopped():
+ line = self.log.readline()
+ if line == "":
+ date_mod = datetime.datetime.strptime(self.log.headers['last-modified'], "%a, %d %b %Y %H:%M:%S GMT")
+ self.log.close()
+
+ next_log = urllib2.urlopen(HeadRequest(self.address))
+ date_new = datetime.datetime.strptime(next_log.headers['last-modified'], "%a, %d %b %Y %H:%M:%S GMT")
+ while date_new <= date_mod and not self.stopped():
+ next_log = urllib2.urlopen(HeadRequest(self.address))
+ date_new = datetime.datetime.strptime(next_log.headers['last-modified'], "%a, %d %b %Y %H:%M:%S GMT")
+ if self.stopped():
+ break
+
+ self.log = urllib2.urlopen(self.address)
+ line = self.log.readline()
+
+ self.cond.acquire()
+ self.lines.append(line)
+ self.cond.notifyAll()
+ self.cond.release()
+
+ #sys.stderr.write(" HttpGetter got \'" + str(line) + "\'\n")
+
+ self.log.close()
+
+
+
+
+
+class HttpReplay():
+ def __init__(self, address):
+ self.getter = HttpGetter(address)
+ self.getter.start()
+
+ def readline(self):
+ self.getter.cond.acquire()
+ while len(self.getter.lines) == 0:
+ self.getter.cond.wait()
+
+ result = self.getter.lines[0]
+ self.getter.lines = self.getter.lines[1:]
+ self.getter.cond.release()
+
+ return result
+
+
+ def close(self):
+ self.getter.stop()
+
+class FileReplay():
+ def __init__(self, filename):
+ self.f = open(filename, "r", 0)
+ self.filename = filename
+ self.mod = os.path.getmtime(filename)
+ self.count = 0
+
+ def readline(self):
+ line = self.f.readline()
+
+ while line == "":
+ mod2 = os.path.getmtime(self.filename)
+ if mod2 > self.mod:
+ #sys.stderr.write("File changed!\n")
+ self.mod = mod2
+ self.f.close()
+ self.f = open(self.filename, "r", 0)
+
+ new_line = self.f.readline()
+
+ if " ".join(new_line.split(" ")[0:3]) != "# Short log":
+ for i in range(self.count):
+ new_line = self.f.readline()
+ #sys.stderr.write("Read back " + str(i) + ": " + str(new_line) + "\n")
+ new_line = self.f.readline()
+ else:
+ self.count = 0
+
+ line = new_line
+
+ self.count += 1
+ return line
+
+ def close(self):
+ self.f.close()
+
+
+def log(s):
+ for l in log_files:
+ l.write(s)
+
+
+def log_init(board, players):
+ for l in log_files:
+ l.setup(board, players)
+
+# --- log.py --- #
+
+
+
+
+
+# A thread that runs the game
+class GameThread(StoppableThread):
+ def __init__(self, board, players):
+ StoppableThread.__init__(self)
+ self.board = board
+ self.players = players
+ self.state = {"turn" : None} # The game state
+ self.error = 0 # Whether the thread exits with an error
+ self.lock = threading.RLock() #lock for access of self.state
+ self.cond = threading.Condition() # conditional for some reason, I forgot
+ self.final_result = ""
+
+
+
+ # Run the game (run in new thread with start(), run in current thread with run())
+ def run(self):
+ result = ""
+ while not self.stopped():
+
+ for p in self.players:
+ with self.lock:
+ if isinstance(p, NetworkSender):
+ self.state["turn"] = p.base_player # "turn" contains the player who's turn it is
+ else:
+ self.state["turn"] = p
+ #try:
+ if True:
+ [x,y] = p.select() # Player selects a square
+ if self.stopped():
+ break
+
+
+
+
+ result = self.board.select(x, y, colour = p.colour)
+ for p2 in self.players:
+ p2.update(result) # Inform players of what happened
+
+
+ log(result)
+
+ target = self.board.grid[x][y]
+ if isinstance(graphics, GraphicsThread):
+ with graphics.lock:
+ graphics.state["moves"] = self.board.possible_moves(target)
+ graphics.state["select"] = target
+
+ time.sleep(turn_delay)
+
+
+ if len(self.board.possible_moves(target)) == 0:
+ #print "Piece cannot move"
+ target.deselect()
+ if isinstance(graphics, GraphicsThread):
+ with graphics.lock:
+ graphics.state["moves"] = None
+ graphics.state["select"] = None
+ graphics.state["dest"] = None
+ continue
+
+ try:
+ [x2,y2] = p.get_move() # Player selects a destination
+ except:
+ self.stop()
+
+ if self.stopped():
+ break
+
+ result = str(x) + " " + str(y) + " -> " + str(x2) + " " + str(y2)
+ log(result)
+
+ self.board.update_move(x, y, x2, y2)
+
+ for p2 in self.players:
+ p2.update(result) # Inform players of what happened
+
+
+
+ if isinstance(graphics, GraphicsThread):
+ with graphics.lock:
+ graphics.state["moves"] = [[x2,y2]]
+
+ time.sleep(turn_delay)
+
+ if isinstance(graphics, GraphicsThread):
+ with graphics.lock:
+ graphics.state["select"] = None
+ 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()
+
+ if self.stopped():
+ break
+
+
+ for p2 in self.players:
+ p2.quit(self.final_result)
+
+ log(self.final_result)
+
+ if isinstance(graphics, GraphicsThread):
+ graphics.stop()
+
+
+# A thread that replays a log file
+class ReplayThread(GameThread):
+ def __init__(self, players, src, end=False,max_moves=None):
+ self.board = Board(style="empty")
+ self.board.max_moves = max_moves
+ GameThread.__init__(self, self.board, players)
+ self.src = src
+ self.end = end
+
+ self.reset_board(self.src.readline())
+
+ def reset_board(self, line):
+ agent_str = ""
+ self_str = ""
+ while line != "# Start game" and line != "# EOF":
+
+ while line == "":
+ line = self.src.readline().strip(" \r\n")
+ continue
+
+ if line[0] == '#':
+ line = self.src.readline().strip(" \r\n")
+ continue
+
+ self_str += line + "\n"
+
+ if self.players[0].name == "dummy" and self.players[1].name == "dummy":
+ line = self.src.readline().strip(" \r\n")
+ continue
+
+ tokens = line.split(" ")
+ types = map(lambda e : e.strip("[] ,'"), tokens[2:4])
+ for i in range(len(types)):
+ if types[i][0] == "?":
+ types[i] = "unknown"
+
+ agent_str += tokens[0] + " " + tokens[1] + " " + str(types) + " ".join(tokens[4:]) + "\n"
+ line = self.src.readline().strip(" \r\n")
+
+ for p in self.players:
+ p.reset_board(agent_str)
+
+
+ self.board.reset_board(self_str)
+
+
+ def run(self):
+ move_count = 0
+ last_line = ""
+ line = self.src.readline().strip(" \r\n")
+ while line != "# EOF":
+
+
+ if self.stopped():
+ break
+
+ if len(line) <= 0:
+ continue
+
+
+ if line[0] == '#':
+ last_line = line
+ line = self.src.readline().strip(" \r\n")
+ continue
+
+ tokens = line.split(" ")
+ if tokens[0] == "white" or tokens[0] == "black":
+ self.reset_board(line)
+ last_line = line
+ line = self.src.readline().strip(" \r\n")
+ continue
+
+ move = line.split(":")
+ move = move[len(move)-1].strip(" \r\n")
+ tokens = move.split(" ")
+
+
+ try:
+ [x,y] = map(int, tokens[0:2])
+ except:
+ last_line = line
+ self.stop()
+ break
+
+ log(move)
+
+ target = self.board.grid[x][y]
+ with self.lock:
+ if target.colour == "white":
+ self.state["turn"] = self.players[0]
+ else:
+ self.state["turn"] = self.players[1]
+
+ move_piece = (tokens[2] == "->")
+ if move_piece:
+ [x2,y2] = map(int, tokens[len(tokens)-2:])
+
+ if isinstance(graphics, GraphicsThread):
+ with graphics.lock:
+ graphics.state["select"] = target
+
+ if not move_piece:
+ self.board.update_select(x, y, int(tokens[2]), tokens[len(tokens)-1])
+ if isinstance(graphics, GraphicsThread):
+ with graphics.lock:
+ if target.current_type != "unknown":
+ graphics.state["moves"] = self.board.possible_moves(target)
+ else:
+ graphics.state["moves"] = None
+ time.sleep(turn_delay)
+ else:
+ self.board.update_move(x, y, x2, y2)
+ if isinstance(graphics, GraphicsThread):
+ with graphics.lock:
+ graphics.state["moves"] = [[x2,y2]]
+ time.sleep(turn_delay)
+ with graphics.lock:
+ graphics.state["select"] = None
+ graphics.state["moves"] = None
+ graphics.state["dest"] = None
+
+
+
+
+
+ for p in self.players:
+ p.update(move)
+
+ last_line = line
+ line = self.src.readline().strip(" \r\n")
+
+
+ end = self.board.end_condition()
+ if end != None:
+ self.final_result = end
+ self.stop()
+ break
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ if self.end and isinstance(graphics, GraphicsThread):
+ #graphics.stop()
+ pass # Let the user stop the display
+ elif not self.end and self.board.end_condition() == None:
+ global game
+ # Work out the last move
+
+ t = last_line.split(" ")
+ if t[len(t)-2] == "black":
+ self.players.reverse()
+ elif t[len(t)-2] == "white":
+ pass
+ elif self.state["turn"] != None and self.state["turn"].colour == "white":
+ self.players.reverse()
+
+
+ game = GameThread(self.board, self.players)
+ game.run()
+ else:
+ pass
+
+
+
+def opponent(colour):
+ if colour == "white":
+ return "black"
+ else:
+ return "white"
+# --- game.py --- #
+try:
+ import pygame
+except:
+ pass
+import os
+
+# Dictionary that stores the unicode character representations of the different pieces
+# Chess was clearly the reason why unicode was invented
+# For some reason none of the pygame chess implementations I found used them!
+piece_char = {"white" : {"king" : u'\u2654',
+ "queen" : u'\u2655',
+ "rook" : u'\u2656',
+ "bishop" : u'\u2657',
+ "knight" : u'\u2658',
+ "pawn" : u'\u2659',
+ "unknown" : '?'},
+ "black" : {"king" : u'\u265A',
+ "queen" : u'\u265B',
+ "rook" : u'\u265C',
+ "bishop" : u'\u265D',
+ "knight" : u'\u265E',
+ "pawn" : u'\u265F',
+ "unknown" : '?'}}
+
+images = {"white" : {}, "black" : {}}
+small_images = {"white" : {}, "black" : {}}
+
+def create_images(grid_sz, font_name=os.path.join(os.path.curdir, "data", "DejaVuSans.ttf")):
+
+ # Get the font sizes
+ l_size = 5*(grid_sz[0] / 8)
+ s_size = 3*(grid_sz[0] / 8)
+
+ for c in piece_char.keys():
+
+ if c == "black":
+ for p in piece_char[c].keys():
+ images[c].update({p : pygame.font.Font(font_name, l_size).render(piece_char[c][p], True,(0,0,0))})
+ small_images[c].update({p : pygame.font.Font(font_name, s_size).render(piece_char[c][p],True,(0,0,0))})
+ elif c == "white":
+ for p in piece_char[c].keys():
+ images[c].update({p : pygame.font.Font(font_name, l_size+1).render(piece_char["black"][p], True,(255,255,255))})
+ images[c][p].blit(pygame.font.Font(font_name, l_size).render(piece_char[c][p], True,(0,0,0)),(0,0))
+ small_images[c].update({p : pygame.font.Font(font_name, s_size+1).render(piece_char["black"][p],True,(255,255,255))})
+ small_images[c][p].blit(pygame.font.Font(font_name, s_size).render(piece_char[c][p],True,(0,0,0)),(0,0))
+
+
+def load_images(image_dir=os.path.join(os.path.curdir, "data", "images")):
+ if not os.path.exists(image_dir):
+ raise Exception("Couldn't load images from " + image_dir + " (path doesn't exist)")
+ for c in piece_char.keys():
+ for p in piece_char[c].keys():
+ images[c].update({p : pygame.image.load(os.path.join(image_dir, c + "_" + p + ".png"))})
+ small_images[c].update({p : pygame.image.load(os.path.join(image_dir, c + "_" + p + "_small.png"))})
+# --- images.py --- #
+graphics_enabled = True
+try:
+ import pygame
+except:
+ graphics_enabled = False
+
+
+
+
+# A thread to make things pretty
+class GraphicsThread(StoppableThread):
+ def __init__(self, board, title = "UCC::Progcomp 2013 - QChess", grid_sz = [80,80]):
+ StoppableThread.__init__(self)
+
+ self.board = board
+ pygame.init()
+ self.window = pygame.display.set_mode((grid_sz[0] * w, grid_sz[1] * h))