2
\$\begingroup\$

I have created my first personal project - tic tac toe game. It is my first time approaching code using Object-Oriented paradigm and I would like to get feedback on it and on aspects where I can improve.

class Game:
 game_over = False
 players = []
 grids = {"Top left": " ", "Top": " ", "Top right": " ", "Mid left": " ", "Mid": " ", "Mid right": " ", "Bottom left": " " , "Bottom": " ", "Bottom right": " "}
 rounds = 0
 def print_board(self):
 
 print( " | | ", end =" ")
 print( " | | ")
 print(" {} | {} | {} ".format(Game.grids["Top left"], Game.grids["Top"], Game.grids["Top right"]), end=" ")
 print(" {} | {} | {} ".format("Top left", "Top", "Top right"))
 print( " | | ", end = " ")
 print( " | | ")
 print("---------------------------------", end = " ")
 print(" ---------------------------------")
 print( " | | ", end = " ")
 print( " | | ")
 print(" {} | {} | {} ".format(Game.grids["Mid left"], Game.grids["Mid"], Game.grids["Mid right"]), end = " ")
 print(" {} | {} | {} ".format("Mid left", "Mid", "Mid right"))
 print( " | | ", end = " ")
 print( " | | ")
 print("----------------------------------", end = " ")
 print(" ----------------------------------")
 print( " | | ", end =" ")
 print( " | | ")
 print(" {} | {} | {} ".format(Game.grids["Bottom left"], Game.grids["Bottom"], Game.grids["Bottom right"]), end = " ")
 print(" {} | {} | {} ".format("Bottom left", "Bottom", "Bottom right"))
 print( " | | ", end = " ")
 print( " | | ")
 
 
 game.check_win(player1, player2)
 
 def add_players(self):
 if type(self) is Player:
 Game.players.append(self)
 def check_win(self, player1, player2):
 if Game.grids["Top left"] == Game.grids["Top"] == Game.grids["Top right"] == player1.symbol:
 print(f"{player1.name} has won")
 Game.game_over = True
 elif Game.grids["Mid left"] == Game.grids["Mid"] == Game.grids["Mid right"] == player1.symbol:
 print(f"{player1.name} has won")
 Game.game_over = True
 elif Game.grids["Bottom left"] == Game.grids["Bottom"] == Game.grids["Bottom right"] == player1.symbol:
 print(f"{player1.name} has won")
 Game.game_over = True
 elif Game.grids["Top left"] == Game.grids["Mid left"] == Game.grids["Bottom left"] == player1.symbol:
 print(f"{player1.name} has won")
 Game.game_over = True
 elif Game.grids["Top"] == Game.grids["Mid"] == Game.grids["Bottom"] == player1.symbol:
 print(f"{player1.name} has won")
 Game.game_over = True
 elif Game.grids["Top right"] == Game.grids["Mid right"] == Game.grids["Bottom right"] == player1.symbol:
 print(f"{player1.name} has won")
 Game.game_over = True
 elif Game.grids["Top left"] == Game.grids["Mid"] == Game.grids["Bottom right"] == player1.symbol:
 print(f"{player1.name} has won")
 Game.game_over = True
 elif Game.grids["Top right"] == Game.grids["Mid"] == Game.grids["Bottom left"] == player1.symbol:
 print(f"{player1.name} has won")
 Game.game_over = True
 elif Game.grids["Top left"] == Game.grids["Top"] == Game.grids["Top right"] == player2.symbol:
 print(f"{player2.name} has won")
 Game.game_over = True
 elif Game.grids["Mid left"] == Game.grids["Mid"] == Game.grids["Mid right"] == player2.symbol:
 print(f"{player2.name} has won")
 Game.game_over = True
 elif Game.grids["Bottom left"] == Game.grids["Bottom"] == Game.grids["Bottom right"] == player2.symbol:
 print(f"{player2.name} has won")
 Game.game_over = True
 elif Game.grids["Top left"] == Game.grids["Mid left"] == Game.grids["Bottom left"] == player2.symbol:
 print(f"{player2.name} has won")
 Game.game_over = True
 elif Game.grids["Top"] == Game.grids["Mid"] == Game.grids["Bottom"] == player2.symbol: 
 print(f"{player2.name} has won")
 Game.game_over = True
 elif Game.grids["Top right"] == Game.grids["Mid right"] == Game.grids["Bottom right"] == player2.symbol:
 print(f"{player2.name} has won")
 Game.game_over = True
 elif Game.grids["Top left"] == Game.grids["Mid"] == Game.grids["Bottom right"] == player2.symbol:
 print(f"{player2.name} has won")
 Game.game_over = True
 elif Game.grids["Top right"] == Game.grids["Mid"] == Game.grids["Bottom left"] == player2.symbol:
 print(f"{player2.name} has won")
 Game.game_over = True
 elif Game.grids["Top left"] != " " and Game.grids["Top"] != " " and Game.grids["Top right"] != " " and Game.grids["Mid left"] != " " and Game.grids["Mid"] != " " and Game.grids["Mid right"] != " " and Game.grids["Bottom left"] != " " and Game.grids["Bottom"] != " " and Game.grids["Bottom right"] != " ":
 print("It's a tie")
 Game.game_over = True
 
 def round(self):
 Game.rounds += 1
 
 def __repr__(self) -> str:
 return f"This game called Tic Tac Toe its comprised of {len(Game.grids)} grids and {len(Game.players)} players. First player fills three grids in one row, wins."
 
