How can I build a chess uci computer in python?

104 Views Asked by At

I am trying to make a chess engine in python, and so far, everything was fine. I want to make it compatible with uci so I can sign it up on lichess and also display the moves in a chess gui like arena.

import time
import chess
import threading
import chess.pgn
from datetime import datetime


fens = ["rnbqkbnr/pppp1ppp/8/4p3/3PP3/8/PPP2PPP/RNBQKBNR b KQkq d3 0 2", "rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2", "r1bqkbnr/pppp1ppp/2n5/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 3 3", "rnbqkbnr/ppp1pppp/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 2", "rnbqkbnr/pppp1ppp/8/4p3/3P4/8/PPP1PPPP/RNBQKBNR w KQkq e6 0 2", "rnbqkbnr/ppp2ppp/3p4/4p3/4PP2/8/PPPP2PP/RNBQKBNR w KQkq - 0 3", "rnbqkbnr/ppp1pppp/8/3p4/3PP3/8/PPP2PPP/RNBQKBNR b KQkq d3 0 2", "rnbqkbnr/ppp1pppp/8/3p4/3P1P2/8/PPP1P1PP/RNBQKBNR b KQkq d3 0 2", "rnbqkbnr/pp1ppppp/8/2p5/2PP4/8/PP2PPPP/RNBQKBNR b KQkq d3 0 2", "r1bqkbnr/pppp1ppp/2n5/4p3/4PP2/2N5/PPPP2PP/R1BQKBNR b KQkq f3 0 3", "r1bqkbnr/pppp1ppp/2n5/4p3/8/2N2N2/PPPPPPPP/R1BQKB1R w KQkq e6 0 3", "rnbqkbnr/pppp2pp/5p2/4p3/3P4/2N5/PPP1PPPP/R1BQKBNR w KQkq - 0 3", "1Nb2rk1/p4pp1/5p1p/2P5/5q2/P6P/2P2PP1/R2QK2R b - - 0 18", "r1bqk2r/pppp1ppp/2n2n2/8/1bBPP3/5N2/PP1B1PPP/RN1QK2R b KQkq - 2 7"
, "rnbqkbnr/ppp3pp/3p1p2/3Pp3/4PP2/8/PPP3PP/RNBQKBNR b KQkq - 0 4", "rn1qkbnr/ppp3pp/8/3Ppb2/8/8/PPP3PP/RNBQKBNR w KQkq - 0 7", "rn1qkbnr/ppp1p1pp/8/3p1b2/3P4/8/PPP2PPP/RNBQKBNR w KQkq - 0 4", "rnbqkbnr/ppp1p1pp/8/5p2/3Pp3/2N5/PPP2PPP/R1BQKBNR w KQkq f6 0 4", "rn1qkbnr/ppp3pp/8/3p1b2/3P1B2/8/PPP3PP/RN1QKBNR w KQkq - 0 7", "rnb1kbnr/pp3ppp/3p4/8/8/8/PPP1PPPP/RNB1KBNR w KQkq - 0 5"]

def get_pieces_on_board(board):
    pieces_on_board = 0
    for square in chess.SQUARES:
        piece = board.piece_at(square)
        if piece is not None:
            pieces_on_board += 1
    return pieces_on_board
