M2M Day 360: End-to-end code that actually (almost) works

Max Deutsch
4 min readOct 27, 2017

--

This post is part of Month to Master, a 12-month accelerated learning project. For October, my goal is to defeat world champion Magnus Carlsen at a game of chess.

Today, I finally got back to working on my chess algorithm, which I’m hoping to finish by Sunday night.

I started by throwing out all of the code I previously had, which I just couldn’t make work.

After watching a few Tensorflow tutorials on YouTube, I was able to write a new program that works with CSV datasets. I was even able to get it to run on a tiny, test dataset:

Now that I had figured out how to import a dataset into a Python program, and then subsequently run it through a machine learning model, I needed to expand my data processing program from a few days ago, so that I could convert chess games in PGN format into rows in my CSV file.

Here’s what those changes look like:

import chess
import chess.pgn
import chess.uci
board = chess.Board()
pgn = open("data/caruana_carlsen_2017.pgn")
test_game = chess.pgn.read_game(pgn)
engine = chess.uci.popen_engine("stockfish")
engine.uci()
info_handler = chess.uci.InfoHandler()
engine.info_handlers.append(info_handler)
prev_eval = 0
diff = 0
output_data_string = ''
def convertLetterToNumber(letter):
if letter == 'K':
return '1,0,0,0,0,0,0,0,0,0,0,0,'
if letter == 'Q':
return '0,1,0,0,0,0,0,0,0,0,0,0,'
if letter == 'R':
return '0,0,1,0,0,0,0,0,0,0,0,0,'
if letter == 'B':
return '0,0,0,1,0,0,0,0,0,0,0,0,'
if letter == 'N':
return '0,0,0,0,1,0,0,0,0,0,0,0,'
if letter == 'P':
return '0,0,0,0,0,1,0,0,0,0,0,0,'
if letter == 'k':
return '0,0,0,0,0,0,1,0,0,0,0,0,'
if letter == 'q':
return '0,0,0,0,0,0,0,1,0,0,0,0,'
if letter == 'r':
return '0,0,0,0,0,0,0,0,1,0,0,0,'
if letter == 'b':
return '0,0,0,0,0,0,0,0,0,1,0,0,'
if letter == 'n':
return '0,0,0,0,0,0,0,0,0,0,1,0,'
if letter == 'p':
return '0,0,0,0,0,0,0,0,0,0,0,1,'
if letter == '1':
return '0,0,0,0,0,0,0,0,0,0,0,0,'
if letter == '2':
return '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'
if letter == '3':
return '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'
if letter == '4':
return '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'
if letter == '5':
return '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'
if letter == '6':
return '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'
if letter == '7':
return '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'
if letter == '8':
return '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'
if letter == '/':
return ''
def convertToBB(board):
bitBoard = ''
board = str(board.fen()).split(' ')[0]
for letter in board:
bitBoard = bitBoard + convertLetterToNumber(letter)
bitBoard = bitBoard[1:-1]
return bitBoard
for move in test_game.main_line():

engine.position(board)
engine.go(movetime=2000)
evaluation = info_handler.info["score"][1].cp
evaluation_label = ""
if board.turn:
if prev_eval - evaluation > 0.3:
evaluation_label = "B" #badmove
else:
evaluation_label = "G" #goodmove

if not board.turn:
evaluation *= -1
if evaluation - prev_eval > 0.3:
evaluation_label = "B" #badmove
else:
evaluation_label = "G" #goodmove

prev_eval = evaluation

board.push_uci(move.uci())
out_board = convertToBB(board)

output_data_string = output_data_string + out_board + ',' + evaluation_label + '\n'

f = open('chessdata.csv','w')
f.write(output_data_string)
f.close()

And, here’s the output… (Each row has 768 columns of 1’s and 0’s corresponding to a bitboard representation of a chess position, and the 769th column is either a G or a B, labeling the chess position as “good” or “bad” accordingly. I haven’t yet encoded castling rights. I’ll do that once I can get my current setup to work.)

With this done, I had the two pieces I needed: 1. A way to convert chess positions into CSV files, and 2. A way to read CSV files into a machine learning model.

However, when I tried to put them together, the program wouldn’t run. Instead, I just got errors like this one…

pandas.errors.ParserError: Error tokenizing data. C error: Expected 2 fields in line 8, saw 5

…suggesting that my dataset was “dirty” in some capacity.

And this one…

ValueError: Cannot feed value of shape (43, 1) for Tensor u’Placeholder_1:0', which has shape ‘(?, 2)’

…suggesting that my data wasn’t conforming to the machine learning model, which is also likely a result of “dirty” data.

I tried to clean up the data by hand, but continued getting error messages.

Eventually, I discovered that I was generating a CSV file that sometimes contained two commas in a row (see below), which was causing my problems.

I found the bug in my data processing program, and then tried to run my machine learning model on the dataset again.

Finally, it worked (in the sense that it was running), but it was failing to calculate the “cost function”, which is one of the most important ingredients to the machine learning training process.

I was able to reduce the dataset down, and get it to train normally (with a calculated cost, etc.), but, for some reason, when scaling it up to the full dataset, something is breaking down.

Tomorrow, I’ll try to diagnose the problem.

Nevertheless, I made a ton of progress today, and nearly have everything working together. Once everything’s working, I suspect I will need to spend some time optimizing the model (i.e. finding the optimal balance between maximum chess performance and maximum learnability).

Read the next post. Read the previous post.

Max Deutsch is an obsessive learner, product builder, and guinea pig for Month to Master.

If you want to follow along with Max’s year-long accelerated learning project, make sure to follow this Medium account.

--

--