Search code examples
pythontypeerrorsliceindices

"TypeError: list indices must be integers or slices, not NoneType" in tictactoe game (Python 3)


I've been developing a basic tictactoe game in Python 3 where a player competes against an AI to win. On the first turn, the code is executed successfully with no errors, but after that I get the error previously described.

I've tried changing "board[move] = computer" to "move = computer", but that simply skips the computer's turn, as nothing is being filled out.

I expected the output of a tictactoe board with a new square filled in with an "O". But I get the error "board[move] = computer TypeError: list indices must be integers or slices, not NoneType"

Here's the code:

X = "X"
O = "O"
EMPTY = " "
TIE = "TIE"
NUM_SQUARES = 9

def pieces():
    go_first = ask_yes_no("Do you require the first move? (y/n): ")
    if go_first == "y":
        print("\nThen take the first move. You will need it.")
        human = X
        computer = O
    else:
        print("\nYour bravery will be your undoing... I'll go first")
        computer = X
        human = O
    return computer, human

def new_board():
    board = []
    for square in range(NUM_SQUARES):
        board.append(EMPTY)
    return board

def legal_moves(board):
    moves = []
    for square in range(NUM_SQUARES):
        if board[square] == EMPTY:
            moves.append(square)
        return moves

def winner(board):
    WAYS_TO_WIN = ((0, 1, 2),
                  (3, 4, 5),
                  (6, 7, 8),
                  (0, 3, 6),
                  (1, 4, 7),
                  (2, 5, 8),
                  (0, 4, 8),
                  (2, 4, 6))

    for row in WAYS_TO_WIN:
        if board[row[0]] == board[row[1]] == board[row[2]] != EMPTY:
            winner = board[row[0]]
            return winner

    if EMPTY not in board:
        return TIE

    return None


def human_move(board, human):
    legal = legal_moves(board)
    move = None
    while move not in legal:
        move = ask_number("Where will you move? (0 - 8):", 0, 
                           NUM_SQUARES)
        if move not in legal:
            print("\nThat square is already occupied. Choose another.\n")
        print("Fine...")
        return move


def computer_move(board, computer, human):
    board = board[:]

    BEST_MOVES = (4, 0, 2, 6, 8, 1, 3, 5, 7)

    print("I shall take square number", end=" ")

    for move in legal_moves(board):
        board[move] = computer
        if winner(board) == computer:
            print(move)
            return move
        board[move] = EMPTY

    for move in legal_moves(board):
        board[move] = human
        if winner(board) == human:
            print(move)
            return move
        board[move] = EMPTY

    for move in BEST_MOVES:
        if move in legal_moves(board):
            print(move)
            return move

def next_turn(turn):
    if turn == X:
        return O
    else:
        return X


def main():
    computer, human = pieces()
    turn = X
    board = new_board()
    display_board(board)

    while not winner(board):
        if turn == human:
            move = human_move(board, human)
            board[move] = human
        else:
            move = computer_move(board, computer, human)
            board[move] = computer

        display_board(board)
        turn = next_turn(turn)

    the_winner = winner(board)

main()

Solution

  • Your legal_moves function is broken, because it returns just after the first 'sqare' no matter what. If it was alredy taken, it returns an empty list.

    Just move the return an intendation down:

    def legal_moves(board):
        moves = []
        for square in range(NUM_SQUARES):
            if board[square] == EMPTY:
                moves.append(square)
        return moves