CLI Tic-Tac-Toe in python

Is there any way I could make my Tic-Tac-Toe board more aesthetically pleasing?

Is there anything else I can improve besides the look of the board?

from AskUser import ask_user as As
from random import randint
Choice = 0
Player1 = None
Player2 = None

#Introduction
def intro():
global Choice
while True:
Choice = As("Press 1 for Multiplayer\nPress 2 for Single player: ", int)
if Choice != (1 or 2):
print("Invalid Input")
continue
else:
break
return Choice

#Players
def choosenplayer():
# Defines whos going first
if Choice == 1:
def whofirst():
global q
q = randint(1, 2)
if q == 1:
print(f"Player 1 ({Player1}) is going first")
else:
print(f"Player 2 ({Player2}) is going first")
# Defines the player
while True:
global Player2
global Player1
Player = As("Press 1 if you want to be 'X'\nPress 2 if you want to be 'O': ", int)
if Player == 1:
Player1 = "X"
Player2 = "O"
break
elif Player == 2:
Player1 = "O"
Player2 = "X"
break
else:
print("Invalid Input")
continue
print(f"Player 1 is {Player1}, Player 2 is {Player2}")
whofirst()
return Player1 and Player2

board = list (range(0,9))
#Initialise The Tic Tac Toe Board
def t_Board():
print(f"| {board[0]} | {board[1]} | {board[2]} |\n_____________")
print(f"| {board[3]} | {board[4]} | {board[5]} |\n_____________")
print(f"| {board[6]} | {board[7]} | {board[8]} |")

#Stops the game if theres a win
def stops_board():
if (board[0] == board[1] == board[2]) or (board[3] == board[4] == board[5]) or (
board[6] == board[7] == board[8]) or (board[0] == board[3] == board[6]) or (
board[1] == board[4] == board[7]) or (board[2] == board[5] == board[8]) or (
board[0] == board[4] == board[8]) or (board[2] == board[4] == board[6]):
return True

x_data = []
def xuser_input():
def over_ride():
global x
while True:
x = As("Player X, Please enter a number to place: ",int)
if board[x] == "X" or board[x] == "O":
print("You can't place here, it is already used")
continue
else:
break
while True:
over_ride()
if (x > 10) or (x < 0):
print("Input must be Bigger than 0 and Smaller than 9")
continue
try:
board[x] = "X"
x_data.append(x)
except (IndexError):
print("Invalid input")
continue
t_Board()
break
O_data = []
def yuser_input():
def over_rideo():
global O
while True:
O = As("Player O, Please enter a number to place: ",int)
if board[O] == "X" or board[O] == "O":
print("You can't place here, it is already used")
continue
else:
break
while True:
over_rideo()
if (O > 10) or (O < 0):
print("Input must be Bigger than 0 and Smaller than 9")
continue
try:
board[O] = "O"
O_data.append(O)
except (IndexError):
print("Invalid input")
continue
t_Board()
break

print("Welcome to TicTacToe simulator")
intro()
choosenplayer()
t_Board()
if ((q == 1) and (Player1 == "X")) or ((q == 2) and (Player2 =="X")):
for i in range(5):
xuser_input()
if stops_board():
print(f"Congrats X, You are the winner")
break
if i == 4:
print("This is a tie")
break
yuser_input()
if stops_board():
print(f"Congrats Y, You are the winner")
break

elif ((q == 1) and (Player1 == "O")) or ((q == 2) and (Player2 == "O")):
for i in range(5):
yuser_input()
if stops_board():
print(f"Congrats O, You are the winner")
break
if i == 4:
print("This is a tie")
break
xuser_input()
if stops_board():
print(f"Congrats X, You are the winner")
break


This is the As function

def ask_user(message, type_= str, valid=lambda x: True, invalid_message="Invalid"):
while True:
try:
user_input = type_(input(message))
except (ValueError, TypeError):
print("Invalid input")
continue
if valid(user_input):
return user_input
else:
print(invalid_message)


This code is fully functional, but only for local multiplayer. No AI is in place as of yet.

• Welcome to Code Review! I changed the title so that it describes what the code does per site goals: "State what your code does in your title, not your main concerns about it.". Feel free to edit and give it a different title if there is something more appropriate. – Sᴀᴍ Onᴇᴌᴀ May 22 at 23:34
• An immediate bug I see is if Choice != (1 or 2): – Linny May 23 at 7:03
• you were right it wasn't working when i type 2 – ShreddedPumpkin May 24 at 13:18

Check out the pyinputplus module. It's good for when you are asking for specific user input, such as when you want the user to answer with 'yes' or 'no', or in your case, 'X' or 'O'.

So in that case you would do something like this:

from pyinputplus import *

inputChoice(('X', 'O'))


Which will return:

Please select one of: X, O


Alternatively, as a second argument for inputChoice(), you can enter a string for the prompt:

from pyinputplus import *



Which will return:

Please enter your letter: X or O


The input will act as a while loop until one of the provided choices are input(typecase is not taken into account by default, though I think this can be changed within the function's arguments). If an invalid choice is input, the code will return (assuming your input was 'x')

'x' is not a valid choice.


pyinputplus and its various functions are great when requiring specific user input, as it keeps you from having to have a bunch of if/elif/else statements under a while loop. It saves a lot of time.

• i know that it is a good module but i still need to assign player 1 as "X" or "Y' and same for player 2, so i think i still need the if else statement. Unless its for the single player mode where i just need it to be 1 letter – ShreddedPumpkin May 24 at 13:05
• In that case, have the pyinputplus inputChoice command assign the letter to player 1, and use and if/else statements to assign the other letter to player 2. – Joel V May 25 at 14:34
• – userff May 27 at 14:15

Your first question "can the board be made more aesthetically pleasing" is subjective, but I would prefer, for only ASCII characters, something like:

 X | O | 2
---+---+---
3 | X | 5
---+---+---
6 | 7 | O


But you could make it even more fancier by using Unicode Box-drawing characters, you can copy and paste these from the Wikipedia page 'Box-drawing character':

 X │ O │ 2
───┼───┼───
3 │ X │ 5
───┼───┼───
6 │ 7 │ O


Some other remarks:

• The function ask_user could be just as good in the main file and renaming it in the import statement to As does not make its use clearer.
• You could use a main guard clause to indicate, amongst others, where the main part of the code begins, see for example this StackOverflow Question and links therein.
• Think of more descriptive function names, for instance t_Board would be better called print_board.