Commit ef3a2d18 authored by judge's avatar judge

Tried to fix borked permissions

parent 52068b63
File mode changed from 100755 to 100644
\ No newline at end of file
NAME - Play quantum chess
SYNOPSIS [OPTIONS] [white] [black]
An implementation of Quantum Chess as originally described and implemented here:
Reimplemented for UCC::Progcomp 2013
- This version does not implement castling or en passen rules.
- If a piece currently in a pawn state moves into the opposing back row, that state always becomes a queen.
- (The other state of the piece is unaffected).
If no arguments are given, a window should appear asking you to pick each player.
Then the game will commence using default values.
white, black
Each of the two players in order. They need not be provided if graphics is enabled (default).
Any arguments that do not begin with a hyphen (-) are treated as the player arguments in the order they appear.
Player arguments that begin with '@' are treated as special players:
A human player; if graphics are enabled, this players turns are made through the GUI
A player over a network connection.
For example, if [email protected] wants to play [email protected]:
[email protected]:~$ ./ @network @human
[email protected]:~$ ./ @human @network:host1
IMPORTANT: Only ONE of the games should give the other's address.
An internal agent player
These agents run within the qchess program (unless there is a timeout setting... never mind).
Choices are:
AgentRandom - Makes random moves only
AgentBishop - Uses probability estimates and a min/max recursive (depth is only one) algorithm
- Will usually take a long time to run
Print this page
Enable the GUI
If graphics are enabled (default), then the user will be prompted to choose any of the two players not supplied as arguments.
Disable the GUI
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.
If graphics are disabled, has no effect.
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 moves to a file or stdout if no filename given
The game pauses between moves so that it can be followed by a human observer.
This option can be used to change the delay. If no time is given, the delay is disabled.
If graphics are enabled (default), the delay is 0.5s by default.
If graphics are disabled, there is no delay unless this option is used.
Set the maximum time in seconds to wait before declaring an AI program unresponsive.
If no time is given, the timeout is disabled.
By default the timeout is disabled.
Setting a blackout time will cause the display to become black if the mouse is not moved and no keys or buttons are pressed.
If no time is given, the blackout time is disabled.
By default the blackout is disabled.
This switch was introduced for entirely obscure purposes.
If this option is used, the game will treat pieces "classically", ie: as in standard chess.
Note that the game does not enforce rules related to check and checkmate.
The game uses the quantum chess representation of pieces (default).
Written for the UCC Programming Competition 2013 by Sam Moore.
UCC::Progcomp home page:
Report bugs to [email protected]
Join IRC channel #progcomp on irc://
Copyright 2013 The University Computer Club, Inc.
Contact [email protected]
\ No newline at end of file
#!/usr/bin/python -u
import random
# I know using non-abreviated strings is inefficient, but this is python, who cares?
# Oh, yeah, this stores the number of pieces of each type in a normal chess game
piece_types = {"pawn" : 8, "bishop" : 2, "knight" : 2, "rook" : 2, "queen" : 1, "king" : 1, "unknown" : 0}
# Class to represent a quantum chess piece
class Piece():
def __init__(self, colour, x, y, types):
self.colour = colour # Colour (string) either "white" or "black"
self.x = x # x coordinate (0 - 8), none of this fancy 'a', 'b' shit here
self.y = y # y coordinate (0 - 8)
self.types = types # List of possible types the piece can be (should just be two)
self.current_type = "unknown" # Current type
self.choice = -1 # Index of the current type in self.types (-1 = unknown type)
self.last_state = None
self.move_pattern = None
self.coverage = None
self.possible_moves = {}
def init_from_copy(self, c):
self.colour = c.colour
self.x = c.x
self.y = c.y
self.types = c.types[:]
self.current_type = c.current_type
self.choice = c.choice
self.last_state = None
self.move_pattern = None
# Make a string for the piece (used for debug)
def __str__(self):
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"):
# First draw the image corresponding to self.current_type
img = images[self.colour][self.current_type]
rect = img.get_rect()
if style == "classical":
offset = [-rect.width/2, -rect.height/2]
offset = [-rect.width/2,-3*rect.height/4]
window.blit(img, (self.x * grid_sz[0] + grid_sz[0]/2 + offset[0], self.y * grid_sz[1] + grid_sz[1]/2 + offset[1]))
if style == "classical":
# Draw the two possible types underneath the current_type image
for i in range(len(self.types)):
if always_reveal_states == True or self.types[i][0] != '?':
if self.types[i][0] == '?':
img = small_images[self.colour][self.types[i][1:]]
img = small_images[self.colour][self.types[i]]
img = small_images[self.colour]["unknown"] # If the type hasn't been revealed, show a placeholder
rect = img.get_rect()
offset = [-rect.width/2,-rect.height/2]
if i == 0:
target = (self.x * grid_sz[0] + grid_sz[0]/5 + offset[0], self.y * grid_sz[1] + 3*grid_sz[1]/4 + offset[1])
target = (self.x * grid_sz[0] + 4*grid_sz[0]/5 + offset[0], self.y * grid_sz[1] + 3*grid_sz[1]/4 + offset[1])
window.blit(img, target) # Blit shit
# Collapses the wave function!
def select(self):
if self.current_type == "unknown" or not self.choice in [0,1]:
self.choice = random.randint(0,1)
if self.types[self.choice][0] == '?':
self.types[self.choice] = self.types[self.choice][1:]
self.current_type = self.types[self.choice]
return self.choice
# Uncollapses (?) the wave function!
def deselect(self):
#print "Deselect called"
if (self.x + self.y) % 2 != 0:
if (self.types[0] != self.types[1]) or (self.types[0][0] == '?' or self.types[1][0] == '?'):
self.current_type = "unknown"
self.choice = -1
self.choice = 0 # Both the two types are the same
# The sad moment when you realise that you do not understand anything about a subject you studied for 4 years...
# --- --- #
[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
# Otherwise, they are left as unknown
# So you can use this class in Agent programs, and fill in the types as they are revealed
def __init__(self, style="agent"): = style
self.pieces = {"white" : [], "black" : []}
self.grid = [[None] * w for _ in range(h)] # 2D List (you can get arrays in python, somehow, but they scare me)
self.unrevealed_types = {"white" : piece_types.copy(), "black" : piece_types.copy()}
self.king = {"white" : None, "black" : None} # We need to keep track of the king, because he is important
self.max_moves = None
self.moves = 0
self.move_stack = []
for c in ["black", "white"]:
del self.unrevealed_types[c]["unknown"]
if style == "empty":
# Add all the pieces with known primary types
for i in range(0, 2):
s = ["black", "white"][i]
c = self.pieces[s]
y = [0, h-1][i]
c.append(Piece(s, 0, y, ["rook"]))
c.append(Piece(s, 1, y, ["knight"]))
c.append(Piece(s, 2, y, ["bishop"]))
k = Piece(s, 3, y, ["king", "king"]) # There can only be one ruler!
k.current_type = "king"
self.king[s] = k
c.append(Piece(s, 4, y, ["queen"])) # Apparently he may have multiple wives though.
c.append(Piece(s, 5, y, ["bishop"]))
c.append(Piece(s, 6, y, ["knight"]))
c.append(Piece(s, 7, y, ["rook"]))
if y == 0:
y += 1
y -= 1
# Lots of pawn
for x in range(0, w):
c.append(Piece(s, x, y, ["pawn"]))
types_left = {}
del types_left["king"] # We don't want one of these randomly appearing (although it might make things interesting...)
del types_left["unknown"] # We certainly don't want these!
for piece in c:
# Add to grid
self.grid[piece.x][piece.y] = piece
if len(piece.types) > 1:
if style == "agent": # Assign placeholder "unknown" secondary type
elif style == "quantum":
# The master allocates the secondary types
choice = types_left.keys()[random.randint(0, len(types_left.keys())-1)]
types_left[choice] -= 1
if types_left[choice] <= 0:
del types_left[choice]
piece.types.append('?' + choice)
elif style == "classical":
piece.current_type = piece.types[0]
piece.choice = 0
def clone(self):
newboard = Board(master = False)
newpieces = newboard.pieces["white"] + newboard.pieces["black"]
mypieces = self.pieces["white"] + self.pieces["black"]
for i in range(len(mypieces)):
# Reset the board from a string
def reset_board(self, s):
self.pieces = {"white" : [], "black" : []}
self.king = {"white" : None, "black" : None}
self.grid = [[None] * w for _ in range(h)]
for x in range(w):
for y in range(h):
self.grid[x][y] = None
for line in s.split("\n"):
if line == "":
if line[0] == "#":
tokens = line.split(" ")
[x, y] = map(int, tokens[len(tokens)-1].split(","))
current_type = tokens[1]
types = map(lambda e : e.strip(" '[],"), line.split('[')[1].split(']')[0].split(','))
target = Piece(tokens[0], x, y, types)
target.current_type = current_type
target.choice = types.index(current_type)
target.choice = -1
if target.current_type == "king":
self.king[tokens[0]] = target
self.grid[x][y] = target
def display_grid(self, window = None, grid_sz = [80,80]):
if window == None:
return # I was considering implementing a text only display, then I thought "Fuck that"
# The indentation is getting seriously out of hand...
for x in range(0, w):
for y in range(0, h):
if (x + y) % 2 == 0:
c = pygame.Color(200,200,200)
c = pygame.Color(64,64,64)
pygame.draw.rect(window, c, (x*grid_sz[0], y*grid_sz[1], (x+1)*grid_sz[0], (y+1)*grid_sz[1]))
def display_pieces(self, window = None, grid_sz = [80,80]):
if window == None:
for p in self.pieces["white"] + self.pieces["black"]:
p.draw(window, grid_sz,
# Draw the board in a pygame window
def display(self, window = None):
def verify(self):
for x in range(w):
for y in range(h):
if self.grid[x][y] == None:
if (self.grid[x][y].x != x or self.grid[x][y].y != y):
raise Exception(sys.argv[0] + ": MISMATCH " + str(self.grid[x][y]) + " should be at " + str(x) + "," + str(y))
# Select a piece on the board (colour is the colour of whoever is doing the selecting)
def select(self, x,y, colour=None):
if not self.on_board(x, y): # Get on board everyone!
raise Exception("BOUNDS " + str(x) + ","+str(y))
piece = self.grid[x][y]
if piece == None:
raise Exception("EMPTY")
if colour != None and piece.colour != colour:
raise Exception("COLOUR " + str(piece.colour) + " not " + str(colour))
# I'm not quite sure why I made this return a string, but screw logical design
return str(x) + " " + str(y) + " " + str( + " " + str(piece.current_type)
# Update the board when a piece has been selected
# "type" is apparently reserved, so I'll use "state"
def update_select(self, x, y, type_index, state, sanity=True, deselect=True):
#debug(str(self) + " update_select called")
piece = self.grid[x][y]
if piece.types[type_index] == "unknown":
if not state in self.unrevealed_types[piece.colour].keys() and sanity == True:
raise Exception("SANITY: Too many " + piece.colour + " " + state + "s")
self.unrevealed_types[piece.colour][state] -= 1
if self.unrevealed_types[piece.colour][state] <= 0:
del self.unrevealed_types[piece.colour][state]
piece.types[type_index] = state
piece.current_type = state
if deselect == True and len(self.possible_moves(piece)) <= 0:
piece.deselect() # Piece can't move; deselect it
# Piece needs to recalculate moves
piece.possible_moves = None
# Update the board when a piece has been moved
def update_move(self, x, y, x2, y2, sanity=True):
#debug(str(self) + " update_move called \""+str(x)+ " " + str(y) + " -> " + str(x2) + " " + str(y2) + "\"")
piece = self.grid[x][y]
#print "Moving " + str(x) + "," + str(y) + " to " + str(x2) + "," + str(y2) + "; possible_moves are " + str(self.possible_moves(piece))
if not [x2,y2] in self.possible_moves(piece) and sanity == True:
raise Exception("ILLEGAL move " + str(x2)+","+str(y2))
self.grid[x][y] = None
taken = self.grid[x2][y2]
if taken != None:
if taken.current_type == "king":
self.king[taken.colour] = None
self.grid[x2][y2] = piece
piece.x = x2
piece.y = y2
# If the piece is a pawn, and it reaches the final row, it becomes a queen
# I know you are supposed to get a choice
# But that would be effort
if piece.current_type == "pawn" and ((piece.colour == "white" and piece.y == 0) or (piece.colour == "black" and piece.y == h-1)):
if == "classical":
piece.types[0] = "queen"
piece.types[1] = "queen"
piece.types[piece.choice] = "queen"
piece.current_type = "queen"
piece.deselect() # Uncollapse (?) the wavefunction!
self.moves += 1
# All other pieces need to recalculate moves
for p in self.pieces["white"] + self.pieces["black"]:
p.possible_moves = None
# Update the board from a string
# Guesses what to do based on the format of the string
def update(self, result, sanity=True, deselect=True):
#debug(str(self) + " update called \""+str(result)+"\"")
# String always starts with 'x y'
s = result.split(" ")
[x,y] = map(int, s[0:2])
raise Exception("GIBBERISH \""+ str(result) + "\"") # Raise expectations
piece = self.grid[x][y]
if piece == None and sanity == True:
raise Exception("EMPTY " + str(x) + " " + str(y))
# If a piece is being moved, the third token is '->'
# We could get away with just using four integers, but that wouldn't look as cool
if "->" in s:
# Last two tokens are the destination
[x2,y2] = map(int, s[3:])
raise Exception("GIBBERISH \"" + str(result) + "\"") # Raise the alarm
# Move the piece (take opponent if possible)
self.update_move(x, y, x2, y2, sanity)
# Otherwise we will just assume a piece has been selected
type_index = int(s[2]) # We need to know which of the two types the piece is in; that's the third token
state = s[3] # The last token is a string identifying the type
raise Exception("GIBBERISH \"" + result + "\"") # Throw a hissy fit
# Select the piece
self.update_select(x, y, type_index, state, sanity=sanity, deselect=deselect)
return result
# Gets each piece that could reach the given square and the probability that it could reach that square
# Will include allied pieces that defend the attacker
def coverage(self, x, y, colour = None, reject_allied = True):
result = {}
if colour == None:
pieces = self.pieces["white"] + self.pieces["black"]
pieces = self.pieces[colour]
for p in pieces:
prob = self.probability_grid(p, reject_allied)[x][y]
if prob > 0:
result.update({p : prob})
return result
# Associates each square with a probability that the piece could move into it
# Look, I'm doing all the hard work for you here...
def probability_grid(self, p, reject_allied = True):
result = [[0.0] * w for _ in range(h)]
if not isinstance(p, Piece):
return result
if p.current_type != "unknown":
#sys.stderr.write(sys.argv[0] + ": " + str(p) + " moves " + str(self.possible_moves(p, reject_allied)) + "\n")
for point in self.possible_moves(p, reject_allied):
result[point[0]][point[1]] = 1.0
return result
for i in range(len(p.types)):
t = p.types[i]
prob = 1.0 / float(len(p.types))
if t == "unknown" or p.types[i][0] == '?':
total_types = 0
for t2 in self.unrevealed_types[p.colour].keys():
total_types += self.unrevealed_types[p.colour][t2]
for t2 in self.unrevealed_types[p.colour].keys():
prob2 = float(self.unrevealed_types[p.colour][t2]) / float(total_types)
#p.current_type = t2
for point in self.possible_moves(p, reject_allied, state=t2):
result[point[0]][point[1]] += prob2 * prob
#p.current_type = t
for point in self.possible_moves(p, reject_allied, state=t):
result[point[0]][point[1]] += prob
#p.current_type = "unknown"
return result
def prob_is_type(self, p, state):
prob = 0.5
result = 0
for i in range(len(p.types)):
t = p.types[i]
if t == state:
result += prob
if t == "unknown" or p.types[i][0] == '?':
total_prob = 0
for t2 in self.unrevealed_types[p.colour].keys():
total_prob += self.unrevealed_types[p.colour][t2]
for t2 in self.unrevealed_types[p.colour].keys():
if t2 == state:
result += prob * float(self.unrevealed_types[p.colour][t2]) / float(total_prob)
# Get all squares that the piece could move into