Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added board_evaluation/__init__.py
Empty file.
36 changes: 29 additions & 7 deletions visualization/BoardEvaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,28 @@
import sys
from ..board_evaluation import model
import numpy as np
import copy

def _board_to_feature_cube(goBoard, color_to_move):
def rotate_coord(coord, rot):
(y, x) = coord
size = 19
if rot & 1:
x = (size - 1) - x
if rot & 2:
y = (size - 1) - y
if rot & 4:
tmp = x
x = y
y = tmp
return (y, x)


def _board_to_feature_cube(goBoard, color_to_move, rotation):
enemy_color = goBoard.otherColor( color_to_move )
feature_cube = np.zeros((19,19, 8))
for row in range(goBoard.boardSize ):
for col in range(goBoard.boardSize ):
pos = (row,col)
pos = rotate_coord((row, col), rotation)
if goBoard.board.get(pos) == color_to_move:
if goBoard.goStrings[pos].liberties.size() == 1:
feature_cube[row][col][0] = 1.0
Expand Down Expand Up @@ -42,16 +57,23 @@ def __init__(self, tf_ckpt_path):

#feature_cube - [361,8] matrix of floats
#returns - [19,19] matrix of probabilities
def predict_single_sample(self, feature_cube):
def predict_single_sample(self, feature_cube, rotation):
y_pred = self.sess.run(self.y_conv, feed_dict={self.x:[feature_cube]})
return np.reshape(y_pred, [19,19])
res = np.reshape(y_pred, [19,19])
out = copy.copy(res);
for y in xrange(len(res)):
for x in xrange(len(res)):
(ry, rx) = rotate_coord((y, x), rotation)
out[ry][rx] = res[y][x]
return out

#board - GoBoard object
#rotation - rotation to use before sending to dcnn ([0-7], 0: no change)
#returns [19,19] matrix of floats, each float in [0,1] indicating probability black owns the territory
#at the end of the game
def evaluate_board(self, goBoard, color_to_move):
feature_cube = _board_to_feature_cube(goBoard, color_to_move)
predicted_ownership = self.predict_single_sample(feature_cube)
def evaluate_board(self, goBoard, color_to_move, rotation):
feature_cube = _board_to_feature_cube(goBoard, color_to_move, rotation)
predicted_ownership = self.predict_single_sample(feature_cube, rotation)

#the model was trained on predicting ownership of color to move
#here we swap this back to predicting ownership of black
Expand Down
45 changes: 10 additions & 35 deletions visualization/GoDriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
#TODO: fix this relative path, it will cause an import error if gogui is not started from the visualization directory
sys.path.append("../thirdparty")

import GoBoard #this is in the thirdparty directory
import gomill.sgf
import GoCNN.thirdparty.GoBoard as GoBoard #this is in the thirdparty directory
from BoardEvaluator import BoardEvaluator

def _swap_color(color):
Expand All @@ -18,51 +17,27 @@ def _swap_color(color):
raise ValueError("color needs to be w or b")

'''
GoDriver has a pointer to a gomill.Sgf_game object which contains the game tree defined by an sgf file.
The current position in the file in maintained by the Sgf_game.main_sequence_iter() iterator.
It also contains a BoardEvaluator object which will load the tensorflow model and be able to make predictions
GoDriver contains a BoardEvaluator object which will load the tensorflow model and be able to make predictions
based on the current board. '''
class GoDriver:
def __init__(self, sgf_filepath, tf_ckpt_path, BOARD_SIZE = 19):
def __init__(self, tf_ckpt_path, BOARD_SIZE = 19):
self.board_evaluator = BoardEvaluator(tf_ckpt_path)
self.BOARD_SIZE = BOARD_SIZE
self.load_sgf_file(sgf_filepath)
self.color_to_move = "b"

def load_sgf_file(self, sgf_filepath):
with open(sgf_filepath, 'r') as sgf_file:
sgfContents = sgf_file.read()
self.sgf = gomill.sgf.Sgf_game.from_string( sgfContents)
print("%s loaded. Winner: %s"%(sgf_filepath, self.sgf.get_winner()), file=sys.stderr)
self.board = GoBoard.GoBoard(self.BOARD_SIZE)
self.sgf_iterator = self.sgf.main_sequence_iter()

