python-chess icon indicating copy to clipboard operation
python-chess copied to clipboard

list(board.pieces(chess.KING, chess.BLACK))[0] IndexError: list index out of range

Open MattisIsEpic opened this issue 7 months ago • 0 comments

Hello. I'm trying to make a chess engine using the chess library, however during testing this error occurred:

Traceback (most recent call last):
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 163, in <module>
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 159, in <module>
    move = search.minimax(board, 20, -1, -1, True, chess.BLACK)[1]
           ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 92, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, False, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 111, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, True, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 92, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, False, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 111, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, True, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 92, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, False, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 111, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, True, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 92, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, False, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 111, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, True, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 92, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, False, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 111, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, True, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 92, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, False, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 111, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, True, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 92, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, False, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 111, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, True, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 92, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, False, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 111, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, True, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 92, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, False, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 111, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, True, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 92, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, False, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 124, in minimax
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 111, in minimax
    evaluation = self.minimax(new_board, depth-1, alpha, beta, True, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 79, in minimax
    return self.Quiescence(board, side, 4, -1, 1)
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 47, in Quiescence
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 37, in Quiescence
    current = self.Quiescence(new_board, side, depth-1, alpha, beta)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 47, in Quiescence
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 37, in Quiescence
    current = self.Quiescence(new_board, side, depth-1, alpha, beta)
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 47, in Quiescence
    raise e
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 32, in Quiescence
    attack_outcome = {list(board.legal_moves)[0]: Evaluate(board, side)}
                                                  ~~~~~~~~^^^^^^^^^^^^^
  File "c:\Users\Admin\Desktop\MyFish 1.0\search.py", line 6, in Evaluate
    friendly = EvaluateSide(board, side)
  File "c:\Users\Admin\Desktop\MyFish 1.0\eval.py", line 93, in EvaluateSide
    king_location = list(board.pieces(chess.KING, side))[0]
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^
IndexError: list index out of range

so then i used a try and except statement to findout what color the "side" argument is, and it is black like it should be.

Here's the search.py file:

from eval import EvaluateSide
import random
import chess

def Evaluate(board: chess.Board, side: chess.WHITE | chess.BLACK):
    friendly = EvaluateSide(board, side)
    enemy = EvaluateSide(board, not side)
    return ((friendly/(friendly+enemy))*2)-1


class Search:
    def __init__(self):
        self.pv_table = {}
        self.nodes = 0
    
    def Quiescence(self, board: chess.Board, side, depth, alpha = -1, beta = 1):
        if depth == 0:
            return Evaluate(board, side)
        try:
            attacks = [list(board.legal_moves)[0]]
            for move in list(board.legal_moves):
                if move.to_square in list(board.attacks(move.from_square)):
                    attacks.append(move)

            if len(attacks) == 0:
                return Evaluate(board, side)
            attack_outcome = {list(board.legal_moves)[0]: Evaluate(board, side)}
            for attack in attacks:
                self.nodes += 1
                new_board = board.copy()
                new_board.push(attack)
                current = self.Quiescence(new_board, side, depth-1, alpha, beta)
                attack_outcome[attack] = current
                if (board.turn == side):
                    alpha = max(current, alpha)
                else:
                    beta = min(current, beta)
            attacks.sort(key=lambda move: attack_outcome[move], reverse=(board.turn == side))
            return attack_outcome[attacks[0]]
        except Exception as e:
            if not board.is_game_over():
                raise e
            return Evaluate(board, side)
        
    def MoveOrdering(self, board: chess.Board):
        move_ranking = {}
        moves = list(board.legal_moves)
        key = (int(board.occupied)*2)+int(not board.turn)
        
        hit = None
        if key in self.pv_table and self.pv_table[key] in moves:
            try:
                hit = self.pv_table[key]
                moves.remove(moves.index(hit))
            except:
                hit = None

        current_board = board.copy()
        for move in moves:
            board = current_board.copy()
            board.push(move)
            score = Evaluate(board, board.turn)
            move_ranking[move] = score

        moves.sort(key=lambda move: move_ranking[move], reverse=False)

        if hit:
            moves.insert(0, hit)

        return moves
    
    def minimax(self, board: chess.Board, depth, alpha = -1, beta = 1, maximizingPlayer = False, side = chess.WHITE):
        if depth == 0:
            return self.Quiescence(board, side, 4, -1, 1)
        
        moves = self.MoveOrdering(board)
        
        try:
            if maximizingPlayer:
                key = (int(board.occupied)*2)+int(not board.turn)
                maxMove = moves[0]
                maxEval = -1
                for move in moves:
                    self.nodes += 1
                    new_board = board.copy()
                    new_board.push(move)
                    evaluation = self.minimax(new_board, depth-1, alpha, beta, False, side)
                    if type(evaluation) != float:
                        evaluation = evaluation[0]
                    if evaluation > maxEval:
                        maxEval = evaluation
                        maxMove = move
                    alpha = max(alpha, evaluation)
                    if beta <= alpha:
                        break
                self.pv_table[key] = maxMove
                return [maxEval, maxMove]
            else:
                key = (int(board.occupied)*2)+int(not board.turn)
                minMove = moves[0]
                minEval = 1
                for move in moves:
                    self.nodes += 1
                    new_board = board.copy()
                    new_board.push(move)
                    evaluation = self.minimax(new_board, depth-1, alpha, beta, True, side)
                    if type(evaluation) != float:
                        evaluation = evaluation[0]
                    if evaluation < minEval:
                        minEval = evaluation
                        minMove = move
                    beta = min(beta, evaluation)
                    if beta <= alpha:
                        break
                self.pv_table[key] = minMove
                return [minEval, minMove]
        except Exception as e:
            if not board.is_game_over():
                raise e
            return self.Quiescence(board, side, 4, -1, 1)

def Visualize(board: chess.Board):
    black = ["KQRBNP", "♔♕♖♗♘♙"]
    white = ["KQRBNP", "♚♛♜♝♞♟"]

    lines = []
    for i in range(8):
        lines.append([".", ".", ".", ".", ".", ".", ".", "."])#([".", ".", ".", ".", ".", ".", ".", "."])
    
    pieces = []
    for piece in [chess.PAWN, chess.KNIGHT, chess.BISHOP, chess.ROOK, chess.QUEEN, chess.KING]:
        pieces = pieces + list(board.pieces(piece, chess.BLACK))
        pieces = pieces + list(board.pieces(piece, chess.WHITE))
    
    for piece in pieces:
        if board.color_at(piece) == chess.BLACK:
            lines[int(chess.square_name(piece)[1])-1]["abcdefgh".index(chess.square_name(piece)[0])] = black[1][black[0].index(board.piece_at(piece).symbol().upper())]
        if board.color_at(piece) == chess.WHITE:
            lines[int(chess.square_name(piece)[1])-1]["abcdefgh".index(chess.square_name(piece)[0])] = white[1][white[0].index(board.piece_at(piece).symbol().upper())]
    string = ""
    for i in range(8):
        string = string+str(9-(i+1))+". "+(" ".join(lines[7-i]))+"\n"
    string = string + "   " + (" ".join(list("ABCDEFGH")))
    return string

board = chess.Board()
search = Search()

while True:
    try:
        print(Visualize(board))
        board.push_uci(input())
        print(Visualize(board))
        move = search.minimax(board, 20, -1, -1, True, chess.BLACK)[1]
        board.push(move)
    except Exception as e:
        print(board.fen())
        raise e

and here's the eval.py file (with one unprofessional comment removed):

import chess
import math

MG_PIECE_VALUES = {
    chess.PAWN: 82,
    chess.KNIGHT: 337,
    chess.BISHOP: 365,
    chess.ROOK: 477,
    chess.QUEEN: 1025,
    chess.KING: 24000,
}

EG_PIECE_VALUES = {
    chess.PAWN: 94,
    chess.KNIGHT: 281,
    chess.BISHOP: 297,
    chess.ROOK: 512,
    chess.QUEEN: 936,
    chess.KING: 24000,
}

Attack_Table = {
    1: 0,
    2: 50,
    3: 75,
    4: 88,
    5: 94,
    6: 97,
    7: 99,
}

def PieceEvaluation(board: chess.Board, position: int, type: any, side: chess.WHITE | chess.BLACK):
    if type == chess.PAWN:
        if side == chess.WHITE:
            if position == 0:
                position = 1
            position = math.ceil(position/8)
        else:
            position = 63-position
            if position == 0:
                position = 1
            position = math.ceil(position/8)
        return 1+(position/32)
    return 1

def EvaluateSide(board: chess.Board, side: chess.WHITE | chess.BLACK):
    # Mate-at-a-Glance detection

    board.turn = not side
    for move in list(board.legal_moves):
        board.push(move)
        if board.is_checkmate():
            return 0
        board.pop()

    # Compute Safe-Entropy / Mobility

    attacked = {}
    for attacker in chess.SquareSet(board.occupied_co[not side]):
        for attack in board.attacks(attacker):
            attacked[attack] = 0

    board.turn = side
    moves = 0
    Safe = {}
    for move in list(board.legal_moves):
        if move.from_square in Safe or board.attackers(not side, move.from_square) == chess.SquareSet():
            Safe[move.from_square] = 1
            moves += 1

    controlled = {}
    for attacker in chess.SquareSet(board.occupied_co[side]):
        if attacker in Safe:
            for attack in board.attacks(attacker):
                if not attack in attacked:
                    controlled[attack] = 1

    entropy = 140*math.log((len(list(controlled.keys()))*moves)+1)

    # Compute Material (+ entropy consideration)

    stage = (len(list(chess.SquareSet(board.occupied_co[side])))+len(list(chess.SquareSet(board.occupied_co[not side]))))/32
    material = 0
    for piece in chess.SquareSet(board.occupied_co[side]):
        piece_type = board.piece_type_at(piece)
        MG_value = MG_PIECE_VALUES[piece_type]
        EG_value = EG_PIECE_VALUES[piece_type]
        material += ((MG_value * stage) + (EG_value * (1 - stage)))*PieceEvaluation(board, piece, board.piece_type_at(piece), board.color_at(piece))

    # Compute king-safety

    attackers = 0
    king_location = list(board.pieces(chess.KING, side))[0]
    for offset in [-9, -8, -7, -1, 1, 7, 8, 9]:
        try:
            square = king_location - offset
            if chess.square_distance(king_location, square) == 1:
                attackers += len(list(board.attackers(not side, square)))
        except:
            pass

    attackers = max(1, attackers)
    attackers = min(7, attackers)

    king_safety = 1-(Attack_Table[attackers]/100)

    # Compute Final Score

    score = (material+entropy)*king_safety

    return score

this error occured on python 3.13.3 with the latest "chess" library update.

MattisIsEpic avatar May 21 '25 12:05 MattisIsEpic