class Player:
 def __init__(self, name, symbol) -> None:
 self.name = name
 self.symbol = symbol
 Game.add_players(self)
 
 def choose_grid(self, grid):
 if grid not in Game.grids:
 grid = input("\nNot available. Choose another one\n-")
 self.choose_grid(grid)
 
 elif Game.grids[grid] != " ":
 grid = input("\nNah bro. Choose another one\n-")
 self.choose_grid(grid)
 elif Game.grids[grid] == " ":
 self.print_move(grid)
 def print_move(self, grid):
 Game.grids[grid] = self.symbol
 
 def __repr__(self) -> str:
 return f"{self.name} is one of best players and they have chosen {self.symbol} to play with."
print("\nWelcome to Tic Tac Toe, first player enter your name please and choose which mark you want 'X' or 'O'.\n")
player1_name = input("\n").split()
if len(player1_name) < 2:
 mark = input(f"{player1_name[0]} please choose the mark 'X' or 'O'\n")
 while mark != "X" and mark != "O":
 mark = input("\nOnly X or O\n")
 player1_name.append(mark)
player2_name = input("\nPlayer 2 enter your name only please.\n")
if player1_name[1] == "X":
 player2_mark = "O"
else:
 player2_mark = "X"
player1 = Player(player1_name[0], player1_name[1])
player2 = Player(player2_name, player2_mark)
game = Game()
game.print_board()
while not game.game_over:
 chosen_grid = input(f"Choose a grid {player1.name}\n-")
 player1.choose_grid(chosen_grid)
 game.print_board()
 if game.game_over:
 print(f"Game took {Game.rounds} rounds.")
 break
 chosen_grid = input(f"Choose a grid {player2.name}\n-")
 player2.choose_grid(chosen_grid) 
 game.print_board()
 game.round()
Sᴀᴍ Onᴇᴌᴀ
29.5k16 gold badges45 silver badges201 bronze badges
asked Jun 7, 2023 at 17:28
\$\endgroup\$
1
  • \$\begingroup\$ I see that you don't check what the first player chooses as his mark. Maybe you would want to check if he selected 'X' or 'O'. \$\endgroup\$ Commented Jun 8, 2023 at 13:06

2 Answers 2

3
\$\begingroup\$

You could assign numbers to the grids and let the player use these numbers instead of full names.

Instead of checking strict equality when you ask the type of mark, you could allow more options. For example:

ans = input()
if ans in ["x", "X"]:
 print("X")

For the game logic, you’ve chosen to store the states in a dictionary. Perhaps a two-dimensional array would make more sense.

You might consider using f-strings instead of str.format. The code would be more readable.

For example:

g = [["O", "", "O"], ["", "O", "X"], ["X", "X", "O"]]
board = (
 f" | | | | \n"
 f"{g[0][0]:^13} | {g[0][1]:^13} | {g[0][2]:^13} Top left | Top | Top right \n"
 f" | | | | \n"
 f"--------------|---------------|--------------- --------------|---------------|---------------\n"
 f" | | | | \n"
 f"{g[1][0]:^13} | {g[1][1]:^13} | {g[1][2]:^13} Mid left | Mid | Mid right \n"
 f" | | | | \n"
 f"--------------|---------------|--------------- --------------|---------------|---------------\n"
 f" | | | | \n"
 f"{g[2][0]:^13} | {g[2][1]:^13} | {g[2][2]:^13} Bottom left | Bottom | Bottom right \n"
 f" | | | | \n"
)
print(board)
answered Jun 9, 2023 at 8:59
\$\endgroup\$
3
  • \$\begingroup\$ Thanks for your response and if I may ask, why 2D array is better option than dictionary? When I approached the game, the first thing came to my mind was using dictionary as the grid will be key that holds value of that place. So I wonder why. \$\endgroup\$ Commented Jun 9, 2023 at 19:06
  • \$\begingroup\$ Tic-tac-toe can be seen as a 3×3 matrix where each element can take 3 values ("", "O", "X"). By analogy, I’m making the assumption that it’s more rational to model the game with an array (called "list" in Python) than with a dictionary. With an array, you can easily test the equality of three squares (that you called "grids") with a simple function: all(vector[0] != "" and element == vector[0] for element in vector). In this function, "vector" is an array of 3 elements, which can be a row, column or diagonal. \$\endgroup\$ Commented Jun 11, 2023 at 9:05
  • \$\begingroup\$ Note also that you can find the winner with an algorithm complexity of O(1), i.e. the execution time does not depend on the size of the game. It could be a nice challenge that you find how. See these questions for more information: stackoverflow.com/q/4198955/3057377 stackoverflow.com/q/1056316/3057377 \$\endgroup\$ Commented Jun 11, 2023 at 12:28
2
\$\begingroup\$

There could be some improvements here for your game.

So in a tic-tac-toe game, we have 3 classes: Game, Player and Board. As we play the game in real life, we need to have a board to play and it's an object to hold the state of the game. The game is more about managing the players' turns, counting wins, and init/restarting the board. A player is responsible for placing their marks 'X' or 'O' on the board. The mark should be decided by the game as the starter would have 'X'.

Having another class Board managing the state of the current round, printing out itself would be better.

answered Sep 1, 2023 at 4:04
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.