0

I'm trying to code a simple chess engine that looks for the move that yields the most material advantage. However, I'm running into an odd error that shows a board that is not the one I had passed into it.

My code:

def best_move(board):
    print(board)
    
    moves = board.legal_moves
    
    result = choice(list(moves))
    for i in moves:
        newboard = board
        newboard.push(i)
        
        oldboard = board
        oldboard.push(result)
        
        if material_count(newboard) > material_count(oldboard):
            result = i

    return result

However, when running this function, I receive this error:

AssertionError: push() expects move to be pseudo-legal, but got g8h6 in rnbqkb1r/ppppnppp/8/8/3PP3/8/PPP2PPP/RNBQKBNR

The board in the error message looks like this:

r n b q k b . r
p p p p n p p p
. . . . . . . .
. . . . . . . .
. . . P P . . .
. . . . . . . .
P P P . . P P P
R N B Q K B N R

As you can see, my e-file pawn has disappeared entirely, and my knight has taken its fallen comrade's place. However, this is not the board that I passed into my method, as shown below:

r n b q k b n r
p p p p . p p p
. . . . . . . .
. . . . p . . .
. . . P P . . .
. . . . . . . .
P P P . . P P P
R N B Q K B N R

Any ideas? I can't see why the board is being altered in this way.

  • 1
    `newboard = board` doesn't create a copy of `broad`. Any changes you make to `newboard` will affect both `board` and `oldboard`. If you instead create a deep copy of `board`, does that fix it? – Random Davis Feb 24 '21 at 23:15
  • @RandomDavis Thank you very much! Can you post this as an answer so that I may accept it? Also, this is the way I had made a deep-copy: `newboard = chess.Board(board.fen())`. Is this the proper way to do it? – aidenwilson Feb 25 '21 at 01:11
  • 1
    I don't know how `Board`'s constructor is implemented, so I can't say for sure that that'd work, but if it is actually working, then I assume that's correct. – Random Davis Feb 25 '21 at 16:58
  • @RandomDavis awesome! by the way, would you like to post an answer to this thread for me to accept? – aidenwilson Feb 25 '21 at 23:02
  • Okay I posted an answer, I missed that you asked that before. – Random Davis Feb 25 '21 at 23:04

3 Answers3

0

You need to use deep copy to create a copy of the board, otherwise you are using the same board as before.

from copy import deepcopy

newboard = deepcopy(board)
eligolf
  • 1,682
  • 1
  • 6
  • 22
0

newboard = board doesn't create a copy of board. Any changes you make to newboard will affect both board and oldboard. The solution is to create a deep copy of board instead.

According to the asker this is done via:

newboard = chess.Board(board.fen())
Random Davis
  • 6,662
  • 4
  • 14
  • 24
  • @eligolf My question was not, "How do you create a deepcopy". It was, "why wasn't the code shown below working, and how can I fix it?" Your answer also required importing another library, which I did not prefer. – aidenwilson Feb 26 '21 at 13:14
0

As other commenters have pointed out, you are making a shallow copy of board.

However, Board has a built-in copy method that is faster than the alternatives:

newboard = board.copy()
>>> board = chess.Board()
>>> %timeit new_board = chess.Board(board.fen())
275 µs ± 3.56 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit import copy; new_board = copy.deepcopy(board)
24.8 µs ± 542 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit new_board = board.copy()
16 µs ± 51.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

From the docs, the method:

Creates a copy of the board.

Defaults to copying the entire move stack. Alternatively, stack can be False, or an integer to copy a limited number of moves.

so making use of the stack parameter could also be helpful in some scenarios.

Jackson H
  • 171
  • 10