Python Forum
Help with classes - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: General Coding Help (https://python-forum.io/forum-8.html)
+--- Thread: Help with classes (/thread-15928.html)



Help with classes - lucaswinnerton - Feb-06-2019

Hi everyone!
I've created a noughts and crosses game in Python, and I can't figure out the best way to eliminate global variables.
I'm not sure what variables to instantiate or methods to have in my class.
Any help would be great!

I am aware that my code doesn't follow PEP 8, I'm just trying to get rid of the global variables right now.

import time
import random


def reset_values():
    global BOARD, INPUTS, PLAYER_SYMBOLS, draw_count, computer_level, MAIN_TEXT
    
    BOARD = [["1", "2", "3"],
             ["4", "5", "6"],
             ["7", "8", "9"]]

    INPUTS = [{"row": 0, "column": 0},
              {"row": 0, "column": 1},
              {"row": 0, "column": 2},
              {"row": 1, "column": 0},
              {"row": 1, "column": 1},
              {"row": 1, "column": 2},
              {"row": 2, "column": 0},
              {"row": 2, "column": 1},
              {"row": 2, "column": 2}]

    PLAYER_SYMBOLS = {"O": "noughts", "X": "crosses"}

    draw_count = 0

    computer_level = 0

    MAIN_TEXT = """
    ┌───────────────────────────────┐
    │Welcome to noughts and crosses!│
    │Press Q in any prompt to quit. │
    └───────────────────────────────┘"""

def computer_player():
    """
    Runs the player vs computer game. Calls other functions in a while True loop.
    """
    
    while True:
        c_get_level()
        c_get_symbol()
        c_print_symbols()
        c_get_start()
        number_board()
        print_board()
        reset_board()

        while True:
            c_get_square()
            check_end()
            c_computer_move()
            check_end()


def c_get_level():

    global computer_level

    while True:
        wait()
        level_input = input("\nWhat level would you like the computer to play at? (1/2/3) ")
        wait()

        if level_input in ("1", "2", "3"):
            computer_level = int(level_input)
            break

        elif level_input in ("H", "h"):
            print("You need to enter either 1, 2 or 3 here. This will decide the level that the computer will")
        
        else:
            invalid_input()


def c_get_symbol():
    """
    Gets player's input for their character
    """
    
    global player_symbol, computer_symbol
    
    while True:
        wait()
        symbol_input = input("\nWould you like to be noughts or crosses? (O/X) ")
        wait()
        
        if symbol_input in ("O", "o", "0"):
            player_symbol = "O"
            computer_symbol = "X"
            
            number_board()
            break

        elif symbol_input in ("X", "x"):
            player_symbol = "X"
            computer_symbol = "O"
            
            number_board()
            break
        
        elif symbol_input in ("Q", "q"):
            quit_program()
        
        else:
            invalid_input()


def c_print_symbols():
    print("\n\
┌───────────────────────┐\n\
│Player 1 is " + PLAYER_SYMBOLS[player_symbol] + " " + "(" + player_symbol + ")" + "│\n\
│Computer is " + PLAYER_SYMBOLS[computer_symbol] + " " + "(" + computer_symbol + ")" + "│\n\
└───────────────────────┘")


def c_get_start():
    """
    Ask the user if they want to start the game. Either restarts or quits if they don't.
    """

    while True:
        wait()
        start_input = input("\nWould you like to start the game? (Y/N) ")
        wait()

        if start_input in ("Y", "y"):
            break

        elif start_input in ("N", "n"):
            quit_program()

        elif start_input in ("H", "h"):
            print("You need to enter either Y or N here. Y will start the game and N will quit it.")

        else:
            invalid_input()


def reset_board():
    """
    Initialise the board to empty spaces.
    ┌───┬───┬───┐
    │   │   │   │ 
    ├───┼───┼───┤
    │   │   │   │ 
    ├───┼───┼───┤
    │   │   │   │ 
    └───┴───┴───┘
    """
    
    global BOARD
    
    BOARD = [[" ", " ", " "],
             [" ", " ", " "],
             [" ", " ", " "]]


def number_board():
    """
    Initialise the board to numbers.
    ┌───┬───┬───┐
    │ 1 │ 2 │ 3 │ 
    ├───┼───┼───┤
    │ 4 │ 5 │ 6 │ 
    ├───┼───┼───┤
    │ 7 │ 8 │ 9 │ 
    └───┴───┴───┘
    """
    
    global BOARD
    
    BOARD = [["1", "2", "3"],
             ["4", "5", "6"],
             ["7", "8", "9"]]


def print_board():
    """
    Draw the board with the characters in the BOARD list.
    """
    
    global BOARD

    wait()
    row_count = 0
    
    print("\n┌───┬───┬───┐") # print first line (row_count = 0)
    
    for line in BOARD:
        print("│ ", end = "") # print first vertical separator and space
        row_count += 1 # 1, 2, 3
        
        for item in line:
            print(item, end = " │ ") # print symbol, a space, a vertical separator and a space
        
        if row_count in (1, 2):
            print("\n├───┼───┼───┤") # print middle lines (row_count = 1, 2)
        
        elif row_count == 3:
            print("\n└───┴───┴───┘\n") # print last line (row_count = 3)

    wait()


def c_get_square():
    """
    Get player's square and check if it's legal.
    """
    
    global BOARD
    
    while True:
        square_input = input("What square would you like to choose? (1-9) ")

        if square_input in ("1", "2", "3", "4", "5", "6", "7", "8", "9"):
            pass
        
        else:
            invalid_input()
            continue

        if BOARD[INPUTS[int(square_input) - 1]["row"]][INPUTS[int(square_input) - 1]["column"]] in ("X", "O"):
            print("\nSorry, you have put a symbol in the same place as another!\n")
 
        else:
            BOARD[INPUTS[int(square_input) - 1]["row"]][INPUTS[int(square_input) - 1]["column"]] = player_symbol
            break

    print_board()


def c_computer_move():
    if computer_level == 1:

        while True:
            computer_move = random.randint(1, 9)
            computer_row = INPUTS[computer_move - 1]["row"]
            computer_col = INPUTS[computer_move - 1]["column"]

            if BOARD[computer_row][computer_col] in ("X", "O"):
                continue

            else:
                BOARD[computer_row][computer_col] = computer_symbol
                break

        print_board()

##    if computer_level == 2:
##        
##        while True:
##            if 
        



def check_end():
    global BOARD
    global draw_count
    
    while True:
        if BOARD[0][0] == BOARD[0][1] == BOARD[0][2] != " ":
            state_win()

        elif BOARD[1][0] == BOARD[1][1] == BOARD[1][2] != " ":
            state_win()

        elif BOARD[2][0] == BOARD[2][1] == BOARD[2][2] != " ":
            state_win()

        elif BOARD[0][0] == BOARD[1][0] == BOARD[2][0] != " ":
            state_win()

        elif BOARD[0][1] == BOARD[1][1] == BOARD[2][1] != " ":
            state_win()

        elif BOARD[0][2] == BOARD[1][2] == BOARD[2][2] != " ":
            state_win()

        elif BOARD[0][0] == BOARD[1][1] == BOARD[2][2] != " ":
            state_win()

        elif BOARD[0][2] == BOARD[1][1] == BOARD[2][0] != " ":
            state_win()

        else:
            break

    while True:
        for row in BOARD:
            for symbol in row:
                if symbol != " ":
                    draw_count += 1
                    if draw_count == 9:
                        state_draw()
                    else:
                        pass
                
                else:
                    break
                break
            break
        break


def state_win():
    print("\n\
┌────────┐\n\
│YOU WON!│\n\
└────────┘\n")

    while True:
        restart_input = input("Do you want to play again? (Y/N) ")

        if restart_input in ("Y", "y"):
            wait()
            print("Restarting the game...")
            wait()
            computer_player()

        elif restart_input in ("N", "n"):
            quit_program()

        else:
            wait()
            invalid_input()


def state_draw():
    print("\n\
┌────────────┐\n\
│It's a draw!│\n\
└────────────┘\n")

    while True:
        restart_input = input("Do you want to play again? (Y/N) ")

        if restart_input in ("Y", "y"):
            restart_calculator()

        elif restart_input in ("N", "n"):
            quit_program()

        else:
            wait()
            invalid_input()


def quit_program():
    while True:
        quit_input = input("Do you want to quit the game (Y/N)? ")
        
        if quit_input in ("Y", "y"):
            wait()
            print("\nNow quitting the game", end="")
            print_dots()
            wait()
            raise SystemExit

        elif quit_input in ("N", "n"):
            wait()
            print("Restarting the game...")
            wait()
            
            computer_player()

        else:
            wait()
            invalid_input()


def restart_calculator():
    wait()
    print("\nRestarting the game", end="")
    print_dots()
    wait()
    computer_player()


def print_dots():
    wait()
    for _ in range(3):
        print(".", end="", flush=True)
        wait()


def wait():
    time.sleep(0.25)


def invalid_input():
    wait()
    print("\nYou have entered an invalid response. Enter H for help.\n")


computer_player()



RE: Help with classes - woooee - Feb-06-2019

Quote:I'm not sure what variables to instantiate or methods to have in my class.
All of them. BTW you instantiate a class, not a variable.


RE: Help with classes - Larz60+ - Feb-06-2019

Your code doesn't run as presented:
Error:
What level would you like the computer to play at? (1/2/3) 1 Would you like to be noughts or crosses? (O/X) X Traceback (most recent call last): File "/media/larz60/Data-2TB/Projects/T-Z/T/TryStuff/src/classesFeb6-2019Orig.py", line 396, in <module> computer_player() File "/media/larz60/Data-2TB/Projects/T-Z/T/TryStuff/src/classesFeb6-2019Orig.py", line 42, in computer_player c_print_symbols() File "/media/larz60/Data-2TB/Projects/T-Z/T/TryStuff/src/classesFeb6-2019Orig.py", line 112, in c_print_symbols │Computer is " + PLAYER_SYMBOLS[computer_symbol] + " " + "(" + computer_symbol + ")" + "│\n\ NameError: name 'PLAYER_SYMBOLS' is not defined
So you will have to fix errors in class as well.
** Note ** I have not attempted to make code any more efficient, but there is room for improvement.
Again you will need to fix errors:
import time
import random


class NoughtsAndCrosses:
    def __init__(self):
        self.Board = None
        self.draw_count = None
        self.INPUTS = None
        self.PLAYER_SYMBOLS = None
        self.computer_level = None
        self.MAIN_TEXT = None
        self.computer_symbol = None
        self.player_symbol = None
        self.reset_values()
        self.computer_player()

    def reset_values(self):
        self.BOARD = [["1", "2", "3"],
                ["4", "5", "6"],
                ["7", "8", "9"]]
    
        self.INPUTS = [{"row": 0, "column": 0},
                {"row": 0, "column": 1},
                {"row": 0, "column": 2},
                {"row": 1, "column": 0},
                {"row": 1, "column": 1},
                {"row": 1, "column": 2},
                {"row": 2, "column": 0},
                {"row": 2, "column": 1},
                {"row": 2, "column": 2}]
    
        self.PLAYER_SYMBOLS = {"O": "noughts", "X": "crosses"}
    
        self.draw_count = 0
    
        self.computer_level = 0
    
        self.MAIN_TEXT = """
        ┌───────────────────────────────┐
        │Welcome to noughts and crosses!│
        │Press Q in any prompt to quit. │
        └───────────────────────────────┘"""
    
    def computer_player(self):
        """
        Runs the player vs computer game. Calls other functions in a while True loop.
        """
        
        while True:
            self.c_get_level()
            self.c_get_symbol()
            self.c_print_symbols()
            self.c_get_start()
            self.number_board()
            self.print_board()
            self.reset_board()
    
            while True:
                self.c_get_square()
                self.check_end()
                self.c_computer_move()
                self.check_end()
    
    
    def c_get_level(self):
        while True:
            self.wait()
            level_input = input("\nWhat level would you like the computer to play at? (1/2/3) ")
            self.wait()
    
            if level_input in ("1", "2", "3"):
                self.computer_level = int(level_input)
                break
    
            elif level_input in ("H", "h"):
                print("You need to enter either 1, 2 or 3 here. This will decide the level that the computer will")
            
            else:
                self.invalid_input()
    
    
    def c_get_symbol(self):
        """
        Gets player's input for their character
        """
        while True:
            self.wait()
            symbol_input = input("\nWould you like to be noughts or crosses? (O/X) ")
            self.wait()
            
            if symbol_input in ("O", "o", "0"):
                self.player_symbol = "O"
                self.computer_symbol = "X"
                
                self.number_board()
                break
    
            elif symbol_input in ("X", "x"):
                player_symbol = "X"
                computer_symbol = "O"
                
                self.number_board()
                break
            
            elif symbol_input in ("Q", "q"):
                self.quit_program()
            
            else:
                self.invalid_input()
    
    
    def c_print_symbols(self):
        print("\n\
    ┌───────────────────────┐\n\
    │Player 1 is " + self.PLAYER_SYMBOLS[self.player_symbol] + " " + "(" + self.player_symbol + ")" + "│\n\
    │Computer is " + self.PLAYER_SYMBOLS[self.computer_symbol] + " " + "(" + self.computer_symbol + ")" + "│\n\
    └───────────────────────┘")
    
    
    def c_get_start(self):
        """
        Ask the user if they want to start the game. Either restarts or quits if they don't.
        """
    
        while True:
            self.wait()
            start_input = input("\nWould you like to start the game? (Y/N) ")
            self.wait()
    
            if start_input in ("Y", "y"):
                break
    
            elif start_input in ("N", "n"):
                self.quit_program()
    
            elif start_input in ("H", "h"):
                print("You need to enter either Y or N here. Y will start the game and N will quit it.")
    
            else:
                self.invalid_input()
    
    
    def reset_board(self):
        """
        Initialise the board to empty spaces.
        ┌───┬───┬───┐
        │   │   │   │ 
        ├───┼───┼───┤
        │   │   │   │ 
        ├───┼───┼───┤
        │   │   │   │ 
        └───┴───┴───┘
        """
        
        self.BOARD = [[" ", " ", " "],
                [" ", " ", " "],
                [" ", " ", " "]]
    
    
    def number_board(self):
        """
        Initialise the board to numbers.
        ┌───┬───┬───┐
        │ 1 │ 2 │ 3 │ 
        ├───┼───┼───┤
        │ 4 │ 5 │ 6 │ 
        ├───┼───┼───┤
        │ 7 │ 8 │ 9 │ 
        └───┴───┴───┘
        """
                
        self.BOARD = [["1", "2", "3"],
                ["4", "5", "6"],
                ["7", "8", "9"]]
    
    
    def print_board(self):
        """
        Draw the board with the characters in the BOARD list.
        """
    
        self.wait()
        row_count = 0
        
        print("\n┌───┬───┬───┐") # print first line (row_count = 0)
        
        for line in self.BOARD:
            print("│ ", end = "") # print first vertical separator and space
            row_count += 1 # 1, 2, 3
            
            for item in line:
                print(item, end = " │ ") # print symbol, a space, a vertical separator and a space
            
            if row_count in (1, 2):
                print("\n├───┼───┼───┤") # print middle lines (row_count = 1, 2)
            
            elif row_count == 3:
                print("\n└───┴───┴───┘\n") # print last line (row_count = 3)
    
        self.wait()
    
    
    def c_get_square(self):
        """
        Get player's square and check if it's legal.
        """
        
        while True:
            square_input = input("What square would you like to choose? (1-9) ")
    
            if square_input in ("1", "2", "3", "4", "5", "6", "7", "8", "9"):
                pass
            
            else:
                self.invalid_input()
                continue
    
            if self.BOARD[self.INPUTS[int(square_input) - 1]["row"]][self.INPUTS[int(square_input) - 1]["column"]] in ("X", "O"):
                print("\nSorry, you have put a symbol in the same place as another!\n")
    
            else:
                self.BOARD[self.INPUTS[int(square_input) - 1]["row"]][self.INPUTS[int(square_input) - 1]["column"]] = self.player_symbol
                break
    
        self.print_board()
    
    
    def c_computer_move(self):
        if self.computer_level == 1:
    
            while True:
                computer_move = random.randint(1, 9)
                computer_row = self.INPUTS[computer_move - 1]["row"]
                computer_col = self.INPUTS[computer_move - 1]["column"]
    
                if self.BOARD[computer_row][computer_col] in ("X", "O"):
                    continue
    
                else:
                    self.BOARD[computer_row][computer_col] = self.computer_symbol
                    break
    
            self.print_board()
    
    ##    if computer_level == 2:
    ##        
    ##        while True:
    ##            if 
            
    
    
    
    def check_end(self):
        draw_count = self.draw_count
        
        while True:
            if self.BOARD[0][0] == self.BOARD[0][1] == self.BOARD[0][2] != " ":
                self.state_win()
    
            elif self.BOARD[1][0] == self.BOARD[1][1] == self.BOARD[1][2] != " ":
                self.state_win()
    
            elif self.BOARD[2][0] == self.BOARD[2][1] == self.BOARD[2][2] != " ":
                self.state_win()
    
            elif self.BOARD[0][0] == self.BOARD[1][0] == self.BOARD[2][0] != " ":
                self.state_win()
    
            elif self.BOARD[0][1] == self.BOARD[1][1] == self.BOARD[2][1] != " ":
                self.state_win()
    
            elif self.BOARD[0][2] == self.BOARD[1][2] == self.BOARD[2][2] != " ":
                self.state_win()
    
            elif self.BOARD[0][0] == self.BOARD[1][1] == self.BOARD[2][2] != " ":
                self.state_win()
    
            elif self.BOARD[0][2] == self.BOARD[1][1] == self.BOARD[2][0] != " ":
                self.state_win()
    
            else:
                break
    
        while True:
            for row in self.BOARD:
                for symbol in row:
                    if symbol != " ":
                        draw_count += 1
                        if draw_count == 9:
                            self.state_draw()
                        else:
                            pass
                    
                    else:
                        break
                    break
                break
            break
    
    
    def state_win(self):
        print("\n\
    ┌────────┐\n\
    │YOU WON!│\n\
    └────────┘\n")
    
        while True:
            restart_input = input("Do you want to play again? (Y/N) ")
    
            if restart_input in ("Y", "y"):
                self.wait()
                print("Restarting the game...")
                self.wait()
                self.computer_player()
    
            elif restart_input in ("N", "n"):
                self.quit_program()
    
            else:
                self.wait()
                self.invalid_input()
    
    
    def state_draw(self):
        print("\n\
    ┌────────────┐\n\
    │It's a draw!│\n\
    └────────────┘\n")
    
        while True:
            restart_input = input("Do you want to play again? (Y/N) ")
    
            if restart_input in ("Y", "y"):
                self.restart_calculator()
    
            elif restart_input in ("N", "n"):
                self.quit_program()
    
            else:
                self.wait()
                self.invalid_input()
    
    
    def quit_program(self):
        while True:
            quit_input = input("Do you want to quit the game (Y/N)? ")
            
            if quit_input in ("Y", "y"):
                self.wait()
                print("\nNow quitting the game", end="")
                self.print_dots()
                self.wait()
                raise SystemExit
    
            elif quit_input in ("N", "n"):
                self.wait()
                print("Restarting the game...")
                self.wait()
                
                self.computer_player()
    
            else:
                self.wait()
                self.invalid_input()
    
    
    def restart_calculator(self):
        self.wait()
        print("\nRestarting the game", end="")
        self.print_dots()
        self.wait()
        self.computer_player()
    
    
    def print_dots(self):
        self.wait()
        for _ in range(3):
            print(".", end="", flush=True)
            self.wait()
    
    
    def wait(self):
        time.sleep(0.25)
    
    
    def invalid_input(self):
        self.wait()
        print("\nYou have entered an invalid response. Enter H for help.\n")
 
if __name__ == '__main__':
    NoughtsAndCrosses()



RE: Help with classes - lucaswinnerton - Feb-06-2019

Larz60+,
Thanks for the help! Yeah, there are some errors that I need to fix.
Is it worth eliminating all the global variables for the extra use of self? It seems like self is overused in all the functions. And if the class is only being used for global variables and calling functions, does it really count as a class?

Thanks!


RE: Help with classes - woooee - Feb-06-2019

You want to start using a class for all of your programs. It eliminates a lot of the stuff that is a pain, like keeping track of globals.


RE: Help with classes - lucaswinnerton - Feb-06-2019

woooee,
Thanks for the help! Does it really count as a class, however, seeing as it's only being used for the purpose of removing global variables?
And I seem to be overusing "self" a lot when I use a class; is there any way to avoid this?


RE: Help with classes - Larz60+ - Feb-07-2019

self doesn't cost anything
removing global variables is worth it.

As you write larger and larger code, sooner or later a global will burn you badly.

consider the following where two programmers are working on the same code.
both use the same global variable, but in different functions.
Programmer A changes the global variable.
Programmer B had changed the global variable at the start of his routine, prior to the change that
programmer A made.
Next, based on the value in the global, programmer B executes code that erases backup files.
Not good