First of all lets get the 0-based index of the position, the board is a list which means it has 0-based indexing, so the position should as well.


        def player_turn(board, position, player):
            position -= 1 # 0-based index
        

The board contains a list of rows, and the position should tell us which row we need to edit, lets have a look at which position value corresponds to each row:

Drawing out a table like this helps us see a pattern, in this case we can say that row = position // 3.


        def player_turn(board, position, player):
            position -= 1 # 0-based index
            row_pos = position // 3
        

Next is the column, again lets draw up a table:

Similar to the row table before, theres another pattern here, although it is less noticeable. Note how the numbers in column 0 are all exact multiples of 3, and column 1 positions are all 1 above that, this can be extrapolated to column = position % 3.


        def player_turn(board, position, player):
            position -= 1 # 0-based index
            row_pos = position // 3
            column_pos = position % 3
        

Now that we know the row and column to edit, lets check if there's already an 'X' or 'O' there, the question says a '#' is an 'empty' cell so:


        def player_turn(board, position, player):
            position -= 1 # 0-based index
            row_pos = position // 3
            column_pos = position % 3
            if board[row_pos][column_pos] != "#":
                return print("Invalid move, try again.")
        

Chaining a 'return' and a 'print' statement here is entirely optional, as doing print() and then return is just as valid.
Now that we've done that check, we can update the board, no 'else' statement necessary.


        def player_turn(board, position, player):
            position -= 1 # 0-based index
            row_pos = position // 3
            column_pos = position % 3
            if board[row_pos][column_pos] != "#":
                return print("Invalid move, try again.")
            board[row_pos] = board[row_pos][:column_pos] + player + board[row_pos][column_pos + 1:]
        
Back to 101 Index