1
class Board():
    """Board class for Tic Tac Toe game. Board can be 3x3, 4x4, or 5x5."""
    def __init__(self, size):
        # offset by 1 to make indexing easier
        self.board = [None]
        self.board_size = size
    def create_board(self):
        """Create an empty board with dimensions of board_size argument."""
        row = [None] + ([' '] * self.board_size)
        for i in range(self.board_size):
            self.board.append(row)
    def draw_mark(self, choice, player_mark):
        """Draw an x or o on the space chosen by the player if the space is
           unoccupied.
        
           Choice is a list containing the coordinates of the space to place a
           mark [row, column].
           
           Return True if move was valid, return False if not.
        """
        row = choice[0]
        column = choice[1]
        if self.board[row][column] == ' ':
            self.board[row][column] = player_mark
            return True
        else:
            return False
    
oBoard = Board(3)
oBoard.create_board()
oBoard.draw_mark([1, 1], 'x')
print(oBoard.board)

When I instantiate an board object and call the draw_mark method on that object I expect this as the output:

[None, [None, 'x', ' ', ' '], [None, ' ', ' ', ' '], [None, ' ', ' ', ' ']]

However the code produces this output:

[None, ['x', ' ', ' ', ' '], ['x', ' ', ' ', ' '], ['x', ' ', ' ', ' ']]

I tried creating the grid as a global variable in the python shell and attempting the assignment directly and that produced the desired result.

My question is what is it about my implementation of classes that is causing these extra assignments to happen?

wjandrea
  • 28,235
  • 9
  • 60
  • 81
Omace
  • 42
  • 3
  • This is an intended mechanic in python. Each list you see that has an "x" is the same array. So better said, its not the class, rather the way a list is coded in python. – Shmack Jun 18 '23 at 21:38
  • Open python and type this in: `k = [[1,2,3,4,5]] * 5; print(k); k[0][0] = 4; print(k);` – Shmack Jun 18 '23 at 21:43
  • `row` is the same object each loop. See [How do I clone a list so that it doesn't change unexpectedly after assignment?](/q/2612802/4518341) – wjandrea Jun 18 '23 at 21:47
  • 1
    No need to waste space by storing `None` in element 0 of a list just to have a 1-indexed board. Just subtract one from the given row and column number before indexing `board`. – chepner Jun 18 '23 at 21:48

1 Answers1

0

You create a single row and then build the table by appending that same single row many times. A change to one row is seen by all rows because they are the same underlying list. You should create a new list for each row in the table. The change is in the create_board method.

class Board():
    """Board class for Tic Tac Toe game. Board can be 3x3, 4x4, or 5x5."""
    def __init__(self, size):
        # offset by 1 to make indexing easier
        self.board = [None]
        self.board_size = size
    def create_board(self):
        """Create an empty board with dimensions of board_size argument."""
        for i in range(self.board_size):
            self.board.append([None] + ([' '] * self.board_size))
    def draw_mark(self, choice, player_mark):
        """Draw an x or o on the space chosen by the player if the space is
           unoccupied.
        
           Choice is a list containing the coordinates of the space to place a
           mark [row, column].
           
           Return True if move was valid, return False if not.
        """
        row = choice[0]
        column = choice[1]
        if self.board[row][column] == ' ':
            self.board[row][column] = player_mark
            return True
        else:
            return False
    
oBoard = Board(3)
oBoard.create_board()
oBoard.draw_mark([1, 1], 'x')
print(oBoard.board)

Output

[None, [None, 'x', ' ', ' '], [None, ' ', ' ', ' '], [None, ' ', ' ', ' ']]
tdelaney
  • 73,364
  • 6
  • 83
  • 116