Training a Chess AI using TensorFlow

Training a Chess AI using TensorFlow

in

layout: post title: “Training a Chess AI using TensorFlow” subtitle: “Using the power of deep learning and Stockfish to train a neural network to play the game of chess.” background: ‘/img/posts/chessai/chess.jpeg’ —

Why am I doing this?

To put it plainly, I love chess. Don’t get me wrong, I’m not a great player. But, I have been playing chess since I was 5 or 6. Although I have never been good enough to compete, the game has been around me a lot and its a part of my life.

recently during the lockdown I have started playing chess again and it really peaked my interest. But after being exposed to machine learning and deep learning I always wanted to build my own ChessBot/ChessAI.

First step ofcourse is to come up with a plan and here’s mine

  • Make a reinforcement learning model that learns by playing itself.
  • Make a regression model that evaluates a position (like Stockfish)

Installing Stockfish

Now that we know what to do, its time to first install stockfish. You can clone my github repo here (contains all files including stockfish).

To generate the board and dictate the general play we will use the python-chess library.

I will be sharing some code and they key ideas and concepts that are involved, the complete code can be found here.

# this function will create our x (board)
def random_board(max_depth=200):
  board = chess.Board()
  depth = random.randrange(0, max_depth)

  for _ in range(depth):
    all_moves = list(board.legal_moves)
    random_move = random.choice(all_moves)
    board.push(random_move)
    if board.is_game_over():
      break
  return board

# this function will create our f(x) (score)
def stockfish(board, depth):
  with chess.engine.SimpleEngine.popen_uci('stockfish/13/bin/stockfish') as sf:
    result = sf.analyse(board, chess.engine.Limit(depth=depth))
    score = result['score'].white().score()
    return score

Using this code we can evaluate any chess position. A positive number “White is winning” or negative number meaning “Black is winning”. Using this function we can create a dataset that can evaluate a set of random positions. We can then create a model that can use this information to evaluate future predictions.

Creating the dataset

Now we need to convert the board representation to something meaningful. A 3d matrix of sizes 8 x 8 x 14 where 8x8 repersents the board and the 14 represents the 7 different pieces.

squares_index  =  {  'a':  0,  'b':  1,  'c':  2,  'd':  3,  'e':  4,  'f':  5,  'g':  6,  'h':  7  }

def square_to_index(square):
  letter = chess.square_name(square)
  return 8 - int(letter[1]), squares_index[letter[0]]
  
def split_dims(board):
  # this is the 3d matrix
  board3d = numpy.zeros((14, 8, 8), dtype=numpy.int8)
  for piece in chess.PIECE_TYPES:
    for square in board.pieces(piece, chess.WHITE):
      idx = numpy.unravel_index(square, (8, 8))
      board3d[piece - 1][7 - idx[0]][idx[1]] = 1
    for square in board.pieces(piece, chess.BLACK):
      idx = numpy.unravel_index(square, (8, 8))
      board3d[piece + 5][7 - idx[0]][idx[1]] = 1
    aux = board.turn
	board.turn = chess.WHITE
    for move in board.legal_moves:
	  i, j = square_to_index(move.to_square)
      board3d[12][i][j] = 1
    board.turn = chess.BLACK
    for move in board.legal_moves:
      i, j = square_to_index(move.to_square)
      board3d[13][i][j] = 1
  board.turn = aux

  return board3d

This function above maps the pieces to the squares and stacks them into an array of size (8,8,14). We can use this multi-dimentional array as an input into our Convolutional Neural Network(CNN) to predict our evaluation.

Building the model

I have made a function through which you can train a model on a parameter of depth. Its a simple Convolutional layer with a flatten layer proceeding it. It contains a ‘same’ padding parameter as we do not want to change the overall dimensions of the input during the flow.

def build_model(conv_size, conv_depth):
  board3d = layers.Input(shape=(14, 8, 8))
  x = board3d
  for _ in range(conv_depth):
    x = layers.Conv2D(filters=conv_size, kernel_size=3,
    padding='same', activation='relu')(x)
  x = layers.Flatten()(x)
  x = layers.Dense(64, 'relu')(x)
  x = layers.Dense(1, 'sigmoid')(x)
  return models.Model(inputs=board3d, outputs=x)

There a bunch of designs you can try I’ve experimented with a few ones. You can find them in my repo.

Results

The only step left is to train the model. I’ve trained my models on google colab. and tested them out on my local machine. I have run the following tests:

  1. AI (white) vs Stockfish (Black)
  2. AI (white) vs AI (Black)
  3. AI (white) vs Human (Black) - me

What I have learned from these results is that the AI knows how to play chess, but it does’nt really play at a good level. Stockfish was very easily able to beat it, this was expected as the AI was trying to imitate stockfish and we can’t expect it to be better than the data it was trained on. My over the board rating is around 1300 elo and I was comfortably able to beat it. My guesstimate is that this AI might be around 600 elo or even lower like a beginner in the game. It starts of well, but makes blunders quite often and ends up losing the games. I will try other methods like reinforcement learning later on. You can contact me at my socials linked below and with that peace out!