0

I'm making a chess engine using alpha-beta pruning for a project, and here is my code. I'm getting an error on line 36 and 68,and I'm not sure how or why. Please help me. Thank you for your answers in advance.

import chess

def evaluate() :
    if board.is_checkmate() :
        if board.turn :
            return -9999
        else :
            return 9999
    if board.is_stalemate() :
        return 0
    if board.is_insufficient_material() :
        return 0

    wp = len(board.pieces(chess.PAWN, chess.WHITE))
    bp = len(board.pieces(chess.PAWN, chess.BLACK))
    wn = len(board.pieces(chess.KNIGHT, chess.WHITE))
    bn = len(board.pieces(chess.KNIGHT, chess.BLACK))
    wb = len(board.pieces(chess.BISHOP, chess.WHITE))
    bb = len(board.pieces(chess.BISHOP, chess.BLACK))
    wr = len(board.pieces(chess.ROOK, chess.WHITE))
    br = len(board.pieces(chess.ROOK, chess.BLACK))
    wq = len(board.pieces(chess.QUEEN, chess.WHITE))
    bq = len(board.pieces(chess.QUEEN, chess.BLACK))

    material = 100 * (wp - bp) + 320 * (wn - bn) + 330 * (wb - bb) + 500 * (wr - br) + 900 * (wq - bq)
    return material

def alphabeta(position, depth_, alpha = float('inf'), beta= -float('inf')):
    """Returns [eval, best move] for the position at the given depth"""
    if depth_ == 0 or position.is_game_over():
        return [position.evaluate(), None]
    else:
        if position.turn == chess.WHITE:
            best_move = None
            for _move in [position.legal_moves]:
                new_position = position.push(_move)
                score, move_ = alphabeta(new_position, depth_ - 1, alpha, beta)
                if score > alpha: # white maximizes their score
                    alpha = score
                    best_move = _move
                    if alpha >= beta: # alpha-beta cutoff
                        break
            return [alpha, best_move]
        else:
            best_move = None
            for move_ in position.legal_moves:
                new_position = position.push(move_)
                score, move_ = alphabeta(new_position, depth_ - 1, alpha, beta)
                if score < beta: # black minimizes their score
                    beta = score
                    best_move = move_
                    if alpha >= beta: # alpha-beta cutoff
                        break
            return [alpha, best_move]
fen_ = input('Enter fen: ')
board = chess.Board(fen_)
_depth = int(input('Enter depth: '))
engine = alphabeta(board,_depth)
print(board,engine[0],engine[1])
board.push(engine[0])

Also, there are errors on line 2181 and 3602

Yash
  • 1

1 Answers1

0

So, here is the corrected code (I explain what was wrong just after it) :

import chess

def evaluate(position) :
    if board.is_checkmate() :
        if board.turn :
            return -9999
        else :
            return 9999
    if board.is_stalemate() :
        return 0
    if board.is_insufficient_material() :
        return 0

    wp = len(board.pieces(chess.PAWN, chess.WHITE))
    bp = len(board.pieces(chess.PAWN, chess.BLACK))
    wn = len(board.pieces(chess.KNIGHT, chess.WHITE))
    bn = len(board.pieces(chess.KNIGHT, chess.BLACK))
    wb = len(board.pieces(chess.BISHOP, chess.WHITE))
    bb = len(board.pieces(chess.BISHOP, chess.BLACK))
    wr = len(board.pieces(chess.ROOK, chess.WHITE))
    br = len(board.pieces(chess.ROOK, chess.BLACK))
    wq = len(board.pieces(chess.QUEEN, chess.WHITE))
    bq = len(board.pieces(chess.QUEEN, chess.BLACK))

    material = 100 * (wp - bp) + 320 * (wn - bn) + 330 * (wb - bb) + 500 * (wr - br) + 900 * (wq - bq)
    return material if position.turn else -material

def alphabeta(position, depth_, alpha=-float('inf'), beta=float('inf')):
    """Returns [eval, best move] for the position at the given depth"""

    if depth_ == 0 or position.is_game_over():
        return [evaluate(position), None]

    best_move = None
    for _move in position.legal_moves:
        position.push(_move)
        score, move_ = alphabeta(position, depth_ - 1, -beta, -alpha)
        score = -score
        position.pop()
        if score > alpha: # player maximizes his score
            alpha = score
            best_move = _move
            if alpha >= beta: # alpha-beta cutoff
                break
    return [alpha, best_move]


fen_ = input('Enter fen: ')
board = chess.Board(fen_)
_depth = int(input('Enter depth: '))
engine = alphabeta(board,_depth)
print(board,engine[0],engine[1])
board.push(engine[1])

So, first of all, alpha-beta is never used in chess programming like this. Instead, we prefer the negaMax version of it, and here is the pseudo-code :

int alphaBeta( int alpha, int beta, int depthleft ) {
   if( depthleft == 0 ) return quiesce( alpha, beta );
   for ( all moves)  {
      score = -alphaBeta( -beta, -alpha, depthleft - 1 );
      if( score >= beta )
         return beta;   //  fail hard beta-cutoff
      if( score > alpha )
         alpha = score; // alpha acts like max in MiniMax
   }
   return alpha;
}

Then, in this line of code (new_position = position.push(_move)), you were doing something strange. The move is made on the board, and you never undo the move. And you create a variable like if the chess.Board.push method will return a chess.Board type. This is false. Instead, tou should use something like this :

position.push(_move)
... some code ...
position.pop() # undo the move

And the last bug is here (board.push(engine[0])). This is not making a move on the board, but your alpha-beta evaluation. So the good index is the following : board.push(engine[1]).

This gives a functional code.

BUT, to make tour chess engine better, an evaluation as simple as your is not enough. Material score of the board is not a really good indicator of who is winning. So, I recommend you to implement PSQT and a Quiescent Search.

LittleCoder
  • 391
  • 1
  • 13