def reset_board(self):
self.board = GoBoard.GoBoard(self.BOARD_SIZE)
self.sgf_iterator = self.sgf.main_sequence_iter()

def gen_move(self):
try:
it = self.sgf_iterator.next()
color, move = it.get_move()
if move is None: #sometimes the first move isn't defined
it = self.sgf_iterator.next()
color, move = it.get_move()

except StopIteration: #at the end of the file
return "pass"

if move is None:
return "pass"
self.color_to_move = "b"

def play(self, color, move):
if move != 'pass':
(row,col) = move
self.board.applyMove( color, (row,col) )
self.color_to_move = _swap_color(color)
(row,col) = move
self.board.applyMove( color, (row,col) )
return row, col

#returns [19,19] matrix of floats indicating the probability black will own the territory at the end
#of the game
def evaluate_current_board(self):
def evaluate_current_board(self, rotation):
if self.board is None:
return np.zeros((19,19))
return self.board_evaluator.evaluate_board(self.board, self.color_to_move)
return self.board_evaluator.evaluate_board(self.board, self.color_to_move, rotation)
7 changes: 2 additions & 5 deletions visualization/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
# Visualizing the trained model
## Usage

Set the MODEL_PATH and SGF_DIRECTORY variables in main.py. If you cloned this repo a trained model .ckpt file
Set the MODEL_PATH variable in main.py. If you cloned this repo a trained model .ckpt file
should be in data/working.

Start gogui, then goto Program -> Attach and enter "python /path/to/main.py" for the command.
This should load a random sgf file in SGF_DIRECTORY. You can walk through the file in gogui
by clicking the "make program play" button. You can visualize the output of the model or
load a new sgf file by going to Tools -> Analyze Commands. Note after you load a new sgf make
sure to click the 'clear board' button to tell gogui to reset the board as well.
You can visualize the output of the model by going to Tools -> Analyze Commands.
Empty file added visualization/__init__.py
Empty file.
81 changes: 34 additions & 47 deletions visualization/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@

'''
This file contains the main loop which communicates to gogui via the Go Text Protocol (gtp) via stdin.
We start by loading a random sgf file contained in the SGF_DIRECTORY. You can walk through
the sgf file by clicking the 'make board_evaluator play' button in gogui. Everytime the
loadsgf command is sent from gogui (Tools -> Analyze Commands in gogui) we load
a new random sgf from the directory. When the predict ownership method is called
we return the models board evaluation prediction on the current state of the board.
When the predict ownership method is called we return the models board evaluation prediction on the
current state of the board.
'''

from __future__ import print_function
Expand All @@ -17,53 +14,48 @@
import os
from .GoDriver import GoDriver

MODEL_PATH = "../../data/working/board_eval_cnn_5layer.ckpt"
MODEL_PATH = "/home/tensorflow/src/GoCNN/data/working/board_eval_cnn_5layer.ckpt"

#everytime we reset the board we will load a random game from this directory to view
#SGF_DIRECTORY = "/home/justin/Programming/GoAI/Completing_Go_Games/pro_games"
SGF_DIRECTORY = "../../data/sgf_files"

N = 19 #size of the board
letter_coords = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T']

#sgf_dir - string, directory containing sgf files
#returns list of strings
def get_sgf_filelist(sgf_dir):
sgf_files = []
for subdir, dirs, files in os.walk(SGF_DIRECTORY):
for file in files:
filepath = subdir + os.sep + file
if filepath.endswith(".sgf"):
sgf_files.append(filepath)
print("Number of sgf files found: %d" %len(sgf_files), file=sys.stderr)
return sgf_files

#go from matrix index to board position string e.g. 0,2 -> A3
def coord_to_str(row, col):
return letter_coords[row] + str(col+1)
row = (19-1) - row
return letter_coords[col] + str(row+1)

def str_to_coord(coord):
row = int(coord[1:]) - 1
col = letter_coords.index(coord[0].upper())
return ( (19-1) - row, col)