def evaluate(board):
    piece_values = {
        chess.PAWN: 100,
        chess.KNIGHT: 300,
        chess.BISHOP: 330,
        chess.ROOK: 500,
        chess.QUEEN: 900,
        chess.KING: 15000
    }
    if get_pieces_on_board(board) < 7:
        position_values = {
        chess.PAWN: [
            100,100,100,100,100,100,100,100,
            50,50,50,50,50,50,50,50,
            20,20,20,20,20,20,20,20,
            10,10,10,10,10,10,10,10,
            -10,-10,-10,-10,-10,-10,-10,-10,
            -20,-20,-20,-20,-20,-20,-20,-20,
            -50,-50,-50,-50,-50,-50,-50,-50,
            -100,-100,-100,-100,-100,-100,-100,-100
        ],
        chess.KNIGHT: [
            -20,-15,-15,-15,-15,-15,-15,-20,
            -15,-10,-10,-10,-10,-10,-10,-15,
            -15,-10,0,0,0,0,-10,-15,
            -15,-10,0,0,0,0,-10,-15,
            -15,-10,0,0,0,0,-10,-15,
            -15,-10,0,0,0,0,-10,-15,
            -15,-10,-10,-10,-10,-10,-10,-15,
            -20,-15,-15,-15,-15,-15,-15,-20,
        ],
        chess.BISHOP: [
            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,
        ],
        chess.ROOK: [
            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,
        ],
        chess.QUEEN: [
            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,
        ],
        chess.KING: [
            -100,-50,0,0,0,0,-50,-100,
            -50,0,0,0,0,0,0,-50,
            0,0,10,10,10,10,0,0,
            0,0,10,25,25,10,0,0,
            0,0,10,25,25,10,0,0,
            0,0,10,10,10,10,0,0,
            -50,0,0,0,0,0,0,0,
            -100,-50,0,0,0,0,-50,-100,
        ],
    }
    else:
        position_values = {
            chess.PAWN: [
                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,50,50,0,0,0,
                0,0,0,50,50,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,
            ],
            chess.KNIGHT: [
                -40,-30,-30,-30,-30,-30,-30,-40,
                -30,-20,-20,-20,-20,-20,-20,-30,
                -30,-20,0,0,0,0,-20,-30,
                -30,-20,0,0,0,0,-20,-30,
                -30,-20,0,0,0,0,-20,-30,
                -30,-20,0,0,0,0,-20,-30,
                -30,-20,-20,-20,-20,-20,-20,-30,
                -40,-30,-30,-130,-30,-30,-30,-40,
            ],
            chess.BISHOP: [
                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,
            ],
            chess.ROOK: [
                0,-50,0,0,0,0,-50,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,-50,0,0,0,0,-50,0,
            ],
            chess.QUEEN: [
                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,
            ],
            chess.KING: [
                0,30,20,0,0,0,30,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,50,30,0,0,0,50,0,
            ],
        }


    score = 0
    for square in chess.SQUARES:
        piece = board.piece_at(square)
        if piece == None:
            continue
        if piece.color == chess.WHITE:
            score += piece_values[piece.piece_type] + position_values[piece.piece_type][square]
        else:
            score -= piece_values[piece.piece_type] + position_values[piece.piece_type][square]
    if board.is_checkmate():
        if board.turn == chess.WHITE:
            score += float("-inf")
        else:
            score += float("inf")
    if board.is_check():
        if board.turn == chess.WHITE:
            score += 50
        else:
            score -= 50
  
    return score
def minimax(alpha, beta, board, depth, maximizing_player, checkext):
    if depth == 0 or board.is_game_over():
        return evaluate(board)
    
    
    if maximizing_player:
        max_eval = float("-inf")
        

        capture_moves = [move for move in board.legal_moves if board.is_capture(move)]
        check_moves = [move for move in board.legal_moves if board.gives_check(move)]
        other_moves = [move for move in board.legal_moves if not board.is_capture(move) and not board.gives_check(move)]
        

        for move in capture_moves:
            board.push(move)
            if board.is_check() and checkext > 0 and depth == 1:
                evaluation = minimax(alpha, beta, board, depth, False, checkext-1)
            else:
                evaluation = minimax(alpha, beta, board, depth-1, False, checkext)
            board.pop()
            max_eval = max(max_eval, evaluation)
            alpha = max(alpha, evaluation)
            if beta <= alpha:
                break
        
        for move in check_moves:
            board.push(move)
            if board.is_check() and checkext > 0 and depth == 1:
                evaluation = minimax(alpha, beta, board, depth, False, checkext-1)
            else:
                evaluation = minimax(alpha, beta, board, depth-1, False, checkext)
            board.pop()
            max_eval = max(max_eval, evaluation)
            alpha = max(alpha, evaluation)
            if beta <= alpha:
                break

        for move in other_moves:
            board.push(move)
            if board.is_check() and checkext > 0 and depth == 1:
                evaluation = minimax(alpha, beta, board, depth, False, checkext-1)
            else:
                evaluation = minimax(alpha, beta, board, depth-1, False, checkext)
            board.pop()
            max_eval = max(max_eval, evaluation)
            alpha = max(alpha, evaluation)
            if beta <= alpha:
                break
        
        return max_eval
    else:
        min_eval = float("inf")
        

        capture_moves = [move for move in board.legal_moves if board.is_capture(move)]
        check_moves = [move for move in board.legal_moves if board.gives_check(move)]
        other_moves = [move for move in board.legal_moves if not board.is_capture(move) and not board.gives_check(move)]
        
        # First, evaluate capture moves
        for move in capture_moves:
            board.push(move)
            if board.is_check() and checkext > 0 and depth == 1:
                evaluation = minimax(alpha, beta, board, depth, True, checkext-1)
            else:
                evaluation = minimax(alpha, beta, board, depth-1, True, checkext)
            board.pop()
            min_eval = min(min_eval, evaluation)
            beta = min(min_eval, beta)
            if beta <= alpha:
                break
        
        for move in check_moves:
            board.push(move)
            if board.is_check() and checkext > 0 and depth == 1:
                evaluation = minimax(alpha, beta, board, depth, True, checkext-1)
            else:
                evaluation = minimax(alpha, beta, board, depth-1, True, checkext)
            board.pop()
            min_eval = min(min_eval, evaluation)
            beta = min(min_eval, beta)
            if beta <= alpha:
                break
        
        for move in other_moves:
            board.push(move)
            if board.is_check() and checkext > 0 and depth == 1:
                evaluation = minimax(alpha, beta, board, depth, True, checkext-1)
            else:
                evaluation = minimax(alpha, beta, board, depth-1, True, checkext)
            board.pop()
            min_eval = min(min_eval, evaluation)
            beta = min(min_eval, beta)
            if beta <= alpha:
                break
        
        return min_eval
