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()
\$\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\$
    – LoukasPap
    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)
\$\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\$
    – nico
    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\$
    – nico
    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.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.