1

This is my search function for python chess engine:

def search(pos: position.Position, depth: int, alpha: int, beta: int, side_to_move: chess.Color, root: bool = False):
    global nodes, best_score, best_move, max_score, min_score, killers
    bestMove = None
    """Search the position to a given depth."""
    
    if depth == 0 or pos.board.chess_board().is_game_over():
        nodes += 1
        return evaluate.evaluate(pos, side_to_move), best_move
    
    for move in pos.board.chess_board().legal_moves:
        if stop_search.search_has_stopped():
            return best_score, best_move
        
        if prune(pos, move, alpha, beta, side_to_move, depth):
            continue
        
        pos.board.chess_board().push(move)
        score, _ = search(pos, depth - 1, -beta, -alpha, not side_to_move, root=False)
        score = -score
        pos.board.chess_board().pop()
        
        if score > best_score:
            best_score = score
            best_move = move
        
        if score > max_score:
            max_score = score
        if score < min_score:
            min_score = score
        
        if score > alpha:
            alpha = score
            bestMove = move
            if score >= beta:
                best_move = bestMove
                return beta, bestMove
        else:
            killers.append(move)
        
    return alpha, bestMove

When I run it, the results are fine until depth 4, where I get this strange error:

File "path/to/search.py", line 66, in search
    pos.board.chess_board().pop()
  File "path/to/chess/__init__.py", line 2267, in pop
    move = self.move_stack.pop()
           ^^^^^^^^^^^^^^^^^^^^^
IndexError: pop from empty list

I assume this means that pushing the move to the position was not successful.

I do not know why this occurs, because I am certain that the generated move is legal (it uses the default legal move generator).

Any help on this would be much appreciated.

Boo Who
  • 17
  • 6
  • Hard to help you since we cannot run your code... Maybe give us parameters to call your function ? – Phoenixo Feb 21 '23 at 13:31
  • The function is called in an iterative deepening loop, which calls search() from depth 1 to a certain depth. – Boo Who Feb 21 '23 at 13:35
  • @Phoenixo The entire code is available here: https://github.com/XInTheDark/Strategos/tree/new_search and I'd be very grateful for assistance on this – Boo Who Feb 21 '23 at 13:37
  • I haven't checked in detail but are you sure that position can be modified in-place? – munichmath Feb 21 '23 at 13:47
  • In your github version you have `return` statements before the `pop` happens. In these cases you should (stalemate, ...) still pop to get a consistent state. – trincot Feb 21 '23 at 15:46
  • Log chess board after your push and after your search and see what happens – eligolf Feb 22 '23 at 05:14
  • @trincot Hi, thanks for the response! I tried adding the `pop` even if checkmate/stalemate is returned, but it results in the same error. – Boo Who Feb 22 '23 at 10:29
  • @eligolf Seems that the error occurred when the FEN was `rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1`, i.e. when no move has been made. I'm not sure why this happens, but any ideas how I can bypass this issue? – Boo Who Feb 22 '23 at 10:38
  • @BooWho, you shouldn't be getting to the case where you have made no moves, since you should always make a move with push before undoing it with pop. Are you sure you are sending the right board to the next iteration of minimax? – eligolf Feb 22 '23 at 13:39
  • @eligolf Yes, I think so, in my code I'm always pushing the move, passing the new position to the next iteration of search, and then undoing the move. Of course there is the case of checkmate/stalemate that I did not consider, but even after adding a check for that, the same error occurs. – Boo Who Feb 23 '23 at 12:51
  • @BooWho, well since you get the starting position without any moves made at that point, then you are obviously not always doing that. Why is hard to say, but you should look more into that. – eligolf Feb 23 '23 at 13:00
  • I'll look into it, hopefully I can fix it :) If not I'll have to seek help again – Boo Who Feb 23 '23 at 13:29
  • @eligolf Hi, unfortunately I haven't found the problem yet, it's puzzling me – Boo Who Feb 25 '23 at 07:26
  • Chess engines are hard to debug unfortunately. Start by creating the most basic negamax function you can from the pseudo code found on e.g. Wikipedia or Chessprogramming.org. Also use a very basic eval function, e.g. always send back a 0 or something. Then you add things along the way and see when/if the problem occurs. Then you know what is causing it. – eligolf Feb 25 '23 at 09:44

1 Answers1

1

I've made a chess engine in python before, so I hope I can be helpful here. First of all the error you are getting doesn't mean that pushing the move was unsuccessful, but rather that undoing it was impossible, because no move was recorded: In python chess, the move_stack is a list stored in the Board class which contains the moves that were played. In your case,

IndexError: pop from empty list

This means that the move_stack is empty. For debugging purposes you can access it with board.move_stack and get something like [Move.from_uci('e2e4'), Move.from_uci('e7e5')].


Ok, now that we know this, there is only one way the error can happen: at some point in your code, you run pop() once more than push(). In the code you provide, pop() is always run after a push() call, so the issue doesn't seem to come from here. However, I searched for all the times you use pop() in the github code, and if I am not mistaken in the see_eval() function you run

if e > 0:
    pos.board.chess_board().pop()
    return capturedV - capturingV

without pushing before. This could be the cause of your issue. I hope this helps anyways!

Nonlinear
  • 684
  • 1
  • 12