Depthflag = True
def stop_depth():
    global Depthflag
    Depthflag = False

maxdepth = 0

def find_best_move(board,depth = 1, best_move=None, best_eval=None, recursive=False, starttime=None):
    alpha = float("-inf")
    beta = float("inf")
    maximizing_player = board.turn
    force_finish = False
    if recursive == True:
        best_move_of_previous_iteration = best_move
        best_eval_of_previous_iteration = best_eval
        best_move = None
        best_eval = None

    # Separate capture moves and other moves
    capture_moves = [move for move in board.legal_moves if board.is_capture(move)]
    check_moves = [move for move in board.legal_moves if board.gives_check(move)]
    other_moves = [move for move in board.legal_moves if not board.is_capture(move) and not board.gives_check(move)]
    if starttime is None:
        starttime = time.time()
    limittime = 3

    if not recursive:
        best_move = None  # Reset best move at the start of a new iteration
        best_eval = None
        depth = 1
    else:
        if depth < 10:
            print(depth)
        

    for move in capture_moves:
        if time.time() - starttime <= limittime:
            board.push(move)
            evaluation = minimax(alpha, beta, board, depth - 1, not maximizing_player, 10)
            board.pop()

            if maximizing_player and (best_eval is None or evaluation > best_eval):
                best_eval = evaluation
                best_move = move
                alpha = evaluation
            elif (not maximizing_player) and (best_eval is None or evaluation < best_eval):
                best_eval = evaluation
                best_move = move
                beta = evaluation

            if beta <= alpha:
                break
        else:
            force_finish = True

    for move in check_moves:
        if time.time() - starttime <= limittime:
            board.push(move)
            evaluation = minimax(alpha, beta, board, depth - 1, not maximizing_player, 10)
            board.pop()

            if maximizing_player and (best_eval is None or evaluation > best_eval):
                best_eval = evaluation
                best_move = move
                alpha = evaluation
            elif (not maximizing_player) and (best_eval is None or evaluation < best_eval):
                best_eval = evaluation
                best_move = move
                beta = evaluation

            if beta <= alpha:
                break
        force_finish = True

    for move in other_moves:
        if time.time() - starttime <= limittime:
            board.push(move)
            evaluation = minimax(alpha, beta, board, depth - 1, not maximizing_player, 10)
            board.pop()

            if maximizing_player and (best_eval is None or evaluation > best_eval):
                best_eval = evaluation
                best_move = move
                alpha = evaluation
            elif (not maximizing_player) and (best_eval is None or evaluation < best_eval):
                best_eval = evaluation
                best_move = move
                beta = evaluation

            if beta <= alpha:
                break
        force_finish = True
    if best_eval == None:
        best_eval = 0
    if recursive:
        if force_finish:
            if best_eval >= best_eval_of_previous_iteration and maximizing_player:
                best_move = best_move
            elif best_eval < best_eval_of_previous_iteration and maximizing_player:
                best_move = best_move_of_previous_iteration
            elif best_eval >= best_eval_of_previous_iteration and not maximizing_player:
                best_move = best_move_of_previous_iteration
            elif best_eval < best_eval_of_previous_iteration and not maximizing_player:
                best_move = best_move
        else:
            best_move = best_move
    if time.time() - starttime <= limittime and depth < 100:
        best_move = find_best_move(board, depth + 1, best_move, best_eval, True, starttime)
    else:
        print(depth)
        
    return best_move

