Commit 877034f0 authored by Sam Moore's avatar Sam Moore
Browse files

Progress?

- Fixed .dll files that were the wrong ones for cx_freeze win32
- Created "internal" agents, and made AgentBishop one of them
- Implemented terrible wrapper class that runs an internal agent in a seperate python process
  - This is for when timeouts are used
  - select == better than threads
  - TimeoutPlayer appears to not work properly anyway
- Tested win32 version (script and frozen binary)
  - ExternalAgent seems to break
  - TimeoutPlayer seems to break (even more than normal)
  - Pretty much everything breaks actually
    - More things seem to break with the frozen binary than with the script
    - Amazingly the pygame interface doesn't break, just the fancy threading/subprocess/socket/file related stuff
  - Somehow, the InternalAgents, ie: AgentBishop were working
  - So windows users can play the python sample agents, but otherwise the program is pretty useless

- Implemented the --log and --file arguments
  - Get an error if the game hasn't actually finished when using --file, should probably fix
    - It might be cool to let people use --file for a set number of moves, and then continue play from that position
      - Would need more work...

- When graphics are disabled, the game still works
  - But I need to test it on a server that doesn't have pygame at all

TODO: Finish the qchess program and actually work on setting up the competition.
parent 521b6fc0
......@@ -3,192 +3,15 @@
from qchess import *
"""
Agent Bishop
( an agent, not an implementation of a bishop chess piece!)
"""
# Skeleton class for your agent
class Agent(AgentRandom): # Inherits from AgentRandom (in qchess.py)
def __init__(self, name, colour):
AgentRandom.__init__(self, name, colour)
self.value = {"pawn" : 1, "bishop" : 3, "knight" : 3, "rook" : 5, "queen" : 9, "king" : 100, "unknown" : 4}
self.aggression = 2.0 # Multiplier for scoring due to aggressive actions
self.defence = 1.0 # Multiplier for scoring due to defensive actions
self.depth = 0 # Current depth
self.max_depth = 2 # Recurse this many times (for some reason, makes more mistakes when this is increased???)
self.recurse_for = -1 # Recurse for the best few moves each times (less than 0 = all moves)
for p in self.board.pieces["white"] + self.board.pieces["black"]:
p.last_moves = None
p.selected_moves = None
def get_value(self, piece):
if piece == None:
return 0.0
return float(self.value[piece.types[0]] + self.value[piece.types[1]]) / 2.0
# Score possible moves for the piece
def prioritise_moves(self, piece):
#sys.stderr.write(sys.argv[0] + ": prioritise called for " + str(piece) + "\n")
grid = self.board.probability_grid(piece)
#sys.stderr.write("\t Probability grid " + str(grid) + "\n")
moves = []
for x in range(w):
for y in range(h):
if grid[x][y] < 0.3: # Throw out moves with < 30% probability
#sys.stderr.write("\tReject " + str(x) + "," + str(y) + " (" + str(grid[x][y]) + ")\n")
continue
target = self.board.grid[x][y]
# Get total probability that the move is protected
[xx,yy] = [piece.x, piece.y]
[piece.x, piece.y] = [x, y]
self.board.grid[x][y] = piece
self.board.grid[xx][yy] = None
defenders = self.board.coverage(x, y, piece.colour, reject_allied = False)
d_prob = 0.0
for d in defenders.keys():
d_prob += defenders[d]
if len(defenders.keys()) > 0:
d_prob /= float(len(defenders.keys()))
if (d_prob > 1.0):
d_prob = 1.0
# Get total probability that the move is threatened
attackers = self.board.coverage(x, y, opponent(piece.colour), reject_allied = False)
a_prob = 0.0
for a in attackers.keys():
a_prob += attackers[a]
if len(attackers.keys()) > 0:
a_prob /= float(len(attackers.keys()))
if (a_prob > 1.0):
a_prob = 1.0
self.board.grid[x][y] = target
self.board.grid[xx][yy] = piece
[piece.x, piece.y] = [xx, yy]
# Score of the move
value = self.aggression * (1.0 + d_prob) * self.get_value(target) - self.defence * (1.0 - d_prob) * a_prob * self.get_value(piece)
# Adjust score based on movement of piece out of danger
attackers = self.board.coverage(piece.x, piece.y, opponent(piece.colour))
s_prob = 0.0
for a in attackers.keys():
s_prob += attackers[a]
if len(attackers.keys()) > 0:
s_prob /= float(len(attackers.keys()))
if (s_prob > 1.0):
s_prob = 1.0
value += self.defence * s_prob * self.get_value(piece)
# Adjust score based on probability that the move is actually possible
moves.append([[x, y], grid[x][y] * value])
moves.sort(key = lambda e : e[1], reverse = True)
#sys.stderr.write(sys.argv[0] + ": Moves for " + str(piece) + " are " + str(moves) + "\n")
piece.last_moves = moves
piece.selected_moves = None
return moves
def select_best(self, colour):
self.depth += 1
all_moves = {}
for p in self.board.pieces[colour]:
self.choice = p # Temporarily pick that piece
m = self.prioritise_moves(p)
if len(m) > 0:
all_moves.update({p : m[0]})
if len(all_moves.items()) <= 0:
return None
opts = all_moves.items()
opts.sort(key = lambda e : e[1][1], reverse = True)
if self.depth >= self.max_depth:
self.depth -= 1
return list(opts[0])
if self.recurse_for >= 0:
opts = opts[0:self.recurse_for]
#sys.stderr.write(sys.argv[0] + " : Before recurse, options are " + str(opts) + "\n")
This is a wrapper to AgentBishop, which can now be found directly in qchess as one of the internal agents
As well as wrapping, it will also show AgentBishop's thought processes in graphics, which is kind of cool
# Take the best few moves, and recurse
for choice in opts[0:self.recurse_for]:
[xx,yy] = [choice[0].x, choice[0].y] # Remember position
[nx,ny] = choice[1][0] # Target
[choice[0].x, choice[0].y] = [nx, ny] # Set position
target = self.board.grid[nx][ny] # Remember piece in spot
self.board.grid[xx][yy] = None # Remove piece
self.board.grid[nx][ny] = choice[0] # Replace with moving piece
# Recurse
best_enemy_move = self.select_best(opponent(choice[0].colour))
choice[1][1] -= best_enemy_move[1][1] / float(self.depth + 1.0)
[choice[0].x, choice[0].y] = [xx, yy] # Restore position
self.board.grid[nx][ny] = target # Restore taken piece
self.board.grid[xx][yy] = choice[0] # Restore moved piece
opts.sort(key = lambda e : e[1][1], reverse = True)
#sys.stderr.write(sys.argv[0] + " : After recurse, options are " + str(opts) + "\n")
self.depth -= 1
return list(opts[0])
So basically, using `./qchess.py @internal:AgentBishop` is better, unless you want to see the graphics
"""
# Returns [x,y] of selected piece
def select(self):
self.choice = self.select_best(self.colour)[0]
return [self.choice.x, self.choice.y]
# Returns [x,y] of square to move selected piece into
def get_move(self):
self.choice.selected_moves = self.choice.last_moves
moves = self.prioritise_moves(self.choice)
if len(moves) > 0:
return moves[0][0]
else:
return AgentRandom.get_move(self)
# Horrible messy graphics class that draws what the agent is doing, kind of useful for testing
class AgentGraphics(GraphicsThread):
class AgentBishop_Graphics(GraphicsThread):
def __init__(self, board, title):
GraphicsThread.__init__(self, board, title, grid_sz = [64,64])
self.choice = None
......@@ -280,42 +103,13 @@ class AgentGraphics(GraphicsThread):
self.moves = None
pygame.display.quit()
# Main function; don't alter
def main(argv):
global agent
colour = sys.stdin.readline().strip("\n") # Gets the colour of the agent from stdin
agent = Agent(argv[0], colour) # Creates your agent
#graphics = AgentGraphics(agent.board, title="Agent Bishop (" + str(colour) + ") - DEBUG VIEW")
#graphics.start()
# Plays quantum chess using your agent
while True:
line = sys.stdin.readline().strip(" \r\n")
#sys.stderr.write(argv[0] + ": gets line \"" + str(line) + "\"\n")
if line == "SELECTION?":
[x,y] = agent.select() # Gets your agent's selection
#print "Select " + str(x) + "," + str(y)
sys.stdout.write(str(x) + " " + str(y) + "\n")
elif line == "MOVE?":
[x,y] = agent.get_move() # Gets your agent's move
sys.stdout.write(str(x) + " " + str(y) + "\n")
elif line.split(" ")[0] == "QUIT":
agent.quit(" ".join(line.split(" ")[1:])) # Quits the game
# graphics.stop()
break
else:
agent.update(line) # Updates agent.board
#graphics.stop()
#graphics.join()
return 0
# Don't touch this
if __name__ == "__main__":
sys.exit(main(sys.argv))
colour = sys.stdin.readline().strip("\r\n")
agent = AgentBishop(sys.argv[0], colour)
graphics = AgentBishop_Graphics(agent.board, "Agent Bishop ("+agent.colour+") DEBUG")
graphics.start()
run_agent(agent)
graphics.stop()
graphics.join()
:: 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
......@@ -6,7 +6,7 @@ DLL_PATH=win32_dll
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
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
......
......@@ -42,6 +42,18 @@ ARGUMENTS
IMPORTANT: Only ONE of the games should give the other's address.
@internal:name
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
OPTIONS
--help
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment