Jogo xadrez há muito tempo e, desde que comecei no CS, sempre quis criar um bot de xadrez. Eu finalmente consegui?
Aqui está um vídeo onde eu (branco) sou esmagado pelo meu bot (preto).
Aqui está seu perfil no chess.com: https://www.chess.com/member/chessables_with_chat_gpt.
git clone https://github.com/samliu21/chess-ai
. Navegue até o diretório com cd chess-ai
.python -m venv .
e ative-o com source bin/activate
.python -m pip install -r requirements.txt
.cd gui
e chame python main.py
para jogar! Usei o banco de dados oficial do Lichess, que continha jogos no formato PGN padrão. Aqui está o processo de limpeza de dados:
pgn-extract
para adicionar FENs após cada movimento Para obter mais informações, consulte a pasta data_cleaning
.
Inicialmente, tentei criar uma rede neural de avaliação de placa para emparelhar com um algoritmo minimax. Houve dois problemas com essa abordagem:
A rede de avaliação não atendeu às minhas expectativas. Podia detectar desequilíbrios materiais, mas não conseguia detectar simples xeque-mate.
Devido ao grande espaço de ação no xadrez, o algoritmo minimax é muito lento, mesmo quando otimizado com poda alfa-beta.
Juntos, esses fatores me levaram a abandonar essa ideia inicial e tentar outra.
A GUI foi feita à mão usando os módulos pygame
e python-chess
.
Esta arquitetura foi amplamente inspirada neste artigo de Standford.
A IA usa dois modelos. Ambos recebem uma posição no tabuleiro como entrada e geram uma matriz 8x8
de probabilidades softmax. O "modelo de" prevê o quadrado a ser movido e o "modelo para" prevê o quadrado a ser movido.
Esta abordagem é melhor ilustrada com um exemplo. Considere a posição inicial do tabuleiro e o movimento: Nf3
. A avaliação deste movimento é o produto do valor no quadrado g1
do modelo from e o valor no quadrado f3
do modelo to.
Dentre todos os movimentos legais, o maior produto é o movimento selecionado.
As redes neurais consistem em seis camadas convolucionais, seguidas por duas camadas afins e uma camada de saída. Um esboço mais detalhado da arquitetura pode ser encontrado abaixo:
Model: "model"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_1 (InputLayer) [(None, 8, 8, 12)] 0 []
conv2d (Conv2D) (None, 8, 8, 32) 3488 ['input_1[0][0]']
batch_normalization (BatchNorm (None, 8, 8, 32) 128 ['conv2d[0][0]']
alization)
activation (Activation) (None, 8, 8, 32) 0 ['batch_normalization[0][0]']
conv2d_1 (Conv2D) (None, 8, 8, 64) 18496 ['activation[0][0]']
batch_normalization_1 (BatchNo (None, 8, 8, 64) 256 ['conv2d_1[0][0]']
rmalization)
activation_1 (Activation) (None, 8, 8, 64) 0 ['batch_normalization_1[0][0]']
conv2d_2 (Conv2D) (None, 8, 8, 256) 147712 ['activation_1[0][0]']
batch_normalization_2 (BatchNo (None, 8, 8, 256) 1024 ['conv2d_2[0][0]']
rmalization)
activation_2 (Activation) (None, 8, 8, 256) 0 ['batch_normalization_2[0][0]']
concatenate (Concatenate) (None, 8, 8, 512) 0 ['activation_2[0][0]',
'activation_2[0][0]']
conv2d_3 (Conv2D) (None, 8, 8, 256) 1179904 ['concatenate[0][0]']
batch_normalization_3 (BatchNo (None, 8, 8, 256) 1024 ['conv2d_3[0][0]']
rmalization)
activation_3 (Activation) (None, 8, 8, 256) 0 ['batch_normalization_3[0][0]']
concatenate_1 (Concatenate) (None, 8, 8, 320) 0 ['activation_3[0][0]',
'activation_1[0][0]']
conv2d_4 (Conv2D) (None, 8, 8, 256) 737536 ['concatenate_1[0][0]']
batch_normalization_4 (BatchNo (None, 8, 8, 256) 1024 ['conv2d_4[0][0]']
rmalization)
activation_4 (Activation) (None, 8, 8, 256) 0 ['batch_normalization_4[0][0]']
concatenate_2 (Concatenate) (None, 8, 8, 288) 0 ['activation_4[0][0]',
'activation[0][0]']
conv2d_5 (Conv2D) (None, 8, 8, 256) 663808 ['concatenate_2[0][0]']
batch_normalization_5 (BatchNo (None, 8, 8, 256) 1024 ['conv2d_5[0][0]']
rmalization)
activation_5 (Activation) (None, 8, 8, 256) 0 ['batch_normalization_5[0][0]']
dense (Dense) (None, 8, 8, 256) 65792 ['activation_5[0][0]']
batch_normalization_6 (BatchNo (None, 8, 8, 256) 1024 ['dense[0][0]']
rmalization)
dense_1 (Dense) (None, 8, 8, 64) 16448 ['batch_normalization_6[0][0]']
batch_normalization_7 (BatchNo (None, 8, 8, 64) 256 ['dense_1[0][0]']
rmalization)
dense_2 (Dense) (None, 8, 8, 1) 65 ['batch_normalization_7[0][0]']
batch_normalization_8 (BatchNo (None, 8, 8, 1) 4 ['dense_2[0][0]']
rmalization)
softmax (Softmax) (None, 8, 8, 1) 0 ['batch_normalization_8[0][0]']
==================================================================================================
Total params: 2,839,013
Trainable params: 2,836,131
Non-trainable params: 2,882
__________________________________________________________________________________________________