1

I'm attempting to build a chess engine from scratch in Python. Part of this involves storing the current position in a 2D array and updating the position after a move. I have discovered a frustratingly bizarre behaviour when writing the "move" code:

Initial position is this array, where '00' indicates an empty square:

position

[['WR', 'WN', 'WB', 'WQ', 'WK', 'WB', 'WN', 'WR'],
 ['Wp', 'Wp', 'Wp', 'Wp', 'Wp', 'Wp', 'Wp', 'Wp'],
 ['00', '00', '00', '00', '00', '00', '00', '00'],
 ['00', '00', '00', '00', '00', '00', '00', '00'],
 ['00', '00', '00', '00', '00', '00', '00', '00'],
 ['00', '00', '00', '00', '00', '00', '00', '00'],
 ['Bp', 'Bp', 'Bp', 'Bp', 'Bp', 'Bp', 'Bp', 'Bp'],
 ['BR', 'BN', 'BB', 'BQ', 'BK', 'BB', 'BN', 'BR']]

I try to move a white pawn from [1,2] to [2,2]:

position[1][2]='00'

position[2][2]='Wp'

But now the output has additionally written a white pawn to [3,2], [4,2] and [5,2]!

position

[['WR', 'WN', 'WB', 'WQ', 'WK', 'WB', 'WN', 'WR'],
 ['Wp', 'Wp', '00', 'Wp', 'Wp', 'Wp', 'Wp', 'Wp'],
 ['00', '00', 'Wp', '00', '00', '00', '00', '00'],
 ['00', '00', 'Wp', '00', '00', '00', '00', '00'],
 ['00', '00', 'Wp', '00', '00', '00', '00', '00'],
 ['00', '00', 'Wp', '00', '00', '00', '00', '00'],
 ['Bp', 'Bp', 'Bp', 'Bp', 'Bp', 'Bp', 'Bp', 'Bp'],
 ['BR', 'BN', 'BB', 'BQ', 'BK', 'BB', 'BN', 'BR']]

I assume this is because Python assumes all of the empty rows are the same object, so updates all of them together. How do I get around this, and only update the row that I specify?

EDIT in reply to requests for the position definition:

# Set up starting position
position=[['00']*8]*8
position[0]=['WR','WN','WB','WQ','WK','WB','WN','WR']
position[1]=['Wp']*8
position[6]=['Bp']*8
position[7]=['BR','BN','BB','BQ','BK','BB','BN','BR']
ben
  • 11
  • 2
  • 4
    Python does not "assume" these kinds of things. But it is possible the same "row" object ended up in several places of your array. Please show the actual code for creating the array. – Lev M. Jan 01 '22 at 16:53
  • 1
    you are likely creating all the empy rows (rows 3 to 6 on the chessboard) as a reference to the same list. But lists are mutables. See https://stackoverflow.com/questions/8056130/immutable-vs-mutable-types – Valentino Jan 01 '22 at 16:55
  • 1
    Does this answer your question? [List changes unexpectedly after assignment. Why is this and how can I prevent it?](https://stackoverflow.com/questions/2612802/list-changes-unexpectedly-after-assignment-why-is-this-and-how-can-i-prevent-it) – Craig Jan 01 '22 at 17:11

1 Answers1

0

When you use:

position=[['00']*8]*8

You have simply created 8 copies of the same row. Any change to one changes all of them. This doesn't apply to the first and last two rows because you have explicitly made those different objects.

See below for an example.

>>> a = [['0'] * 8] * 8
>>> a
[['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0']]
>>> a[0][0] = 5
>>> a
[[5, '0', '0', '0', '0', '0', '0', '0'], [5, '0', '0', '0', '0', '0', '0', '0'], [5, '0', '0', '0', '0', '0', '0', '0'], [5, '0', '0', '0', '0', '0', '0', '0'], [5, '0', '0', '0', '0', '0', '0', '0'], [5, '0', '0', '0', '0', '0', '0', '0'], [5, '0', '0', '0', '0', '0', '0', '0'], [5, '0', '0', '0', '0', '0', '0', '0']]
>>>

You might use a list comprehension to get around this:

>>> b = [['0'] * 8 for _ in range(8)]
>>> b
[['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0']]
>>> b[0][0] = '1'
>>> b
[['1', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0']]
>>>

The list comprehension generates a new list object on each iteration.

Chris
  • 26,361
  • 5
  • 21
  • 42
  • Perfect, thank you for taking the time to explain this – ben Jan 01 '22 at 17:19
  • You're welcome. From a newcomer's perspective, it's often an odd behavior. On SO, you can say "thank you" by upvoting/accepting answers. – Chris Jan 01 '22 at 17:22
  • It seems I need more reputation points before upvoting your answer. I'll come back and do so once I'm at 15. – ben Jan 01 '22 at 17:58
  • You should still be able to accept the answer, if you so desire. – Chris Jan 01 '22 at 19:52