niklasf / python-chess

A chess library for Python, with move generation and validation, PGN parsing and writing, Polyglot opening book reading, Gaviota tablebase probing, Syzygy tablebase probing, and UCI/XBoard engine communication
https://python-chess.readthedocs.io/en/latest/
GNU General Public License v3.0
2.43k stars 532 forks source link

Using masking to index a np.array #120

Closed GabrielPereyra closed 7 years ago

GabrielPereyra commented 7 years ago

I am training a neural network to play chess.

To predict a move, the neural network produces a (16, 64) dimensional array which represents the values of each move; the first dimension indexes the pieces and the second corresponds to chess.SQUARES. However, the majority of the moves in this array are illegal. Although I could iterate through this array and only grab values that correspond to valid moves, I was wondering if there was a way to use the existing masking code in chess/_init_.py to accomplish this.

As a toy example, I am trying to solve a Rook/King vs. King endgame. This means that the output is (2, 64) array for white. The following code shows how I select a move from this output array:

import chess
import numpy as np

def index_to_square(index):    
    return chess.SQUARE_NAMES[index]

def choose_action(softmax):
    king_index, rook_index = np.argmax(softmax, axis=1)

    if softmax[0, king_index] > softmax[1, rook_index]:
        return  'K{}'.format(index_to_square(king_index))
    else:
        return 'R{}'.format(index_to_square(rook_index))

softmax = np.random.random((2, 64))
print choose_action(softmax)

Although the naive masking of a King and Rook is relatively easy, it would be nice to use the existing masking to handle all of the corner cases. Ideally, it would look something like:

softmax = np.random.random(64)
mask = np.random.randint(0, 2, 64)
valid_softmax = np.ma.array(softmax, mask=mask)

Thanks!

niklasf commented 7 years ago

Using masks is great for pseudo legal move generation because you can simply get the mask from a lookup table based on occupancy and square.

For legal moves each move has to be checked individually, so I don't think there's a great solution using bitmasks there. (Each single bit would have to be checked/set at a time.)