patternpythonMinor
Python Tic Tac Toe Game
Viewed 0 times
toetictacgamepython
Problem
I made this as a personal challenge. It seems to work fine as I can't find any bugs and am happy with how it runs, but I am interested in what I should do to make the code more professional.
I think the
```
def tic_tac_toe():
board = [1, 2, 3, 4, 5, 6, 7, 8, 9]
end = False
win_commbinations = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6))
def draw():
print(board[0], board[1], board[2])
print(board[3], board[4], board[5])
print(board[6], board[7], board[8])
print()
def p1():
n = choose_number()
if board[n] == "X" or board[n] == "O":
print("\nYou can't go there. Try again")
p1()
else:
board[n] = "X"
def p2():
n = choose_number()
if board[n] == "X" or board[n] == "O":
print("\nYou can't go there. Try again")
p2()
else:
board[n] = "O"
def choose_number():
while True:
while True:
a = input()
try:
a = int(a)
a -= 1
if a in range(0, 9):
return a
else:
print("\nThat's not on the board. Try again")
continue
except ValueError:
print("\nThat's not a number. Try again")
continue
def check_board():
count = 0
for a in win_commbinations:
if board[a[0]] == board[a[1]] == board[a[2]] == "X":
print("Player 1 Wins!\n")
print("Congratulations!\n")
I think the
draw() function can be replaced with two for loops but I could never get it working. The code for the input by each of the players and the check_board() function seems messy and I think it could be reduced by using arguments. I also think the while loop could be reduced somehow.```
def tic_tac_toe():
board = [1, 2, 3, 4, 5, 6, 7, 8, 9]
end = False
win_commbinations = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6))
def draw():
print(board[0], board[1], board[2])
print(board[3], board[4], board[5])
print(board[6], board[7], board[8])
print()
def p1():
n = choose_number()
if board[n] == "X" or board[n] == "O":
print("\nYou can't go there. Try again")
p1()
else:
board[n] = "X"
def p2():
n = choose_number()
if board[n] == "X" or board[n] == "O":
print("\nYou can't go there. Try again")
p2()
else:
board[n] = "O"
def choose_number():
while True:
while True:
a = input()
try:
a = int(a)
a -= 1
if a in range(0, 9):
return a
else:
print("\nThat's not on the board. Try again")
continue
except ValueError:
print("\nThat's not a number. Try again")
continue
def check_board():
count = 0
for a in win_commbinations:
if board[a[0]] == board[a[1]] == board[a[2]] == "X":
print("Player 1 Wins!\n")
print("Congratulations!\n")
Solution
My strongest objection is to your use of recursion as a kind of goto:
I suggest changing the
You do some similar recursive calls in
Board layout
Your board layout is
For a better user experience, I suggest flipping it upside-down to mimic a computer number keypad.
I think that the code would be less confusing if the board labels matched the list indices.
Handling two players
The
Implementation details
-
-
In
Suggested solution
if input("Play again (y/n)\n") == "y":
print()
tic_tac_toe()I suggest changing the
tic_tac_toe() function to play just one game, then calling it in a loop.You do some similar recursive calls in
p1() and p2().Board layout
Your board layout is
1 2 3
4 5 6
7 8 9For a better user experience, I suggest flipping it upside-down to mimic a computer number keypad.
I think that the code would be less confusing if the board labels matched the list indices.
Handling two players
The
p1() function resembles p2(), and the main loop contains two copies of code that is mostly the same. check_board() also has some repetition. The root cause is the need to associate Player 1 with the symbol X and the verbal description "cross", and Player 2 with the symbol O and the verbal description "nought". The code would be easier to generalize if you just called them "Player X" and "Player O", and I think that the user experience would be enhanced as well. (You could also generalize the code without such a simplification by using, say, a namedtuple to represent each player.)p1() and p2() could be simplified by moving the validation into choose_number(), especially if you didn't care to distinguish between the "You can't go there" and "That's not on the board" error messages.Implementation details
- In
choose_number(), there is no need for a nestedwhile True. You also don't need to writecontinue.
- Renaming
check_board()tois_game_over()would make its purpose more obvious
-
win_commbinations contains a misspelling, and would be better as WIN_COMBINATIONS to indicate that it is a constant. board[a[0]] == board[a[1]] == board[a[2]] is ugly, and would be better expressed asfor a, b, c in WIN_COMBINATIONS:
if board[a] == board[b] == board[c] …-
In
check_board(), you used the variable a for two different purposes, which is confusing. I would just rewrite the second half of the function entirely asif 9 == sum((pos == 'X' or pos == 'O') for pos in board):
print("The game ends in a tie\n")
return True- There is no need for the
endvariable. (In general, such flag variables should be eliminated.)
Suggested solution
def tic_tac_toe():
board = [None] + list(range(1, 10))
WIN_COMBINATIONS = [
(1, 2, 3),
(4, 5, 6),
(7, 8, 9),
(1, 4, 7),
(2, 5, 8),
(3, 6, 9),
(1, 5, 9),
(3, 5, 7),
]
def draw():
print(board[7], board[8], board[9])
print(board[4], board[5], board[6])
print(board[1], board[2], board[3])
print()
def choose_number():
while True:
try:
a = int(input())
if a in board:
return a
else:
print("\nInvalid move. Try again")
except ValueError:
print("\nThat's not a number. Try again")
def is_game_over():
for a, b, c in WIN_COMBINATIONS:
if board[a] == board[b] == board[c]:
print("Player {0} wins!\n".format(board[a]))
print("Congratulations!\n")
return True
if 9 == sum((pos == 'X' or pos == 'O') for pos in board):
print("The game ends in a tie\n")
return True
for player in 'XO' * 9:
draw()
if is_game_over():
break
print("Player {0} pick your move".format(player))
board[choose_number()] = player
print()
while True:
tic_tac_toe()
if input("Play again (y/n)\n") != "y":
breakCode Snippets
if input("Play again (y/n)\n") == "y":
print()
tic_tac_toe()1 2 3
4 5 6
7 8 9for a, b, c in WIN_COMBINATIONS:
if board[a] == board[b] == board[c] …if 9 == sum((pos == 'X' or pos == 'O') for pos in board):
print("The game ends in a tie\n")
return Truedef tic_tac_toe():
board = [None] + list(range(1, 10))
WIN_COMBINATIONS = [
(1, 2, 3),
(4, 5, 6),
(7, 8, 9),
(1, 4, 7),
(2, 5, 8),
(3, 6, 9),
(1, 5, 9),
(3, 5, 7),
]
def draw():
print(board[7], board[8], board[9])
print(board[4], board[5], board[6])
print(board[1], board[2], board[3])
print()
def choose_number():
while True:
try:
a = int(input())
if a in board:
return a
else:
print("\nInvalid move. Try again")
except ValueError:
print("\nThat's not a number. Try again")
def is_game_over():
for a, b, c in WIN_COMBINATIONS:
if board[a] == board[b] == board[c]:
print("Player {0} wins!\n".format(board[a]))
print("Congratulations!\n")
return True
if 9 == sum((pos == 'X' or pos == 'O') for pos in board):
print("The game ends in a tie\n")
return True
for player in 'XO' * 9:
draw()
if is_game_over():
break
print("Player {0} pick your move".format(player))
board[choose_number()] = player
print()
while True:
tic_tac_toe()
if input("Play again (y/n)\n") != "y":
breakContext
StackExchange Code Review Q#108738, answer score: 6
Revisions (0)
No revisions yet.