2 player CLI Tic-Tac-Toe game in python This is a fully functional code that i have just completed today. I just want to know how efficient is this. and how could i improve it or if theres any bugs that i have miss out.
import os
from AskUser import ask_user as As
from random import randint

#initialising the board
class Board():
    def __init__(self):
        self.boxs = [' '] * 10
    
    def display(self):
        print(f' {self.boxs[1]}  |  {self.boxs[2]}  |  {self.boxs[3]} \n---------------')
        print(f' {self.boxs[4]}  |  {self.boxs[5]}  |  {self.boxs[6]} \n---------------')
        print(f' {self.boxs[7]}  |  {self.boxs[8]}  |  {self.boxs[9]} ')

    def update_box(self, box_num, key):
        def valid_position(box_num, key):
            return 0 &lt; box_num &lt; 10 and self.boxs[box_num] == ' '
        while not valid_position(box_num, key):
            box_num = As(&quot;Please enter a valid position: &quot;, int)
        self.boxs[box_num] = key
   
    def win_solution(self,key):
        if (self.boxs[1] == key and self.boxs[2] == key and self.boxs[3] == key) or\
            (self.boxs[4] == key and self.boxs[5] == key and self.boxs[6] == key) or\
            (self.boxs[7] == key and self.boxs[8] == key and self.boxs[9] == key) or\
            (self.boxs[1] == key and self.boxs[4] == key and self.boxs[7] == key) or\
            (self.boxs[2] == key and self.boxs[5] == key and self.boxs[8] == key) or\
            (self.boxs[3] == key and self.boxs[6] == key and self.boxs[9] == key) or\
            (self.boxs[1] == key and self.boxs[5] == key and self.boxs[9] == key) or\
            (self.boxs[3] == key and self.boxs[5] == key and self.boxs[7] == key):
            return True
        return False

    def tie_game(self):
        count = 0
        for i in self.boxs:
            if i != ' ':
                count += 1
        return count == 9   

    def reset_board(self):
        self.boxs = [' '] * 10

board = Board()
Turn = randint(0,1)

#Introduction 
def clear_screen():
    os.system('clear')
    print('Welcome to the TicTacToe simulator')
    #Displays the board
    board.display()


while True:
    if Turn == 1:
        #Updates the board after input
        clear_screen()

        #For X Player
        x_move = As('X, Choose a position between 1 - 9 : ',int)
        #Put x input into the board
        board.update_box(x_move, &quot;X&quot;)
        Turn = 0
        #Updates the board after input
        clear_screen()
    #Check for win for X or tie
        if board.win_solution(&quot;X&quot;):
            print('Congrats, X you have won')
            Turn = 3
        elif board.tie_game():
            print('This is a tie game')
            Turn = 3
        else:
            pass

    elif Turn == 0: 
        clear_screen()
        #For O Player
        o_move = As('O, Choose a position between 1 - 9 : ',int)
        #Put o input into the board
        board.update_box(o_move, &quot;O&quot;)
        Turn = 1
        #Updates the board after input
        clear_screen()

        #Check for win for O or tie
        if board.win_solution(&quot;O&quot;):
            print('Congrats, O you have won')
            Turn = 3
        elif board.tie_game():
            print('This is a tie game')
            Turn = 3
        else:
            pass

    else:
        p_again = As('Do you want to play again? (Y/N) : ').upper()
        if p_again == &quot;Y&quot;:
            board.reset_board()
            Turn = randint(0,1)
            continue
        else:
            exit('Game is over')

This is the AskUser function
Function to check for the user input
def ask_user(message, type_= str, valid=lambda x: True, invalid_message=&quot;Invalid&quot;):
    while True:
        try:
            user_input = type_(input(message))
        except (ValueError, TypeError):
            print(&quot;Invalid input&quot;)
            continue
        if valid(user_input):
            return user_input
        else:
            print(invalid_message)
Matrix indexing

The display function is a handful. It's often better to use a matrix library like numpy to allow better indexing features. For instance,

import numpy as np
one_thru_nine = list(range(1,10))
print(one_thru_nine)
matrix = np.array(one_thru_nine).reshape(3,3)
print(matrix)
print(matrix[0, 1])
print(matrix[1, 1])
print(matrix[2, 2])


[1, 2, 3, 4, 5, 6, 7, 8, 9]
[[1 2 3]
 [4 5 6]
 [7 8 9]]
2
5
9


(Disclaimer: np.array(one_thru_nine).reshape(3,3) is not really an ideal way of initializing numpy arrays, it's just for demonstration purposes.)

Of course, do beware of zero-indexing. 

Magic numbers

As a rule of thumb, avoid magic numbers. Try not to put raw integers into code. 

return 0 &lt; box_num &lt; 10


will be more readable as 

min_boxes, max_boxes = 0, 10
return min_boxes &lt; box_num &lt; max_boxes


Similarly, I can understand what Turn=3 does, but it's unintuitive.

If-Else statements

else:
  pass


is redundant. If you don't have anything to do when if and elif conditions fail, you don't have to add the else statement altogether -- unlike some other languages.

That is, you can just do: 

while True:
  if condition_one:
    do_something()
  elif condition_two:
    do_something_else()


Class declaration

You don't need to put parentheses next to class declarations if you are not inheriting from a super class. 

class Board():
   def __init__(self):


can just be

class Board:
   def __init__(self):


Casing

As a general rule of thumb, in classes are in PascalCase and everything else (modules, variables, functions, methods) are in snake_case. As such, from AskUser import ask_user as As should be reformatted as from ask_user import ask_user as as_as or something similar.

Similarly, things like Turn should be turn. 

Wrap your module with main

In general, if you are not declaring globals, and you declare functions that you will actually call as main (perhaps you're familiar with C or Java?) you should wrap them in a statement of

if __name__ == "__main__":
  board = Board()
  turn = randint(0,1) ... 


If you don't get into the habit of this, you'll end up executing code you don't want to when you're just importing code from other modules.

Others


I don't see the use of valid and invalid_message anywhere?