#ownership_matrix - [N,N] matrix of floats output from the CNN model
#Formats a valid response string that can be fed into gogui as response to the 'predict_ownership' command
def influence_str(ownership_matrix):
rtn_str = "INFLUENCE "
for i in xrange(len(ownership_matrix)):
for j in xrange(len(ownership_matrix)):
rtn_str+= "%s %.1lf " %(coord_to_str(i,j), 2*(ownership_matrix[i][j] - .5)) #convert to [-1,1] scale
#rtn_str+= " %.1lf\n" %(ownership_matrix[i][j])
for y in xrange(len(ownership_matrix)):
for x in xrange(len(ownership_matrix)):
rtn_str+= "%s %.1lf " %(coord_to_str(y,x), 2*(ownership_matrix[y][x] - .5)) #convert to [-1,1] scale
return rtn_str

def gtp_io():
""" Main loop which communicates to gogui via GTP"""
known_commands = ['boardsize', 'clear_board', 'komi', 'play', 'genmove',
known_commands = ['boardsize', 'clear_board', 'komi', 'play',
'final_score', 'quit', 'name', 'version', 'known_command',
'list_commands', 'protocol_version', 'gogui-analyze_commands']
analyze_commands = ["gfx/Predict Final Ownership/predict_ownership",
"none/Load New SGF/loadsgf"]
sgf_files = get_sgf_filelist(SGF_DIRECTORY)
sgf_file = random.choice(sgf_files)
driver = GoDriver(sgf_file, MODEL_PATH)
analyze_commands = ["gfx/Final Ownership - No rotation/predict_ownership 0",
"gfx/Final Ownership - Rotation 1/predict_ownership 1",
"gfx/Final Ownership - Rotation 2/predict_ownership 2",
"gfx/Final Ownership - Rotation 3/predict_ownership 3",
"gfx/Final Ownership - Rotation 4/predict_ownership 4",
"gfx/Final Ownership - Rotation 5/predict_ownership 5",
"gfx/Final Ownership - Rotation 6/predict_ownership 6",
"gfx/Final Ownership - Rotation 7/predict_ownership 7" ]
driver = GoDriver(MODEL_PATH)

print("starting main.py: loading %s" %sgf_file,file=sys.stderr)
print("starting main.py", file=sys.stderr)
output_file = open("output.txt", "wb")
output_file.write("intializing\n")
while True:
Expand All @@ -88,30 +80,25 @@ def gtp_io():
print("Warning: Trying to set incompatible boardsize %s (!= %d)"%(command[1], N), file=sys.stderr)
elif command[0] == "clear_board":
driver.reset_board()
elif command[0] == "loadsgf":
sgf_file = random.choice(sgf_files)
print("Loading new file: %s" %sgf_file, file=sys.stderr)
print("Make sure to click 'Clear board and start new game' in the gui", file=sys.stderr)
driver.load_sgf_file(sgf_file)
elif command[0] == "komi":
pass
elif command[0] == "play":
color = command[1][0]
move = command[2]
if move != 'pass':
coord = str_to_coord(move)
driver.play(color, coord)
else:
driver.play(color, move)
pass
print("play", file=sys.stderr)
elif command[0] == "genmove":
#color_str = command[1] #currently we ignore this
tup = driver.gen_move()
if tup == "pass":
ret = "pass"
else:
ret = coord_to_str(tup[0],tup[1])
print("genmove", file=sys.stderr)
elif command[0] == "final_score":
print("final_score not implemented", file=sys.stderr)
elif command[0] == "name":
ret = 'board_evaluator'
elif command[0] == "predict_ownership":
ownership_prediction = driver.evaluate_current_board()
rotation = int(command[1])
ownership_prediction = driver.evaluate_current_board(rotation)
ret = influence_str(ownership_prediction)
elif command[0] == "version":
ret = '1.0'
Expand Down Expand Up @@ -142,4 +129,4 @@ def gtp_io():
output_file.close()

if __name__ == '__main__':
gtp_io()
gtp_io()
17 changes: 17 additions & 0 deletions visualization/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env python
# If you get package issues trying to run main.py try this.
# You'll need to setup GoCNN in ~/src/GoCNN.
# If it can't find gomill clone https://github.com/mattheww/gomill
# and copy gomill/gommil in ~/src
#
# Must be a better way to do this, i don't know python.

import os
import sys

# Find python packages in ~/src
sys.path.append(os.environ['HOME'] + '/src')

import GoCNN.visualization.main

GoCNN.visualization.main.gtp_io()