fen_positions = {}
def main(turn, fen):
    global fen_positions
    board = chess.Board(fen)
    depth = 1  
    moves = []
    wTimeUse = 0
    bTimeUse = 0
    game = chess.pgn.Game()  # Create a new Game object for PGN




    if turn == 0:
        while not board.is_game_over():
            current_fen = board.fen()


            if current_fen in fen_positions:
                fen_positions[current_fen] += 1
                if fen_positions[current_fen] > 1:
                    print("Draw due to repetition!")
                    break

            if board.turn == chess.WHITE:
                a = time.time()
                move = find_best_move(board)
                board.push(move)
                print("AI played: ", move)
                b = time.time()
                e = b - a
                print("elapsed time:", round(e, 2))
                moves.append(move)
                print(board)
                print()
                wTimeUse += e
            else:
                a = time.time()
                move = best_move(board, 4)
                board.push(move)
                print("AI played: ", move)
                b = time.time()
                e = b - a
                print("elapsed time:", round(e, 2))
                moves.append(move)
                print(board)
                print()
                bTimeUse += e
    if turn == 1:
        while not board.is_game_over():
            current_fen = board.fen()


            if current_fen in fen_positions:
                fen_positions[current_fen] += 1
                if fen_positions[current_fen] > 1:
                    print("Draw due to repetition!")
                    break

            if board.turn == chess.BLACK:
                a = time.time()
                move = find_best_move(board)
                board.push(move)
                print("AI played: ", move)
                b = time.time()
                e = b - a
                print("elapsed time:", round(e, 2))
                moves.append(move)
                print(board)
                print()
                wTimeUse += e
            else:
               
                a = time.time()
                move = best_move(board, 4)
                board.push(move)
                print("AI played: ", move)
                b = time.time()
                e = b - a
                print("elapsed time:", round(e, 2))
                moves.append(move)
                print(board)
                print()
                bTimeUse += e
   



    
    print("Game over")
    print("Moves in PGN format:")


    print(len(moves))
    print(wTimeUse, bTimeUse)
    if board.is_checkmate():
        print("Checkmate")
    elif board.is_stalemate():
        print("stalemate")
    elif board.is_fifty_moves():
        print("50 moves")
    elif board.is_fivefold_repetition():
        print("Fivefold")
    if board.result() == "1-0" and turn == 0:
        result = 1
    elif board.result == "0-1" and turn == 0:
        result = 0
    elif board.result == "1-0" and turn == 1:
        result = 0
    elif board.result == "0-1" and turn == 1:
        result = 1
    else:
        result = 0.5
    return result
if __name__ == "__main__":

    BotWins = 0
    PilotWins = 0
    draws = 0
    for fen in fens:
        result = main(0, fen)
        if result == 1:
            BotWins += 1
        elif result == 0:
            PilotWins += 1
        else:
            draws += 1
        print("Current bot Wins:", BotWins, "/", len(fens) * 2)
        print("1st bot Wins:", PilotWins, "/", len(fens) * 2)
        print("draws:", draws, "/", len(fens) * 2)
    for fen in fens:
        result = main(1, fen)
        if result == 1:
            BotWins += 1
        elif result == 0:
            PilotWins += 1
        else:
            draws += 1
        print("Current bot Wins:", BotWins, "/", len(fens) * 2)
        print("1st bot Wins:", PilotWins, "/", len(fens) * 2)
        print("draws:", draws, "/", len(fens) * 2)
    print("Current bot Wins:", BotWins, "/", len(fens) * 2)
    print("1st Bot Wins:", PilotWins, "/", len(fens) * 2)
    print("draws:", draws, "/", len(fens) * 2)


I know this code is not very good, but I am 14 and not that good at programming. If anyone knows how to make it uci compatible, please help me out

0

There are 0 best solutions below