diff --git a/agents/bishop.py b/agents/bishop.py index 8c88a1f5c9bd6227eb181a9acfe6e943c09c47e8..3c7738e540bc9d17f2496d6ff6e137089e07998a 100755 --- a/agents/bishop.py +++ b/agents/bishop.py @@ -291,8 +291,8 @@ def main(argv): agent = Agent(argv[0], colour) # Creates your agent - graphics = AgentGraphics(agent.board, title="Agent Bishop (" + str(colour) + ") - DEBUG VIEW") - graphics.start() + #graphics = AgentGraphics(agent.board, title="Agent Bishop (" + str(colour) + ") - DEBUG VIEW") + #graphics.start() # Plays quantum chess using your agent while True: @@ -312,8 +312,8 @@ def main(argv): else: agent.update(line) # Updates agent.board - graphics.stop() - graphics.join() + #graphics.stop() + #graphics.join() return 0 # Don't touch this diff --git a/agents/win32_bishop.bat b/agents/win32_bishop.bat new file mode 100755 index 0000000000000000000000000000000000000000..92d9440cdb3487b817a4047dd2555e3f931456c0 --- /dev/null +++ b/agents/win32_bishop.bat @@ -0,0 +1,4 @@ +:: Replace the path to the python interpreter if necessary +:: Picking this in qchess *should* work for running bishop.py +:: I hope +C:\\Python27\\python.exe bishop.py diff --git a/qchess/Makefile b/qchess/Makefile index 2ad3fefe0ff88665f8af00fe8e25317385f7fcfa..9da58858ffaa9a10c5691c1b62c1ca47c42545d2 100644 --- a/qchess/Makefile +++ b/qchess/Makefile @@ -1,16 +1,40 @@ -# Makefile that builds qchess.py from the component files +# Makefile that builds qchess for UCC::Progcomp2013 -TARGET = qchess.py -COMPONENTS = piece.py board.py player.py network.py thread_util.py game.py graphics.py main.py -#COMPONENTS=$(shell ls *.py | tr '\t' '\n' | grep -v $(TARGET)) +SCRIPT=qchess.py +DLL_PATH=win32_dll -$(TARGET) : $(COMPONENTS) - echo "#!/usr/bin/python -u" > $(TARGET) - for f in $(COMPONENTS); do echo "# +++ $$f +++ #" >> $(TARGET); cat $$f >> $(TARGET); echo "# --- $$f --- #" >> $(TARGET); done - echo "# EOF - created from make on $$(date)" >> $(TARGET) - chmod u+x $(TARGET) +all : python_native frozen + +frozen : win32_frozen linux64_frozen + cd build; for d in $$(ls); do zip -r $$d.zip $$d; rm -r $$d; done + +python_native : + make -C src + mv src/$(SCRIPT) ./ + +images : + cd tools; python image_builder.py + +win32_frozen : $(SCRIPT) images + sed -i 's:create_images(grid_sz):load_images():g' $(SCRIPT) + wine "C:\\Python27\\python.exe" build.py build + for d in $$(ls $(DLL_PATH)); do cp $(DLL_PATH)/$$d build/exe.win32-2.7/; done + for b in $$(ls build); do if [ -d build/$$b ]; then cp -r data build/$$b; fi; done + sed -i 's:load_images():create_images(grid_sz):g' $(SCRIPT) + +linux64_frozen : $(SCRIPT) images + sed -i 's:create_images(grid_sz):load_images():g' $(SCRIPT) + python build.py build + for b in $$(ls build); do if [ -d build/$$b ]; then cp -r data build/$$b; fi; done + sed -i 's:load_images():create_images(grid_sz):g' $(SCRIPT) + + clean : + make -C src clean + rm -f $(SCRIPT) rm -f *~ - rm -f *.pyc - rm -f $(TARGET) + rm -rf build + rm -rf data/images + rm -f tools/*~ + rm -f tools/*.pyc diff --git a/qchess/build.py b/qchess/build.py new file mode 100644 index 0000000000000000000000000000000000000000..9d854ddd28febaae33d73f38f8c3f5918460ad4c --- /dev/null +++ b/qchess/build.py @@ -0,0 +1,4 @@ +import sys +from cx_Freeze import setup, Executable + +setup(name="qchess", executables=[Executable("qchess.py")]) diff --git a/qchess/build/exe.linux-x86_64-2.7.zip b/qchess/build/exe.linux-x86_64-2.7.zip new file mode 100644 index 0000000000000000000000000000000000000000..41103d12600d24adbce56639cff4d43c26033861 Binary files /dev/null and b/qchess/build/exe.linux-x86_64-2.7.zip differ diff --git a/qchess/build/exe.win32-2.7.zip b/qchess/build/exe.win32-2.7.zip new file mode 100644 index 0000000000000000000000000000000000000000..4ef51c0f37a7af4ca804a2b82b809606d7fd61b4 Binary files /dev/null and b/qchess/build/exe.win32-2.7.zip differ diff --git a/qchess/qchess.py b/qchess/qchess.py index 501b3fd8e64a05bfca256c52cea94aea335118f8..8347a753272162510b1d535035c34ee33473d804 100755 --- a/qchess/qchess.py +++ b/qchess/qchess.py @@ -1,5 +1,4 @@ #!/usr/bin/python -u -# +++ piece.py +++ # import random # I know using non-abreviated strings is inefficient, but this is python, who cares? @@ -96,7 +95,6 @@ class Piece(): # The sad moment when you realise that you do not understand anything about a subject you studied for 4 years... # --- piece.py --- # -# +++ board.py +++ # [w,h] = [8,8] # Width and height of board(s) # Class to represent a quantum chess board @@ -501,11 +499,11 @@ class Board(): def on_board(self, x, y): return (x >= 0 and x < w) and (y >= 0 and y < h) # --- board.py --- # -# +++ player.py +++ # import subprocess import select import platform + agent_timeout = -1.0 # Timeout in seconds for AI players to make moves # WARNING: Won't work for windows based operating systems @@ -689,8 +687,80 @@ class AgentRandom(Player): def quit(self, final_result): pass + + # --- player.py --- # -# +++ network.py +++ # +import multiprocessing + +# Hacky alternative to using select for timing out players + +# WARNING: Do not wrap around HumanPlayer or things breakify + +class Sleeper(multiprocessing.Process): + def __init__(self, timeout): + multiprocessing.Process.__init__(self) + self.timeout = timeout + + def run(self): + time.sleep(self.timeout) + + +class Worker(multiprocessing.Process): + def __init__(self, function, args, q): + multiprocessing.Process.__init__(self) + self.function = function + self.args = args + self.q = q + + def run(self): + #print str(self) + " runs " + str(self.function) + " with args " + str(self.args) + self.q.put(self.function(*self.args)) + + + +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("UNRESPONSIVE") + + + + +# 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 @@ -883,7 +953,6 @@ class NetworkReceiver(Player,Network): self.src.close() # --- network.py --- # -# +++ thread_util.py +++ # import threading # A thread that can be stopped! @@ -900,7 +969,6 @@ class StoppableThread(threading.Thread): def stopped(self): return self._stop.isSet() # --- thread_util.py --- # -# +++ game.py +++ # # A thread that runs the game class GameThread(StoppableThread): @@ -925,7 +993,8 @@ class GameThread(StoppableThread): self.state["turn"] = p.base_player # "turn" contains the player who's turn it is else: self.state["turn"] = p - try: + #try: + if True: [x,y] = p.select() # Player selects a square if self.stopped(): break @@ -983,13 +1052,13 @@ class GameThread(StoppableThread): 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 + # 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 if self.board.king["black"] == None: if self.board.king["white"] == None: @@ -1023,8 +1092,8 @@ def opponent(colour): else: return "white" # --- game.py --- # -# +++ graphics.py +++ # import pygame +import os # Dictionary that stores the unicode character representations of the different pieces # Chess was clearly the reason why unicode was invented @@ -1047,6 +1116,44 @@ piece_char = {"white" : {"king" : u'\u2654', 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 --- # +import pygame + + + + + + + + + # A thread to make things pretty class GraphicsThread(StoppableThread): def __init__(self, board, title = "UCC::Progcomp 2013 - QChess", grid_sz = [80,80]): @@ -1056,25 +1163,27 @@ class GraphicsThread(StoppableThread): pygame.init() self.window = pygame.display.set_mode((grid_sz[0] * w, grid_sz[1] * h)) pygame.display.set_caption(title) + + #print "Initialised properly" + self.grid_sz = grid_sz[:] self.state = {"select" : None, "dest" : None, "moves" : None, "overlay" : None, "coverage" : None} self.error = 0 self.lock = threading.RLock() self.cond = threading.Condition() - # Get the font sizes - l_size = 5*(self.grid_sz[0] / 8) - s_size = 3*(self.grid_sz[0] / 8) - for p in piece_types.keys(): - c = "black" - images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", l_size).render(piece_char[c][p], True,(0,0,0))}) - small_images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", s_size).render(piece_char[c][p],True,(0,0,0))}) - c = "white" + #print "Test font" + pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), 32).render("Hello", True,(0,0,0)) + + #create_images(grid_sz) + create_images(grid_sz) - images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", l_size+1).render(piece_char["black"][p], True,(255,255,255))}) - images[c][p].blit(pygame.font.Font("data/DejaVuSans.ttf", l_size).render(piece_char[c][p], True,(0,0,0)),(0,0)) - small_images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", s_size+1).render(piece_char["black"][p],True,(255,255,255))}) - small_images[c][p].blit(pygame.font.Font("data/DejaVuSans.ttf", s_size).render(piece_char[c][p],True,(0,0,0)),(0,0)) + """ + for c in images.keys(): + for p in images[c].keys(): + images[c][p] = images[c][p].convert(self.window) + small_images[c][p] = small_images[c][p].convert(self.window) + """ @@ -1085,10 +1194,13 @@ class GraphicsThread(StoppableThread): while not self.stopped(): + #print "Display grid" self.board.display_grid(window = self.window, grid_sz = self.grid_sz) # Draw the board + #print "Display overlay" self.overlay() + #print "Display pieces" self.board.display_pieces(window = self.window, grid_sz = self.grid_sz) # Draw the board pygame.display.flip() @@ -1238,7 +1350,7 @@ class GraphicsThread(StoppableThread): mp = [self.grid_sz[i] * [x,y][i] for i in range(2)] square_img.fill(pygame.Color(255,0,255,int(m[x][y] * 128))) # Draw in purple self.window.blit(square_img, mp) - font = pygame.font.Font(None, 14) + font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), 14) text = font.render("{0:.2f}".format(round(m[x][y],2)), 1, pygame.Color(0,0,0)) self.window.blit(text, mp) @@ -1250,7 +1362,7 @@ class GraphicsThread(StoppableThread): mp = [self.grid_sz[i] * [p.x,p.y][i] for i in range(2)] square_img.fill(pygame.Color(0,255,255, int(m[p] * 196))) # Draw in pale blue self.window.blit(square_img, mp) - font = pygame.font.Font(None, 14) + font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), 14) text = font.render("{0:.2f}".format(round(m[p],2)), 1, pygame.Color(0,0,0)) self.window.blit(text, mp) # Draw a square where the mouse is @@ -1273,8 +1385,9 @@ class GraphicsThread(StoppableThread): self.window.blit(square_img, mp) # Message in a bottle - def message(self, string, pos = None, colour = None, font_size = 32): - font = pygame.font.Font(None, font_size) + def message(self, string, pos = None, colour = None, font_size = 20): + #print "Drawing message..." + font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), font_size) if colour == None: colour = pygame.Color(0,0,0) @@ -1332,11 +1445,13 @@ class GraphicsThread(StoppableThread): # Function to pick a button - def SelectButton(self, choices, prompt = None, font_size=32): + def SelectButton(self, choices, prompt = None, font_size=20): + + #print "Select button called!" self.board.display_grid(self.window, self.grid_sz) if prompt != None: self.message(prompt) - font = pygame.font.Font(None, font_size) + font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), font_size) targets = [] sz = self.window.get_size() @@ -1385,6 +1500,7 @@ class GraphicsThread(StoppableThread): def SelectPlayers(self, players = []): + #print "SelectPlayers called" missing = ["white", "black"] for p in players: @@ -1393,11 +1509,11 @@ class GraphicsThread(StoppableThread): for colour in missing: - choice = self.SelectButton(["human", "agent", "network"],prompt = "Choose " + str(colour) + " player", font_size=32) + choice = self.SelectButton(["human", "agent", "network"],prompt = "Choose " + str(colour) + " player") if choice == 0: players.append(HumanPlayer("human", colour)) elif choice == 1: - try: + if True: import Tkinter from tkFileDialog import askopenfilename root = Tkinter.Tk() # Need a root to make Tkinter behave @@ -1407,7 +1523,7 @@ class GraphicsThread(StoppableThread): if path == "": return self.SelectPlayers() players.append(make_player(path, colour)) - except Exception,e: + else: print "Exception was " + str(e.message) p = None while p == None: @@ -1456,7 +1572,6 @@ class GraphicsThread(StoppableThread): # --- graphics.py --- # -# +++ main.py +++ # #!/usr/bin/python -u # Do you know what the -u does? It unbuffers stdin and stdout @@ -1558,11 +1673,8 @@ def main(argv): # Timeout if len(arg[2:].split("=")) == 1: agent_timeout = -1 - elif platform.system() != "Windows": # Windows breaks this option - agent_timeout = float(arg[2:].split("=")[1]) else: - sys.stderr.write(sys.argv[0] + " : Warning - You are using Windows\n") - agent_timeout = -1 + agent_timeout = float(arg[2:].split("=")[1]) elif (arg[1] == '-' and arg[2:] == "help"): # Help @@ -1578,6 +1690,7 @@ def main(argv): if graphics_enabled == True: try: graphics = GraphicsThread(board, grid_sz = [64,64]) # Construct a GraphicsThread! + except Exception,e: graphics = None sys.stderr.write(sys.argv[0] + " : Got exception trying to initialise graphics\n"+str(e.message)+"\nDisabled graphics\n") @@ -1617,6 +1730,22 @@ def main(argv): graphics.message("Connecting to " + p.colour + " player...") p.connect() + + # If using windows, select won't work; use horrible TimeoutPlayer hack + if agent_timeout > 0 and platform.system() == "Windows": + sys.stderr.write(sys.argv[0] + " : Warning - You are using Windows\n") + sys.stderr.write(sys.argv[0] + " : - Timeouts will be implemented with a terrible hack.\n") + + for i in range(len(players)): + if isinstance(players[i], AgentPlayer): + players[i] = TimeoutPlayer(players[i], agent_timeout) + + # Could potentially wrap TimeoutPlayer around internal classes... + # But that would suck + + + + # Construct a GameThread! Make it global! Damn the consequences! game = GameThread(board, players) @@ -1636,4 +1765,4 @@ def main(argv): if __name__ == "__main__": sys.exit(main(sys.argv)) # --- main.py --- # -# EOF - created from make on Thu Jan 24 17:04:54 WST 2013 +# EOF - created from make on Mon Jan 28 22:52:28 WST 2013 diff --git a/qchess/src/Makefile b/qchess/src/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..8ed1944638d7bf27e0d9570faa3c5b14043acc23 --- /dev/null +++ b/qchess/src/Makefile @@ -0,0 +1,16 @@ +# Makefile that builds qchess.py from the component files + +TARGET = qchess.py +COMPONENTS = piece.py board.py player.py timeout_player.py network.py thread_util.py game.py images.py graphics.py main.py +#COMPONENTS=$(shell ls *.py | tr '\t' '\n' | grep -v $(TARGET)) + +$(TARGET) : $(COMPONENTS) + echo "#!/usr/bin/python -u" > $(TARGET) + for f in $(COMPONENTS); do echo "# +++ $$f +++ #" >> ../$(TARGET); cat $$f >> $(TARGET); echo "# --- $$f --- #" >> $(TARGET); done + echo "# EOF - created from make on $$(date)" >> $(TARGET) + chmod u+x $(TARGET) + +clean : + rm -f *~ + rm -f *.pyc + rm -f $(TARGET) diff --git a/qchess/board.py b/qchess/src/board.py similarity index 100% rename from qchess/board.py rename to qchess/src/board.py diff --git a/qchess/game.py b/qchess/src/game.py similarity index 92% rename from qchess/game.py rename to qchess/src/game.py index efca0f8a6825fb6997f176c8f3fed9ce7735fc3d..934c043d5cbd8f63ecefadc59e887a7f24db46e8 100644 --- a/qchess/game.py +++ b/qchess/src/game.py @@ -22,7 +22,8 @@ class GameThread(StoppableThread): self.state["turn"] = p.base_player # "turn" contains the player who's turn it is else: self.state["turn"] = p - try: + #try: + if True: [x,y] = p.select() # Player selects a square if self.stopped(): break @@ -80,13 +81,13 @@ class GameThread(StoppableThread): 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 + # 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 if self.board.king["black"] == None: if self.board.king["white"] == None: diff --git a/qchess/graphics.py b/qchess/src/graphics.py similarity index 85% rename from qchess/graphics.py rename to qchess/src/graphics.py index 8e2a5fae864de8289b45708bad12437c3d993449..c068900e64b5d6dbfc517765a7c84f2dd787de64 100644 --- a/qchess/graphics.py +++ b/qchess/src/graphics.py @@ -1,25 +1,12 @@ import pygame -# 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" : {}} + + + + + + + # A thread to make things pretty class GraphicsThread(StoppableThread): @@ -30,25 +17,27 @@ class GraphicsThread(StoppableThread): pygame.init() self.window = pygame.display.set_mode((grid_sz[0] * w, grid_sz[1] * h)) pygame.display.set_caption(title) + + #print "Initialised properly" + self.grid_sz = grid_sz[:] self.state = {"select" : None, "dest" : None, "moves" : None, "overlay" : None, "coverage" : None} self.error = 0 self.lock = threading.RLock() self.cond = threading.Condition() - # Get the font sizes - l_size = 5*(self.grid_sz[0] / 8) - s_size = 3*(self.grid_sz[0] / 8) - for p in piece_types.keys(): - c = "black" - images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", l_size).render(piece_char[c][p], True,(0,0,0))}) - small_images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", s_size).render(piece_char[c][p],True,(0,0,0))}) - c = "white" + #print "Test font" + pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), 32).render("Hello", True,(0,0,0)) - images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", l_size+1).render(piece_char["black"][p], True,(255,255,255))}) - images[c][p].blit(pygame.font.Font("data/DejaVuSans.ttf", l_size).render(piece_char[c][p], True,(0,0,0)),(0,0)) - small_images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", s_size+1).render(piece_char["black"][p],True,(255,255,255))}) - small_images[c][p].blit(pygame.font.Font("data/DejaVuSans.ttf", s_size).render(piece_char[c][p],True,(0,0,0)),(0,0)) + #load_images() + create_images(grid_sz) + + """ + for c in images.keys(): + for p in images[c].keys(): + images[c][p] = images[c][p].convert(self.window) + small_images[c][p] = small_images[c][p].convert(self.window) + """ @@ -59,10 +48,13 @@ class GraphicsThread(StoppableThread): while not self.stopped(): + #print "Display grid" self.board.display_grid(window = self.window, grid_sz = self.grid_sz) # Draw the board + #print "Display overlay" self.overlay() + #print "Display pieces" self.board.display_pieces(window = self.window, grid_sz = self.grid_sz) # Draw the board pygame.display.flip() @@ -212,7 +204,7 @@ class GraphicsThread(StoppableThread): mp = [self.grid_sz[i] * [x,y][i] for i in range(2)] square_img.fill(pygame.Color(255,0,255,int(m[x][y] * 128))) # Draw in purple self.window.blit(square_img, mp) - font = pygame.font.Font(None, 14) + font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), 14) text = font.render("{0:.2f}".format(round(m[x][y],2)), 1, pygame.Color(0,0,0)) self.window.blit(text, mp) @@ -224,7 +216,7 @@ class GraphicsThread(StoppableThread): mp = [self.grid_sz[i] * [p.x,p.y][i] for i in range(2)] square_img.fill(pygame.Color(0,255,255, int(m[p] * 196))) # Draw in pale blue self.window.blit(square_img, mp) - font = pygame.font.Font(None, 14) + font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), 14) text = font.render("{0:.2f}".format(round(m[p],2)), 1, pygame.Color(0,0,0)) self.window.blit(text, mp) # Draw a square where the mouse is @@ -247,8 +239,9 @@ class GraphicsThread(StoppableThread): self.window.blit(square_img, mp) # Message in a bottle - def message(self, string, pos = None, colour = None, font_size = 32): - font = pygame.font.Font(None, font_size) + def message(self, string, pos = None, colour = None, font_size = 20): + #print "Drawing message..." + font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), font_size) if colour == None: colour = pygame.Color(0,0,0) @@ -306,11 +299,13 @@ class GraphicsThread(StoppableThread): # Function to pick a button - def SelectButton(self, choices, prompt = None, font_size=32): + def SelectButton(self, choices, prompt = None, font_size=20): + + #print "Select button called!" self.board.display_grid(self.window, self.grid_sz) if prompt != None: self.message(prompt) - font = pygame.font.Font(None, font_size) + font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), font_size) targets = [] sz = self.window.get_size() @@ -359,6 +354,7 @@ class GraphicsThread(StoppableThread): def SelectPlayers(self, players = []): + #print "SelectPlayers called" missing = ["white", "black"] for p in players: @@ -367,11 +363,11 @@ class GraphicsThread(StoppableThread): for colour in missing: - choice = self.SelectButton(["human", "agent", "network"],prompt = "Choose " + str(colour) + " player", font_size=32) + choice = self.SelectButton(["human", "agent", "network"],prompt = "Choose " + str(colour) + " player") if choice == 0: players.append(HumanPlayer("human", colour)) elif choice == 1: - try: + if True: import Tkinter from tkFileDialog import askopenfilename root = Tkinter.Tk() # Need a root to make Tkinter behave @@ -381,7 +377,7 @@ class GraphicsThread(StoppableThread): if path == "": return self.SelectPlayers() players.append(make_player(path, colour)) - except Exception,e: + else: print "Exception was " + str(e.message) p = None while p == None: diff --git a/qchess/src/images.py b/qchess/src/images.py new file mode 100644 index 0000000000000000000000000000000000000000..dd79b107c71d94e61cc7ac7f26b19a773209928c --- /dev/null +++ b/qchess/src/images.py @@ -0,0 +1,51 @@ +import pygame +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"))}) diff --git a/qchess/main.py b/qchess/src/main.py similarity index 89% rename from qchess/main.py rename to qchess/src/main.py index 82aad82fd66545a64335fe6a113c8c801e3a4945..8dac056306f38d70b6f725c25e950f56c640b9af 100644 --- a/qchess/main.py +++ b/qchess/src/main.py @@ -99,11 +99,8 @@ def main(argv): # Timeout if len(arg[2:].split("=")) == 1: agent_timeout = -1 - elif platform.system() != "Windows": # Windows breaks this option - agent_timeout = float(arg[2:].split("=")[1]) else: - sys.stderr.write(sys.argv[0] + " : Warning - You are using Windows\n") - agent_timeout = -1 + agent_timeout = float(arg[2:].split("=")[1]) elif (arg[1] == '-' and arg[2:] == "help"): # Help @@ -119,6 +116,7 @@ def main(argv): if graphics_enabled == True: try: graphics = GraphicsThread(board, grid_sz = [64,64]) # Construct a GraphicsThread! + except Exception,e: graphics = None sys.stderr.write(sys.argv[0] + " : Got exception trying to initialise graphics\n"+str(e.message)+"\nDisabled graphics\n") @@ -158,6 +156,22 @@ def main(argv): graphics.message("Connecting to " + p.colour + " player...") p.connect() + + # If using windows, select won't work; use horrible TimeoutPlayer hack + if agent_timeout > 0 and platform.system() == "Windows": + sys.stderr.write(sys.argv[0] + " : Warning - You are using Windows\n") + sys.stderr.write(sys.argv[0] + " : - Timeouts will be implemented with a terrible hack.\n") + + for i in range(len(players)): + if isinstance(players[i], AgentPlayer): + players[i] = TimeoutPlayer(players[i], agent_timeout) + + # Could potentially wrap TimeoutPlayer around internal classes... + # But that would suck + + + + # Construct a GameThread! Make it global! Damn the consequences! game = GameThread(board, players) diff --git a/qchess/network.py b/qchess/src/network.py similarity index 100% rename from qchess/network.py rename to qchess/src/network.py diff --git a/qchess/piece.py b/qchess/src/piece.py similarity index 100% rename from qchess/piece.py rename to qchess/src/piece.py diff --git a/qchess/player.py b/qchess/src/player.py similarity index 99% rename from qchess/player.py rename to qchess/src/player.py index ce0ec71f81d4baa47d970bb422d465e7da8db13a..3e6fabadb566b04e856d3ea4e9709345b7498558 100644 --- a/qchess/player.py +++ b/qchess/src/player.py @@ -2,6 +2,7 @@ import subprocess import select import platform + agent_timeout = -1.0 # Timeout in seconds for AI players to make moves # WARNING: Won't work for windows based operating systems @@ -185,3 +186,5 @@ class AgentRandom(Player): def quit(self, final_result): pass + + diff --git a/qchess/thread_util.py b/qchess/src/thread_util.py similarity index 100% rename from qchess/thread_util.py rename to qchess/src/thread_util.py diff --git a/qchess/src/timeout_player.py b/qchess/src/timeout_player.py new file mode 100644 index 0000000000000000000000000000000000000000..36f9e204a90587cbdb4e20c71ae65da30e7e20a6 --- /dev/null +++ b/qchess/src/timeout_player.py @@ -0,0 +1,70 @@ +import multiprocessing + +# Hacky alternative to using select for timing out players + +# WARNING: Do not wrap around HumanPlayer or things breakify + +class Sleeper(multiprocessing.Process): + def __init__(self, timeout): + multiprocessing.Process.__init__(self) + self.timeout = timeout + + def run(self): + time.sleep(self.timeout) + + +class Worker(multiprocessing.Process): + def __init__(self, function, args, q): + multiprocessing.Process.__init__(self) + self.function = function + self.args = args + self.q = q + + def run(self): + #print str(self) + " runs " + str(self.function) + " with args " + str(self.args) + self.q.put(self.function(*self.args)) + + + +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("UNRESPONSIVE") + + + + +# 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) diff --git a/qchess/tools/image_builder.py b/qchess/tools/image_builder.py new file mode 100644 index 0000000000000000000000000000000000000000..c9760eff621db87432ccd2063bc4ed577fe0d1b3 --- /dev/null +++ b/qchess/tools/image_builder.py @@ -0,0 +1,33 @@ +#!/usr/bin/python + +import sys +from images import * + +def main(argv): + pygame.init() + try: + target = str(argv[1]) + except: + target = os.path.join(os.path.curdir, "..","data","images") + + try: + grid_size = int(argv[2]) + except: + grid_size = 64 + + + if not os.path.exists(target): + os.mkdir(target) + + create_images([grid_size, grid_size], font_name=os.path.join(os.path.curdir, "..", "data", "DejaVuSans.ttf")) + + for colour in piece_char.keys(): + for piece in piece_char[colour].keys(): + pygame.image.save(images[colour][piece], os.path.join(target,str(colour)+"_"+str(piece)+".png")) + pygame.image.save(small_images[colour][piece], os.path.join(target,str(colour)+"_"+str(piece)+"_small.png")) + + pygame.quit() + return 0 + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/qchess/tools/images.py b/qchess/tools/images.py new file mode 120000 index 0000000000000000000000000000000000000000..a1ec45aa605d80fd643857c0e37ae8ff18a8013b --- /dev/null +++ b/qchess/tools/images.py @@ -0,0 +1 @@ +../src/images.py \ No newline at end of file diff --git a/qchess/tools/images.pyc b/qchess/tools/images.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0a48eb3c42ed7b00e35fe3a13bf90f07bd66a2be Binary files /dev/null and b/qchess/tools/images.pyc differ diff --git a/qchess/update.sh b/qchess/update.sh deleted file mode 100644 index bec40eacca5f653b0ac81b954050cb62ecffe0a5..0000000000000000000000000000000000000000 --- a/qchess/update.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/bash - -# I still can't believe I am doing this - -# This updates the component files from the target, as well as the target from the components -# You can't do that with gnu make, because of the circular dependency -# But this should probably not be used by any sane person - -exit 1 - -target=qchess.py -components="piece.py board.py player.py network.py thread_util.py game.py graphics.py main.py" -# The below seems nicer, but doesn't work because things need to be imported in the right order :( -#components=$(ls *.py | tr '\t' '\n' | grep -v $target) - -header="#!/usr/bin/python -u" -footer="# EOF - created from update.sh on $(date)" - - - -# If the target was modified more recently than any of the components, update the component file -target_mod=$(stat -c %Y $target 2>/dev/null) - -if [ $? -ne 0 ]; then - merge_required=true -else - merge_required=true - - - for f in $components; do - - component_mod=$(stat -c %Y $f 2>/dev/null) - if [ $? -ne 0 ]; then - update_required=true - elif [ $component_mod -lt $target_mod ]; then - update_required=true - else - update_required=false - fi - - if $update_required; then - echo "$0 - update $f from $target" - sanity=$(egrep "(+++ $f +++)|(--- $f ---)" $target | wc -l) - if [ $sanity -ne 2 ]; then - $(echo "$0 - $target does not have markers for $f in it!") 1>&2 - exit 1 - fi - cp $f $f~ - new_contents=$(nawk "/+++ $f +++/, /--- $f ---/" $target | grep -v "+++ $f +++" | grep -v "\--- $f ---") - - echo "$new_contents" > $f - else - echo "$0 - $f is newer than $target" - merge_required=true - fi - done - - -fi - -# If any components were modified more recently than the target, merge the components into the target -if $merge_required; then - echo $header > $target - for f in $components; do - echo "$0 - merge $f into $target" - echo "# +++ $f +++ #" >> $target - cat $f >> $target - echo "# --- $f --- #" >> $target - done - - echo $footer >> $target - chmod u+x $target -fi diff --git a/qchess/win32_dll/SDL.dll b/qchess/win32_dll/SDL.dll new file mode 100644 index 0000000000000000000000000000000000000000..69fd61ecb81cf1423e7f387f87682540cabaff4a Binary files /dev/null and b/qchess/win32_dll/SDL.dll differ diff --git a/qchess/win32_dll/SDL_image.dll b/qchess/win32_dll/SDL_image.dll new file mode 100644 index 0000000000000000000000000000000000000000..e891b16748723cda486e046a694660edebcec978 Binary files /dev/null and b/qchess/win32_dll/SDL_image.dll differ diff --git a/qchess/win32_dll/SDL_mixer.dll b/qchess/win32_dll/SDL_mixer.dll new file mode 100644 index 0000000000000000000000000000000000000000..89def7635876d1b176aa86a2c52075e0d69b39ee Binary files /dev/null and b/qchess/win32_dll/SDL_mixer.dll differ diff --git a/qchess/win32_dll/SDL_ttf.dll b/qchess/win32_dll/SDL_ttf.dll new file mode 100644 index 0000000000000000000000000000000000000000..a8f1bcc1b08316b5a8866726a958a8a370bb9d4f Binary files /dev/null and b/qchess/win32_dll/SDL_ttf.dll differ diff --git a/qchess/win32_dll/jpeg.dll b/qchess/win32_dll/jpeg.dll new file mode 100644 index 0000000000000000000000000000000000000000..72c33d246917d0a79a0121b4138f5c2f7ee0682e Binary files /dev/null and b/qchess/win32_dll/jpeg.dll differ diff --git a/qchess/win32_dll/libfreetype-6.dll b/qchess/win32_dll/libfreetype-6.dll new file mode 100644 index 0000000000000000000000000000000000000000..3b2e69dfa9a7e55c7c27b7feb64a4c71f345aca1 Binary files /dev/null and b/qchess/win32_dll/libfreetype-6.dll differ diff --git a/qchess/win32_dll/libjpeg-8.dll b/qchess/win32_dll/libjpeg-8.dll new file mode 100644 index 0000000000000000000000000000000000000000..74285d2518b7257283dee41a6f512d5b0418623c Binary files /dev/null and b/qchess/win32_dll/libjpeg-8.dll differ diff --git a/qchess/win32_dll/libpng12-0.dll b/qchess/win32_dll/libpng12-0.dll new file mode 100644 index 0000000000000000000000000000000000000000..f9709be371ee219560b8f14837e78f652754ac49 Binary files /dev/null and b/qchess/win32_dll/libpng12-0.dll differ diff --git a/qchess/win32_dll/libpng15-15.dll b/qchess/win32_dll/libpng15-15.dll new file mode 100644 index 0000000000000000000000000000000000000000..a5854d5db78e4099facf505e4621c957cf1e8b47 Binary files /dev/null and b/qchess/win32_dll/libpng15-15.dll differ diff --git a/qchess/win32_dll/libtiff-5.dll b/qchess/win32_dll/libtiff-5.dll new file mode 100644 index 0000000000000000000000000000000000000000..f449d75060835ef05b392aba8598ff671afc2661 Binary files /dev/null and b/qchess/win32_dll/libtiff-5.dll differ diff --git a/qchess/win32_dll/libwebp-2.dll b/qchess/win32_dll/libwebp-2.dll new file mode 100644 index 0000000000000000000000000000000000000000..e89ac8bfa4361bc674d7fdae80ac396fa69d0ba7 Binary files /dev/null and b/qchess/win32_dll/libwebp-2.dll differ diff --git a/qchess/win32_dll/mfc42.dll b/qchess/win32_dll/mfc42.dll new file mode 100644 index 0000000000000000000000000000000000000000..27692e53318b1c8c0723a91a094940600eac695f Binary files /dev/null and b/qchess/win32_dll/mfc42.dll differ diff --git a/qchess/win32_dll/smpeg.dll b/qchess/win32_dll/smpeg.dll new file mode 100644 index 0000000000000000000000000000000000000000..3742ad21ed0048328fc7e13288e7c9dc47df3e69 Binary files /dev/null and b/qchess/win32_dll/smpeg.dll differ diff --git a/qchess/win32_dll/tcl85.dll b/qchess/win32_dll/tcl85.dll new file mode 100644 index 0000000000000000000000000000000000000000..a933e79f78e7d8f16eff3ccde771fad615803f98 Binary files /dev/null and b/qchess/win32_dll/tcl85.dll differ diff --git a/qchess/win32_dll/tk85.dll b/qchess/win32_dll/tk85.dll new file mode 100644 index 0000000000000000000000000000000000000000..163c2a36dcde77f01525f22caef6d9168d6021c3 Binary files /dev/null and b/qchess/win32_dll/tk85.dll differ diff --git a/qchess/win32_dll/zlib1.dll b/qchess/win32_dll/zlib1.dll new file mode 100644 index 0000000000000000000000000000000000000000..53f3376a6635b0f6bc4227181fd46087f1d0324a Binary files /dev/null and b/qchess/win32_dll/zlib1.dll differ