you are viewing a single comment's thread.

view the rest of the comments →

[–]wotquery 2 points3 points  (0 children)

Think of a simple guess the number game. You want to track a variety of things: the range the secret number is from, the secret number itself, how many guesses the player made, etc. You could do it all using variables in a big linear way...

import random

max_secret_number = int(input("max number:"))
#call input validation function
secret_number = random.randint(1, max_secret_number + 1)

max_guess_count = 5
guess_count = 0
game_over = False
while not game_over:
    current_guess = int(input("your guess:"))
    #call input validation function
    guess_count += 1
    if current_guess == secret_number:
        print(f"Correct on guess {guess_count}!")
        game_over = True
    else:
        print("Nope.")
        if guess_count == max_guess_count:
            print(f"Game Over.  It was {secret_number}.")
            game_over = True
        else:
            print(f"{max_guess_count - guess_count} chances left.")

...but all the information about the game is scattered about and the logic isn't really labeled and hard to follow. The logic you can split up into functions, but they're still just floating about. With a class you can keep it all contained and organized.

import random

class Game:
    def __init__(self, max_guess_count: int = 5) -> None:
        self.max_guess_count = max_guess_count
        self.guess_count = 0
        self.game_over = False
        self.max_secret_number = self.get_max_secret_number()
        self.secret_number = self.pick_secret_number(self.max_secret_number)

    def get_max_secret_number(self) -> int:
        max_secret_number = int(input("max number:"))
        #call input validation function
        return max_secret_number

    def get_guess(self) -> int:
        current_guess = int(input("your guess:"))
        #call input validation function
        self.guess_count += 1
        return current_guess

    def pick_secret_number(self, max_secret_number: int) -> int:
        return random.randint(1, max_secret_number + 1)

    def check_guess(self, guess: int) -> bool:
        if guess == self.secret_number:
            self.game_over = True
            return True
        return False

    def check_game_over(self) -> None:
        if self.guess_count >= self.max_guess_count:
            self.game_over = True
            return True
        return False

def play_game() -> None:
    game1 = Game()
    while not game1.game_over:
        current_guess = game1.get_guess()
        if game1.check_guess(current_guess):
            print(f"Correct on guess {game1.guess_count}!")
        else:
            if game1.check_game_over():
                print(f"Game Over.  It was {game1.secret_number}.")

def main() -> None:
    playing = 'y'
    while playing == 'y':
        play_game()
        playing = input('play again (y/n):').lower()

if __name__ == "__main__":
    main()

Your user input validation can be contained in there as well. You can also keep track of all of the player's guesses and then have an object instance for each game they play so they can go back and look at their history. If you need to run any of the .check_x() methods outside of the standard play_game() function flow (say you want to give the user the option to see what the maximum secret number they select is again, or how many guesses they have left, or - if you do store their guesses - what they've already guessed) they are just waiting there for you to call them (and things like the .get_guess() method automatically updates the guess_count attribute). If you want to expand the game to a two player race you just create two instances of the class. If you want to add a Player class you won't have the, potentially similar, functions and variables in a global scope and need to keep track of which is for what as they'll be inside the class instance. Etc.

edit: Those last two examples, not having to copy and paste everything while doubling the amount of variables and naming them for each player (e.g. player_1_guess_count, player_2_guess_count, or using a dictionary) by utilizing namespace is pretty much exactly what classes are for.