Source code for crocrodile.engine

# -*- coding: utf-8 -*-
"""
MyEngine Engine base.

Base engine
"""
from __future__ import print_function
import math
import random
# import requests
# import copy
import csv
import time
import chess
import chess.polyglot
import crocrodile.nn as nn
import crocrodile.engine.evaluate as evaluation

PAWN_VALUE = 130
KNIGHT_VALUE = 290
BISHOP_VALUE = 310
ROOK_VALUE = 500
QUEEN_VALUE = 901
KING_VALUE = 0  # Infinity is too complex
BISHOPS_PAIR = 50
PROTECTED_KING = 5
PIECES_VALUES = {"p": PAWN_VALUE, "n": KNIGHT_VALUE, "b": BISHOP_VALUE,
                 "r": ROOK_VALUE, "q": QUEEN_VALUE, "k": KING_VALUE}
CENTRAL_SQUARES = [36, 35, 28, 27]
ELARGED_SQUARES = [45, 44, 43, 42, 37, 34, 29, 26, 21, 20, 19, 18]
SEVENTH_ROW = [55, 54, 53, 52, 51, 50, 49, 48]
EIGHT_ROW = [56, 57, 58, 59, 60, 61, 62, 63]
SECOND_ROW = [15, 14, 13, 12, 11, 10, 9, 8]
FIRST_ROW = [0, 1, 2, 3, 4, 5, 6, 7]
VARIANTS = ['standard', 'chess960']


[docs]def printi(*args): """Debug mode printer.""" print("info string", args)
[docs]class EngineBase: """Engine base.""" def __init__(self, name, author, board=chess.Board()): """Initialise engine.""" self.name = name self.author = author self.board = board self.tb = dict() self.tb_limit = 10000000 self.nn_tb = dict() self.nn_tb_limit = 1000000 self.nn = nn.NeuralNetwork() self.nn.load_layers(0) self.evaluator = evaluation.Evaluator() self.nn.load_layers(0)
[docs] def evaluate(self, board): """Evaluate position.""" return self.evaluator.evaluate(board)
[docs] def search(self, depth, board): """Search best move (Minimax from wikipedia)."""
[docs] def minimax_std(self, board, depth, maximimize_white, limit_time): """Minimax algorithm from Wikipedia with NN.""" if depth == 0 or board.is_game_over(): zobrist_hash = chess.polyglot.zobrist_hash(board) if zobrist_hash not in self.tb: # if j'ai du temps self.tb[zobrist_hash] = self.evaluate(board) if len(self.tb) > self.tb_limit: del self.tb[list(self.tb.keys())[0]] # else # evaluation = self.tb[zobrist_hash] # evaluation = self.evaluate(board) attackers = board.attackers(board.turn, board.peek().to_square) if len(attackers) > 0: # Quiescent if board.turn == chess.WHITE: evaluation += PIECES_VALUES[board.piece_map() [board.peek().to_square]. symbol().lower()] else: evaluation -= PIECES_VALUES[board.piece_map() [board.peek().to_square]. symbol().lower()] return evaluation, chess.Move.from_uci("0000") if maximimize_white: value = -float('inf') legal_moves = list(board.legal_moves) list_best_moves = [legal_moves[0]] for move in legal_moves: if time.time() > limit_time: return float('inf'), chess.Move.from_uci("0000") test_board = chess.Board(fen=board.fen()) test_board.push(move) evaluation = self.minimax_std( test_board, depth-1, False, limit_time)[0] if value == evaluation: list_best_moves.append(move) elif value < evaluation: value = evaluation list_best_moves = [move] return value, random.choice(list_best_moves) else: # minimizing white value = float('inf') legal_moves = list(board.legal_moves) list_best_moves = [legal_moves[0]] for move in legal_moves: if time.time() > limit_time: return float('inf'), chess.Move.from_uci("0000") test_board = chess.Board(fen=board.fen()) test_board.push(move) evaluation = self.minimax_std( test_board, depth-1, True, limit_time)[0] if value == evaluation: list_best_moves.append(move) elif value > evaluation: value = evaluation list_best_moves = [move] return value, random.choice(list_best_moves)
[docs] def nn_select_best_moves(self, board): """Select best moves in board.""" hash = chess.polyglot.zobrist_hash(board) if hash not in self.nn_tb: if len(self.nn_tb) > self.nn_tb_limit: del self.nn_tb[list(self.nn_tb.keys())[0]] good_moves = list() for move in board.legal_moves: if self.nn.check_move(board.fen(), move.uci()): good_moves.append(move) self.nn_tb[hash] = good_moves good_moves = self.nn_tb[hash] if not good_moves: good_moves = list(board.legal_moves) return good_moves
# + param time + param best move depth-1 + param evaluation
[docs] def minimax_nn(self, board, depth, maximimize_white, limit_time): """Minimax algorithm from Wikipedia with NN.""" if depth == 0 or board.is_game_over(): zobrist_hash = chess.polyglot.zobrist_hash(board) if zobrist_hash not in self.tb: # if j'ai du temps self.tb[zobrist_hash] = self.evaluate(board) if len(self.tb) > self.tb_limit: del self.tb[list(self.tb.keys())[0]] # else # evaluation = self.tb[zobrist_hash] # evaluation = self.evaluate(board) attackers = board.attackers(board.turn, board.peek().to_square) if len(attackers) > 0: # Quiescent if board.turn == chess.WHITE: evaluation += PIECES_VALUES[board.piece_map() [board.peek().to_square]. symbol().lower()] else: evaluation -= PIECES_VALUES[board.piece_map() [board.peek().to_square]. symbol().lower()] return evaluation, chess.Move.from_uci("0000") if maximimize_white: value = -float('inf') legal_moves = list(board.legal_moves) list_best_moves = [legal_moves[0]] for move in self.nn_select_best_moves(board): if time.time() > limit_time: return float('inf'), chess.Move.from_uci("0000") test_board = chess.Board(fen=board.fen()) test_board.push(move) evaluation = self.minimax_nn( test_board, depth-1, False, limit_time)[0] if value == evaluation: list_best_moves.append(move) elif value < evaluation: value = evaluation list_best_moves = [move] return value, random.choice(list_best_moves) else: # minimizing white value = float('inf') legal_moves = list(board.legal_moves) list_best_moves = [legal_moves[0]] for move in self.nn_select_best_moves(board): if time.time() > limit_time: return float('inf'), chess.Move.from_uci("0000") test_board = chess.Board(fen=board.fen()) test_board.push(move) evaluation = self.minimax_nn( test_board, depth-1, True, limit_time)[0] if value == evaluation: list_best_moves.append(move) elif value > evaluation: value = evaluation list_best_moves = [move] return value, random.choice(list_best_moves)