0

This is part of my 'connect4' game code in python, where I try to create new nodes with different board setup.

class  node(object):
    def __init__(self, depth,board=None):
        if board is None:
            board = []
        self.board = board
        self.depth = depth

I define instance like that:

start = node(2,new_board)

Where new_board is list[7x6] filled with dots.

Afterwards I try to create child nodes with this function:

def child(depth, parent_board = []):
    children = [node(depth,[]) for x in range(7)]
    for x in range(7):
        y = 5
        while(y >= 0 and (parent_board[x,y] == 'O' or parent_board[x,y] == 'X')):
            y = y-1
        parent_board[x,y] = 'O'
        if (depth >=0):
            children[x] = node(depth-1, parent_board)
            children[x].board = parent_board
        parent_board[x,y] = '.'
    return children

The modification of parent board is correct, but whenever I try to pass it to my children in array, every single children receives the same array.

I understand that lists are mutable in python (that's why I used that 'None' thing in class __init__ function), but nevertheless I cant make every children to have different lists.

Ken Y-N
  • 14,644
  • 21
  • 71
  • 114
K. Moteluk
  • 17
  • 5
  • 2
    Why don't you use `None` in `child`, too? Given that you're aware of the problem, it seems like an odd thing to do. – jonrsharpe Jan 13 '16 at 21:30
  • I think you mean lists are *mutable*. – Alyssa Haroldsen Jan 13 '16 at 21:32
  • Keep in mind `parent_board` in `child` is being modified as it is mutable as well. – Alyssa Haroldsen Jan 13 '16 at 21:33
  • @Kupiakos Yes, indeed, sorry, English isn't my native language and got I memorized wording wrong :D And yeah, i missed that part. So you are saying that copying everytime parent_board and passing different copy to child node will solve problem? – K. Moteluk Jan 13 '16 at 21:39
  • You did the `None` thing right in `__init__`, but you seem to have forgotten it in `child`. – user2357112 Jan 13 '16 at 21:47
  • @user2357112 You mean by creating new child nodes as node(depth, None)? Or i misunderstood you? (Because it doesn't seem to help) – K. Moteluk Jan 13 '16 at 21:53
  • @K.Moteluk: Look at that `parent_board` default argument. Why is that a list? (It doesn't seem to be your only bug, but it's one of them.) – user2357112 Jan 13 '16 at 21:54
  • @user2357112 Because it is supposed to be a list as a representation of my board (7x6 elements with dots). – K. Moteluk Jan 13 '16 at 21:56
  • @K.Moteluk: Do you understand why you set a default `board=None` instead of `board=[]` in `__init__`? – user2357112 Jan 13 '16 at 21:56
  • @user2357112 I've read that every instance of class would share same list if not for making it as "None" in default. I hoped that this would create different lists for every instance? – K. Moteluk Jan 13 '16 at 22:03
  • @K.Moteluk: Do you understand *why* the instances would share their `board` lists? The same logic applies to your `parent_board` argument. – user2357112 Jan 13 '16 at 22:05
  • @user2357112 Oh my gosh i got it now, that's why i am supposed to create COPY of the parent_board every iteration in loop and pass it to instance! It works! I owe you big time, thank you for patience and leading me to answer! – K. Moteluk Jan 13 '16 at 22:11

1 Answers1

0

You can copy your list in node function, something like:

import copy

...

    self.board = copy.copy(board)

Details: https://docs.python.org/2/library/copy.html

Stanislav Ivanov
  • 1,854
  • 1
  • 16
  • 22
  • You mean like making a copy within class __init__ method or making a copy of parent_board when passing it to children? – K. Moteluk Jan 13 '16 at 21:43
  • Given it's a `list`, the `copy` module is overkill; if you don't trust it to be a `list` and want to coerce, do `self.board = list(board)`, if you trust it to be a `list`, just use `self.board = board[:]`, which is the fastest way to shallow copy a sequence (and IMO, most Pythonic, but I'll acknowledge this is a matter where opinion differs). Using `copy.copy` is slower than the "empty" slice, and doesn't give the type checking/coercing benefits of the `list` constructor. – ShadowRanger Jan 13 '16 at 22:45
  • @K.Moteluk: It would be up to you, based on use case. If you trust other callers to pass `list`s that they won't modify, then you'd only _need_ to do the work when passing the `list` to the children. But for safety, it's probably best to shallow copy unconditionally in the constructor, to reduce possible data dependencies (the cost to copy is small, the programmer cost to ensure they don't accidentally mutate a `list` passed to the function after initialization is high). – ShadowRanger Jan 13 '16 at 22:47
  • @K.Moteluk: I think that copy should to be done in node constructor for decomposition reasons. If `parent_board` is exactly a list, making an empty slice is a fastest method, see @ShadowRanger comment for details. Or [this](http://stackoverflow.com/questions/2612802/how-to-clone-or-copy-a-list-in-python). – Stanislav Ivanov Jan 14 '16 at 10:43