So iam making a game using OOP in python however is this the best practise when defining your objects? are you meant to define them all at the end of the script like I've done, or are you meant to define them after each class you make? Thanks in advance and any other improvements would be much appreciated!
import random # Import random module to use for computer random move import os # Import system from os module to be able to clear the console # The following function print the current board to the command window. def print_board(board): print("\n") print(" |", board[0], "|", board[1], "|", board[2], " | 1 | 2 | 3") print(" |", "--+---+---", "|") print(" |", board[3], "|", board[4], "|", board[5], " | 4 | 5 | 6") print(" |", "--+---+---", "|") print(" |", board[6], "|", board[7], "|", board[8], " | 7 | 8 | 9") # This function takes the existing board, position input from player, # marker type (either x or o) and returns the updated board based on the arguments. def update_board(board, position, marker): board[position-1] = marker print_board(board) return board # Add any other functions below for your program # Function to allow the user to select the game mode they wish to play either, # Player vs Player, Player vs Computer or their Special equivalent. def game_selection(): while True: print("\tWELCOME TO TIC - TAC - TOE!") game_mode = input("""\nSELECT YOUR GAME MODE FROM BELOW! - Player Vs Player - Type 'PvP' - Player vs Computer - Type 'PvC' Type your desired game mode here: """).strip() if game_mode in ('PvP', 'pvp', 'PVP'): pVp.instructions() return pVp.play_game() # Calls method for Player vs Player elif game_mode in ('PvC', 'pvc', 'PVP'): pVc.instructions() return pVc.play_game() # Calls method for Player vs Computer else: print("\n-- INVALID GAME MODE Type 'PvP' or 'PvC' --") # This function is called whenever the initial game ends,and give the user the # option to play another game or to exit out the console. def play_again(): while True: play_another = input("\nThank you for playing! \ Would you like to play again (Type 'yes') or enter any key to exit!: ").lower() if not play_another == "yes": exit(0) # Execute a successful exit return None # Define a class for Player class Player: # Player class has instance variables for their counter,and if they have won (True/False) def __init__(self, counter): self.has_won = False self.counter = counter # Method to allow player objects to move and select a unoccupied position on the board, # from 1-9,along with error checking/validation of there input e.g.input is a integer. def move(self): while True: try: player_input = int(input(f"\nWhere do you want to place your {self.counter}?(1-9): ")) except ValueError: print("\nInvalid Input not an number,Try Again.") continue if player_input in range(1, 10) and board[player_input - 1] == " ": update_board(board, player_input, self.counter) return self.game_state(self.counter) else: print("\nPlease enter a position between 1-9 ,which is also empty.") # Method which allows the board to be reset back to empty, and to reset the has_won # value back to 'False'. So the user can choose to play a fresh match. def reset(self): global board board = [" " for move in range(9)] self.has_won = False # Method allows a player to move an existing counter to any location on the board def move_exisiting_piece(self): if self.counter not in board: # Check if the player has a counter on the board return None while True: # Ask user if they want to move an exisiting piece or not answer = input("\nDo you want to move an existing piece (yes/no): ").lower() if answer == "yes": break if answer == "no": return None while True: # Loop which asks user to choose the counter they want to move try: location = int(input("\nWhat's the location of the your counter" " you want to move (1-9): ")) except ValueError: print("\nYou did not enter a valid location on the board!") if location in range(1, 10) and board[location - 1] == self.counter: board[location - 1] = " " while True: # Loop to ask user where to move the exisiting counter try: move_location = int(input("\nWhere on the board is the new location" " you want to place the piece? (1-9): ")) except ValueError: print("\nNot a valid location,Try Again.") if move_location in range(1, 10) and board[move_location - 1] == " ": update_board(board, move_location, self.counter) return self.game_state(self.counter) else: print("\nThis location is not avaliable to place your piece.") else: print(f"\nYou do not have a {self.counter} in this location! Try Again.") # This method is used to check the game state after every indivdual move to check if # the game is a draw, a win or to continue playing. def game_state(self, counter): horizontal_win = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] vertical_win = [[1, 4, 7], [2, 5, 8], [3, 6, 9]] diagnol_win = [[1, 5, 9], [3, 5, 7]] for x, y, z in horizontal_win: # For loop to check for horizontal win if board[x-1] == board[y-1] == board[z-1] != " ": print("\nPlayer with counter " + board[x-1] + " has won!") self.has_won = True return 1 for x, y, z in vertical_win: # For loop to check for vertical win if board[x-1] == board[y-1] == board[z-1] != " ": print("\nPlayer with counter " + board[x-1] + " has won!") self.has_won = True return 1 for x, y, z in diagnol_win: # For loop to check for diagnol win if board[x-1] == board[y-1] == board[z-1] != " ": print("\nPlayer with counter " + board[x-1] + " has won!") self.has_won = True return 1 # Use a for loop to check if board is full and there is no winner (draw) if len([c for c in board if c in ('X', 'O')]) == 9: print("\nTHIS GAME IS A DRAW! NOBODY WINS!") self.has_won = True return 1 return 0 # Return 0 if no win/draw is found # Class for the computer, which inherited values and methods from the Player class class Computer(Player): def __init__(self, counter): Player.__init__(self, counter) # Method to allow computer to select the optimal position to place a counter def cpu_move(self): # Create a 2D array of all possible combinations to play winning_combos = [[0, 1, 2], [0, 2, 1], [2, 1, 0], [3, 4, 5], [3, 5, 4], [4, 5, 3], [6, 7, 8], [6, 8, 7], [7, 8, 6], [0, 3, 6], [0, 6, 3], [3, 6, 0], [1, 4, 7], [1, 7, 4], [4, 7, 1], [2, 5, 8], [2, 8, 5], [5, 8, 2], [0, 4, 8], [0, 8, 4], [8, 4, 0], [2, 4, 6], [2, 6, 4], [6, 4, 2]] # For each value in each sub array of win_combos we check for a winning position # for the computer.If there is none then we check if the human player has a # winning turn next, if so we place a 'O' in that position for x, y, z in winning_combos: if board[4] == " ": print("\nThe COMPUTER has placed a 'O' in position 5") update_board(board, 5, self.counter) return self.game_state(self.counter) elif board[x] == "O" and board[y] == "O" and board[z] == " ": print("\nThe COMPUTER has placed a 'O' in position " + str(z+1)) update_board(board, z+1, self.counter) return self.game_state(self.counter) elif board[x] == "X" and board[y] == "X" and board[z] == " ": print("\nThe COMPUTER has placed a 'O' in position " + str(z+1)) update_board(board, z+1, self.counter) return self.game_state(self.counter) # If non of the operations above work then get the computer to select a # random position on the board to place a 'O'. while True: move = random.randint(1, 9) if board[move-1] == " ": print("\nThe COMPUTER has placed a 'O' in position " + str(move)) update_board(board, move, self.counter) return self.game_state(self.counter) # Create Game class that takes argument for the who each player is and what game mode to play class Game: def __init__(self,player1 ,player2, mode): self.player1 = player1 self.player2 = player2 self.mode = mode def instructions(self): os.system("cls") print(f"\tWELCOME TO {self.player1} VS {self.player2}!") print(f"""\nRULES: \n1. {self.player1} you're COUNTER 'X' and {self.player2} is COUNTER 'O'. 2. The first player to get 3 of their counters in a row, (up, down, across, or diagonally) is the winner. 3. When all 9 squares are full, the game is over. 4. If no player has 3 marks in a row, the game ends in a tie. 5. In this mode you also have the option to move an existing counter of your own!""") # Method which plays the oppropriate game mode depending on def play_game(self): # Print out the initial board to the console print_board(board) if self.mode == "PVP": while True: print("\n -PLAYER ONE'S TURN (X)-") if player1.move_exisiting_piece() is None: player1.move() if player1.has_won: # If player 1 wins, reset the board and has_won values player1.reset() player2.reset() return None print("\n -PLAYER TWO'S TURN (O)-") if player2.move_exisiting_piece() is None: player2.move() if player2.has_won: # If player 2 wins, reset the board and has_won values player1.reset() player2.reset() return None elif self.mode == "PVC": while True: print("\n -PLAYER ONE'S TURN (X)-") if player1.move_exisiting_piece() is None: player1.move() if player1.has_won: # If player 1 wins, reset the board and has_won values player1.reset() computer.reset() return None computer.cpu_move() if computer.has_won: # If computer wins, reset the board and has_won values player1.reset() computer.reset() return None # Program main starts from here # Global variable for the board board = [" " for move in range(9)] # Define the require objects player1 = Player("X") # Player 1 object player2 = Player("O") # Player 2 object computer = Computer("O") # Computer player object pVp = Game("PLAYER 1", "PLAYER 2", "PVP") # Player vs Player game mode object pVc = Game("PLAYER 1", "COMPUTER", "PVC") # Player vs Computer game mode object # If this is the main file execute the Tic Tac Toe Game if __name__ == "__main__": while True: # Run a loop to let user select game mode / play again game_selection() play_again() ```