From: Sam Moore Date: Tue, 29 Jan 2013 16:47:24 +0000 (+0800) Subject: Added sample agent + log file writing/parsing X-Git-Url: https://git.ucc.asn.au/?p=progcomp2013.git;a=commitdiff_plain;h=a35e4dc5f4fb6325785b6f8b123266976107b748 Added sample agent + log file writing/parsing I figured no one wants to sift through the qchess internal agent stuff, So I wrote a python external agent agents/sample.py I ended up implementing the cool idea about log files. You can replay a log file up to an arbitrary point, and then continue play. I also fixed a bug with external agents calling run_agent. Removed the reading of the colour line from there, since the external agent should do it. Added reading of the colour line to the ExternalWrapper class, to avoid breaking that. Added --reveal so that all states can be seen in the GUI (but they are not communicated to Agents). Added handling of SIGINT to main.py Probably did something else I forgot. qchess will probably be ready soon (famous last words), I should start on a scoring/entry system... And a webpage. --- diff --git a/agents/sample.py b/agents/sample.py new file mode 100755 index 0000000..34ce858 --- /dev/null +++ b/agents/sample.py @@ -0,0 +1,111 @@ +#!/usr/bin/python -u + +# Sample agent +# Copy this file, change the agent as needed + +from qchess import * # This is normally considered bad practice in python, but good practice in UCC::Progcomp +import random # For the example which makes random moves + +# The first thing to do is pick a cool name... +class AgentSample(InternalAgent): + def __init__(self, name, colour): + InternalAgent.__init__(self, name, colour) # The InternalAgent class gives you some useful stuff + + # You can access self.board to get a qchess.Board that stores the state as recorded by the agent + # This board is automatically updated by the InternalAgent base class + # As well as a grid of pieces, qchess.Board gives you lists of pieces and other useful functions; see qchess/src/board.py + + + #TODO: Any extra initialisation + + # You should print debug messages like this: + sys.stderr.write(sys.argv[0] + " : " + str(self) + " : Initialised agent\n") + # I would appreciate it if you removed them before submitting an entry though. + + # Must return [x,y] of selected piece + # Your agent will call select(), followed by get_move() and so on + # TODO: Implement + def select(self): + # debug message + sys.stderr.write(sys.argv[0] + " : " + str(self) + " : Selecting piece...\n") + + + # Here is a random choice algorithm to help you start + # It is a slight improvement on purely random; it will pick a piece that has at least one known possible move + # BUT it has a possibility to loop infinitely! You should fix that. + + while True: + # Randomly pick a piece + # Use self.board.pieces[self.colour] to get a list of your pieces + # Use self.board.pieces[opponent(self.colour)] to get opponent pieces + # Use self.board.king[self.colour], vice versa, to get the king + + choices = self.board.pieces[self.colour] # All the agent's pieces + choice_index = random.randint(0, len(choices)-1) # Get the index in the list of the chosen piece + self.choice = choices[choice_index] # Choose the piece, and remember it + + # Find all known possible moves for the piece + # Use self.board.possible_moves(piece) to get a list of possible moves for a piece + # *BUT* Make sure the type of the piece is known (you can temporarily set it) first! + # Use Piece.current_type to get/set the current type of a piece + + all_moves = [] # Will store all possible moves for the piece + tmp = self.choice.current_type # Remember the chosen piece's current type + + if tmp == "unknown": # For pieces that are in a supperposition, try both types + for t in self.choice.types: + if t == "unknown": + continue # Ignore unknown types + self.choice.current_type = t # Temporarily overwrite the piece's type + all_moves += self.board.possible_moves(self.choice) # Add the possible moves for that type + else: + all_moves = self.board.possible_moves(self.choice) # The piece is in a classical state; add possible moves + self.choice.current_type = tmp # Reset the piece's current type + if len(all_moves) > 0: + break # If the piece had *any* possible moves, it is a good choice; leave the loop + # Otherwise the loop will try again + # End while loop + + return [self.choice.x, self.choice.y] # Return the position of the selected piece + + # Must return [x,y] of square to move the piece previously selected into + # Your agent will call select(), followed by get_move() and so on + # TODO: Implement this + def get_move(self): + # debug message + sys.stderr.write(sys.argv[0] + " : " + str(self) + " : Moving piece ("+str(self.choice)+")\n") + # As an example we will just pick a random move for the piece previously chosen in select() + + # Note that whichever piece was previously selected will have collapsed into a classical state + + # self.board.possible_moves(piece) will return a list of [x,y] pairs for valid moves + + moves = self.board.possible_moves(self.choice) # Get all moves for the selected piece + move_index = random.randint(0, len(moves)-1) # Get the index in the list of the chosen move + return moves[move_index] # This is a randomly chosen [x,y] pair for a valid move of the piece + + +# Hints: +# select will probably have to be more complicated than get_move, because by the time get_move is called, the piece's state is known +# If you want to see if a square is threatened/defended, you can call self.board.coverage([x,y]); see qchess/src/board.py +# A good approach is min/max. For each move, associate a score. Then subtract the scores for moves that the opponent could make. Then pick the move with the highest score. +# Look at qchess/src/agent_bishop.py for a more effective (but less explained) agent + +if __name__ == "__main__": + colour = sys.stdin.readline().strip("\r\n") + agent = AgentSample(sys.argv[0], colour) # Change the class name here + run_agent(agent) # This is provided by qchess. It calls the functions of your agent as required during the game. + +# You can run this as an external agent with the qchess program +# Just run ./qchess.py and apply common sense (or read the help file) + +# If you are feeling adventurous you can add it to the qchess program as an internal agent +# This might give better performance... unless you use the --timeout switch, in which case there is absolutely no point +# 1. Delete the lines that run the agent (the block that starts with if __name__ == "__main__") +# 2. Copy the file to qchess/src/agent_sample.py (or whatever you want to call it) +# 3. Edit qchess/src/Makefile so that agent_sample.py appears as one of the files in COMPONENTS +# 4. Rebuild by running make in qchess +# Again, run ./qchess.py and apply common sense + + + diff --git a/qchess/Makefile b/qchess/Makefile index c6a7483..6a6e81c 100644 --- a/qchess/Makefile +++ b/qchess/Makefile @@ -3,14 +3,16 @@ SCRIPT=qchess.py DLL_PATH=win32_dll +python_native : + make -C src + mv src/$(SCRIPT) ./ + + all : python_native frozen frozen : win32_frozen linux64_frozen cd build; for d in $$(ls); do if [ -d $$d ]; then zip -r $$d.zip $$d; rm -r $$d; fi; done -python_native : - make -C src - mv src/$(SCRIPT) ./ images : cd tools; python image_builder.py diff --git a/qchess/build/exe.linux-x86_64-2.7.zip b/qchess/build/exe.linux-x86_64-2.7.zip index 80e1819..5b52214 100644 Binary files a/qchess/build/exe.linux-x86_64-2.7.zip 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 index 402948b..9d1041b 100644 Binary files a/qchess/build/exe.win32-2.7.zip and b/qchess/build/exe.win32-2.7.zip differ diff --git a/qchess/data/help.txt b/qchess/data/help.txt index c0a8b6f..892a7ec 100644 --- a/qchess/data/help.txt +++ b/qchess/data/help.txt @@ -64,9 +64,23 @@ OPTIONS If graphics are enabled (default), then the user will be prompted to choose any of the two players not supplied as arguments. + --reveal + If graphics are enabled, the two states for pieces will always be shown, regardless of whether both states have been revealed. + Note that this switch only affects the GUI and does not provide any information to agent players. - --file[=filename] + If graphics are disabled, has no effect. + + --file[=filename][:events] Replay a game saved in file, or read from stdin if no filename given + If a number of events is supplied, the game will advance that many events before stopping. + + If no players are given, the GUI will NOT ask for player selections. + The game will exit after the replay finishes. Events in the replay will be subject to the normal delay (see --delay). + + If black and white players are supplied, the game will continue using those players. + In this case, there will be no delays between events in the replay (the game starts at the end of the replay) + + (We hope that) this feature will be useful for comparing how different versions of an agent respond to the same situation. --log[=filename] Log moves to a file or stdout if no filename given diff --git a/qchess/qchess.py b/qchess/qchess.py index 219b34f..02a724a 100755 --- a/qchess/qchess.py +++ b/qchess/qchess.py @@ -39,7 +39,7 @@ class Piece(): # Make a string for the piece (used for debug) def __str__(self): - return str(self.current_type) + " " + str(self.types) + " at " + str(self.x) + ","+str(self.y) + return str(self.colour) + " " + str(self.current_type) + " " + str(self.types) + " at " + str(self.x) + ","+str(self.y) # Draw the piece in a pygame surface def draw(self, window, grid_sz = [80,80], style="quantum"): @@ -59,7 +59,7 @@ class Piece(): # Draw the two possible types underneath the current_type image for i in range(len(self.types)): - if self.types_revealed[i] == True: + if always_reveal_states == True or self.types_revealed[i] == True: img = small_images[self.colour][self.types[i]] else: img = small_images[self.colour]["unknown"] # If the type hasn't been revealed, show a placeholder @@ -97,6 +97,8 @@ class Piece(): # --- piece.py --- # [w,h] = [8,8] # Width and height of board(s) +always_reveal_states = False + # Class to represent a quantum chess board class Board(): # Initialise; if master=True then the secondary piece types are assigned @@ -111,6 +113,9 @@ class Board(): for c in ["black", "white"]: del self.unrevealed_types[c]["unknown"] + if style == "empty": + return + # Add all the pieces with known primary types for i in range(0, 2): @@ -704,8 +709,6 @@ class AgentRandom(InternalAgent): def run_agent(agent): #sys.stderr.write(sys.argv[0] + " : Running agent " + str(agent) + "\n") - colour = sys.stdin.readline().strip(" \r\n") - agent.colour = colour while True: line = sys.stdin.readline().strip(" \r\n") if line == "SELECTION?": @@ -730,7 +733,7 @@ def run_agent(agent): class ExternalWrapper(ExternalAgent): def __init__(self, agent): - run = "python -u -c \"import sys;import os;from qchess import *;agent = " + agent.__class__.__name__ + "('" + agent.name + "','"+agent.colour+"');sys.exit(run_agent(agent))\"" + 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))\"" # str(run) ExternalAgent.__init__(self, run, agent.colour) @@ -1205,7 +1208,20 @@ def log(s): import datetime log_file.write(str(datetime.datetime.now()) + " : " + s + "\n") +def log_init(board, players): + if log_file != None: + import datetime + log_file.write("# Log starts " + str(datetime.datetime.now()) + "\n") + for p in players: + log_file.write("# " + p.colour + " : " + p.name + "\n") + + log_file.write("# Initial board\n") + for x in range(0, w): + for y in range(0, h): + if board.grid[x][y] != None: + log_file.write(str(board.grid[x][y]) + "\n") + log_file.write("# Start game\n") # A thread that runs the game @@ -1219,6 +1235,8 @@ class GameThread(StoppableThread): 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): @@ -1330,20 +1348,53 @@ class GameThread(StoppableThread): # A thread that replays a log file class ReplayThread(GameThread): - def __init__(self, players, src): - self.board = Board(style="agent") + def __init__(self, players, src, end=False,max_lines=None): + self.board = Board(style="empty") GameThread.__init__(self, self.board, players) self.src = src + self.max_lines = max_lines + self.line_number = 0 + self.end = end - self.ended = False + try: + while self.src.readline().strip(" \r\n") != "# Initial board": + self.line_number += 1 + + line = self.src.readline().strip(" \r\n") + + while line != "# Start game": + #print "Reading line " + str(line) + self.line_number += 1 + [x,y] = map(int, line.split("at")[1].strip(" \r\n").split(",")) + colour = line.split(" ")[0] + current_type = line.split(" ")[1] + types = map(lambda e : e.strip(" [],'"), line.split(" ")[2:4]) + p = Piece(colour, x, y, types) + if current_type != "unknown": + p.current_type = current_type + p.choice = types.index(current_type) + + self.board.pieces[colour].append(p) + self.board.grid[x][y] = p + if current_type == "king": + self.board.king[colour] = p + + line = self.src.readline().strip(" \r\n") + + except Exception, e: + raise Exception("FILE line: " + str(self.line_number) + " \""+str(line)+"\"") #\n" + e.message) def run(self): i = 0 phase = 0 - for line in self.src: + count = 0 + line = self.src.readline().strip(" \r\n") + while line != "# EOF": + count += 1 + if self.max_lines != None and count > self.max_lines: + self.stop() if self.stopped(): - self.ended = True break with self.lock: @@ -1356,10 +1407,8 @@ class ReplayThread(GameThread): try: self.board.update(result) except: - self.ended = True self.final_result = result - if isinstance(graphics, GraphicsThread): - graphics.stop() + self.stop() break [x,y] = map(int, result.split(" ")[0:2]) @@ -1371,14 +1420,16 @@ class ReplayThread(GameThread): graphics.state["moves"] = self.board.possible_moves(target) graphics.state["select"] = target - time.sleep(turn_delay) + if self.end: + time.sleep(turn_delay) elif phase == 1: [x2,y2] = map(int, result.split(" ")[3:5]) with graphics.lock: graphics.state["moves"] = [[x2,y2]] - time.sleep(turn_delay) + if self.end: + time.sleep(turn_delay) with graphics.lock: graphics.state["select"] = None @@ -1396,6 +1447,21 @@ class ReplayThread(GameThread): if phase == 0: i = (i + 1) % 2 + line = self.src.readline().strip(" \r\n") + + if self.max_lines != None and self.max_lines > count: + sys.stderr.write(sys.argv[0] + " : Replaying from file; stopping at last line (" + str(count) + ")\n") + sys.stderr.write(sys.argv[0] + " : (You requested line " + str(self.max_lines) + ")\n") + + if self.end and isinstance(graphics, GraphicsThread): + #graphics.stop() + pass # Let the user stop the display + elif not self.end: + global game + game = GameThread(self.board, self.players) + game.run() + + def opponent(colour): @@ -1965,8 +2031,9 @@ def main(argv): global log_file global src_file global graphics_enabled + global always_reveal_states - + max_lines = None src_file = None style = "quantum" @@ -2003,6 +2070,8 @@ def main(argv): style = "classical" elif arg[1] == '-' and arg[2:] == "quantum": style = "quantum" + elif arg[1] == '-' and arg[2:] == "reveal": + always_reveal_states = True elif (arg[1] == '-' and arg[2:] == "graphics"): graphics_enabled = not graphics_enabled elif (arg[1] == '-' and arg[2:].split("=")[0] == "file"): @@ -2010,7 +2079,11 @@ def main(argv): if len(arg[2:].split("=")) == 1: src_file = sys.stdin else: - src_file = open(arg[2:].split("=")[1]) + src_file = open(arg[2:].split("=")[1].split(":")[0]) + + if len(arg[2:].split(":")) == 2: + max_lines = int(arg[2:].split(":")[1]) + elif (arg[1] == '-' and arg[2:].split("=")[0] == "log"): # Log file if len(arg[2:].split("=")) == 1: @@ -2042,9 +2115,21 @@ def main(argv): # Construct a GameThread! Make it global! Damn the consequences! if src_file != None: - if len(players) == 0: + # Hack to stop ReplayThread from exiting + #if len(players) == 0: + # players = [HumanPlayer("dummy", "white"), HumanPlayer("dummy", "black")] + + # Normally the ReplayThread exits if there are no players + # TODO: Decide which behaviour to use, and fix it + end = (len(players) == 0) + if end: players = [Player("dummy", "white"), Player("dummy", "black")] - game = ReplayThread(players, src_file) + elif len(players) != 2: + sys.stderr.write(sys.argv[0] + " : Usage " + sys.argv[0] + " white black\n") + if graphics_enabled: + sys.stderr.write(sys.argv[0] + " : (You won't get a GUI, because --file was used, and the author is lazy)\n") + return 44 + game = ReplayThread(players, src_file, end=end, max_lines=max_lines) else: board = Board(style) game = GameThread(board, players) @@ -2117,17 +2202,24 @@ def main(argv): + log_init(game.board, players) + if graphics != None: game.start() # This runs in a new thread graphics.run() - game.join() + if game.is_alive(): + game.join() + + error = game.error + graphics.error else: game.run() error = game.error + if log_file != None and log_file != sys.stdout: + log_file.write("# EOF\n") log_file.close() if src_file != None and src_file != sys.stdin: @@ -2137,6 +2229,20 @@ def main(argv): # This is how python does a main() function... if __name__ == "__main__": - sys.exit(main(sys.argv)) + try: + sys.exit(main(sys.argv)) + except KeyboardInterrupt: + sys.stderr.write(sys.argv[0] + " : Got KeyboardInterrupt. Stopping everything\n") + if isinstance(graphics, StoppableThread): + graphics.stop() + graphics.run() # Will clean up graphics because it is stopped, not run it (a bit dodgy) + + if isinstance(game, StoppableThread): + game.stop() + if game.is_alive(): + game.join() + + sys.exit(102) + # --- main.py --- # -# EOF - created from make on Tue Jan 29 18:10:18 WST 2013 +# EOF - created from make on Wed Jan 30 00:45:46 WST 2013 diff --git a/qchess/src/board.py b/qchess/src/board.py index 0d96d1a..5dbf1dd 100644 --- a/qchess/src/board.py +++ b/qchess/src/board.py @@ -1,5 +1,7 @@ [w,h] = [8,8] # Width and height of board(s) +always_reveal_states = False + # Class to represent a quantum chess board class Board(): # Initialise; if master=True then the secondary piece types are assigned @@ -14,6 +16,9 @@ class Board(): for c in ["black", "white"]: del self.unrevealed_types[c]["unknown"] + if style == "empty": + return + # Add all the pieces with known primary types for i in range(0, 2): diff --git a/qchess/src/game.py b/qchess/src/game.py index e3f1c7c..07a1325 100644 --- a/qchess/src/game.py +++ b/qchess/src/game.py @@ -7,7 +7,20 @@ def log(s): import datetime log_file.write(str(datetime.datetime.now()) + " : " + s + "\n") +def log_init(board, players): + if log_file != None: + import datetime + log_file.write("# Log starts " + str(datetime.datetime.now()) + "\n") + for p in players: + log_file.write("# " + p.colour + " : " + p.name + "\n") + + log_file.write("# Initial board\n") + for x in range(0, w): + for y in range(0, h): + if board.grid[x][y] != None: + log_file.write(str(board.grid[x][y]) + "\n") + log_file.write("# Start game\n") # A thread that runs the game @@ -21,6 +34,8 @@ class GameThread(StoppableThread): 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): @@ -132,20 +147,53 @@ class GameThread(StoppableThread): # A thread that replays a log file class ReplayThread(GameThread): - def __init__(self, players, src): - self.board = Board(style="agent") + def __init__(self, players, src, end=False,max_lines=None): + self.board = Board(style="empty") GameThread.__init__(self, self.board, players) self.src = src + self.max_lines = max_lines + self.line_number = 0 + self.end = end - self.ended = False + try: + while self.src.readline().strip(" \r\n") != "# Initial board": + self.line_number += 1 + + line = self.src.readline().strip(" \r\n") + + while line != "# Start game": + #print "Reading line " + str(line) + self.line_number += 1 + [x,y] = map(int, line.split("at")[1].strip(" \r\n").split(",")) + colour = line.split(" ")[0] + current_type = line.split(" ")[1] + types = map(lambda e : e.strip(" [],'"), line.split(" ")[2:4]) + p = Piece(colour, x, y, types) + if current_type != "unknown": + p.current_type = current_type + p.choice = types.index(current_type) + + self.board.pieces[colour].append(p) + self.board.grid[x][y] = p + if current_type == "king": + self.board.king[colour] = p + + line = self.src.readline().strip(" \r\n") + + except Exception, e: + raise Exception("FILE line: " + str(self.line_number) + " \""+str(line)+"\"") #\n" + e.message) def run(self): i = 0 phase = 0 - for line in self.src: + count = 0 + line = self.src.readline().strip(" \r\n") + while line != "# EOF": + count += 1 + if self.max_lines != None and count > self.max_lines: + self.stop() if self.stopped(): - self.ended = True break with self.lock: @@ -158,10 +206,8 @@ class ReplayThread(GameThread): try: self.board.update(result) except: - self.ended = True self.final_result = result - if isinstance(graphics, GraphicsThread): - graphics.stop() + self.stop() break [x,y] = map(int, result.split(" ")[0:2]) @@ -173,14 +219,16 @@ class ReplayThread(GameThread): graphics.state["moves"] = self.board.possible_moves(target) graphics.state["select"] = target - time.sleep(turn_delay) + if self.end: + time.sleep(turn_delay) elif phase == 1: [x2,y2] = map(int, result.split(" ")[3:5]) with graphics.lock: graphics.state["moves"] = [[x2,y2]] - time.sleep(turn_delay) + if self.end: + time.sleep(turn_delay) with graphics.lock: graphics.state["select"] = None @@ -198,6 +246,21 @@ class ReplayThread(GameThread): if phase == 0: i = (i + 1) % 2 + line = self.src.readline().strip(" \r\n") + + if self.max_lines != None and self.max_lines > count: + sys.stderr.write(sys.argv[0] + " : Replaying from file; stopping at last line (" + str(count) + ")\n") + sys.stderr.write(sys.argv[0] + " : (You requested line " + str(self.max_lines) + ")\n") + + if self.end and isinstance(graphics, GraphicsThread): + #graphics.stop() + pass # Let the user stop the display + elif not self.end: + global game + game = GameThread(self.board, self.players) + game.run() + + def opponent(colour): diff --git a/qchess/src/main.py b/qchess/src/main.py index 0adecb0..137d1cb 100644 --- a/qchess/src/main.py +++ b/qchess/src/main.py @@ -67,8 +67,9 @@ def main(argv): global log_file global src_file global graphics_enabled + global always_reveal_states - + max_lines = None src_file = None style = "quantum" @@ -105,6 +106,8 @@ def main(argv): style = "classical" elif arg[1] == '-' and arg[2:] == "quantum": style = "quantum" + elif arg[1] == '-' and arg[2:] == "reveal": + always_reveal_states = True elif (arg[1] == '-' and arg[2:] == "graphics"): graphics_enabled = not graphics_enabled elif (arg[1] == '-' and arg[2:].split("=")[0] == "file"): @@ -112,7 +115,11 @@ def main(argv): if len(arg[2:].split("=")) == 1: src_file = sys.stdin else: - src_file = open(arg[2:].split("=")[1]) + src_file = open(arg[2:].split("=")[1].split(":")[0]) + + if len(arg[2:].split(":")) == 2: + max_lines = int(arg[2:].split(":")[1]) + elif (arg[1] == '-' and arg[2:].split("=")[0] == "log"): # Log file if len(arg[2:].split("=")) == 1: @@ -144,9 +151,21 @@ def main(argv): # Construct a GameThread! Make it global! Damn the consequences! if src_file != None: - if len(players) == 0: + # Hack to stop ReplayThread from exiting + #if len(players) == 0: + # players = [HumanPlayer("dummy", "white"), HumanPlayer("dummy", "black")] + + # Normally the ReplayThread exits if there are no players + # TODO: Decide which behaviour to use, and fix it + end = (len(players) == 0) + if end: players = [Player("dummy", "white"), Player("dummy", "black")] - game = ReplayThread(players, src_file) + elif len(players) != 2: + sys.stderr.write(sys.argv[0] + " : Usage " + sys.argv[0] + " white black\n") + if graphics_enabled: + sys.stderr.write(sys.argv[0] + " : (You won't get a GUI, because --file was used, and the author is lazy)\n") + return 44 + game = ReplayThread(players, src_file, end=end, max_lines=max_lines) else: board = Board(style) game = GameThread(board, players) @@ -219,17 +238,24 @@ def main(argv): + log_init(game.board, players) + if graphics != None: game.start() # This runs in a new thread graphics.run() - game.join() + if game.is_alive(): + game.join() + + error = game.error + graphics.error else: game.run() error = game.error + if log_file != None and log_file != sys.stdout: + log_file.write("# EOF\n") log_file.close() if src_file != None and src_file != sys.stdin: @@ -239,4 +265,18 @@ def main(argv): # This is how python does a main() function... if __name__ == "__main__": - sys.exit(main(sys.argv)) + try: + sys.exit(main(sys.argv)) + except KeyboardInterrupt: + sys.stderr.write(sys.argv[0] + " : Got KeyboardInterrupt. Stopping everything\n") + if isinstance(graphics, StoppableThread): + graphics.stop() + graphics.run() # Will clean up graphics because it is stopped, not run it (a bit dodgy) + + if isinstance(game, StoppableThread): + game.stop() + if game.is_alive(): + game.join() + + sys.exit(102) + diff --git a/qchess/src/piece.py b/qchess/src/piece.py index d9acef2..400a79a 100644 --- a/qchess/src/piece.py +++ b/qchess/src/piece.py @@ -38,7 +38,7 @@ class Piece(): # Make a string for the piece (used for debug) def __str__(self): - return str(self.current_type) + " " + str(self.types) + " at " + str(self.x) + ","+str(self.y) + return str(self.colour) + " " + str(self.current_type) + " " + str(self.types) + " at " + str(self.x) + ","+str(self.y) # Draw the piece in a pygame surface def draw(self, window, grid_sz = [80,80], style="quantum"): @@ -58,7 +58,7 @@ class Piece(): # Draw the two possible types underneath the current_type image for i in range(len(self.types)): - if self.types_revealed[i] == True: + if always_reveal_states == True or self.types_revealed[i] == True: img = small_images[self.colour][self.types[i]] else: img = small_images[self.colour]["unknown"] # If the type hasn't been revealed, show a placeholder diff --git a/qchess/src/player.py b/qchess/src/player.py index b3f0eb7..0a4ddd6 100644 --- a/qchess/src/player.py +++ b/qchess/src/player.py @@ -203,8 +203,6 @@ class AgentRandom(InternalAgent): def run_agent(agent): #sys.stderr.write(sys.argv[0] + " : Running agent " + str(agent) + "\n") - colour = sys.stdin.readline().strip(" \r\n") - agent.colour = colour while True: line = sys.stdin.readline().strip(" \r\n") if line == "SELECTION?": @@ -229,7 +227,7 @@ def run_agent(agent): class ExternalWrapper(ExternalAgent): def __init__(self, agent): - run = "python -u -c \"import sys;import os;from qchess import *;agent = " + agent.__class__.__name__ + "('" + agent.name + "','"+agent.colour+"');sys.exit(run_agent(agent))\"" + 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))\"" # str(run) ExternalAgent.__init__(self, run, agent.colour)