Syed Jafer K

Its all about Trade-Offs

Build a game where the user guesses a randomly generated number.

Building a number-guessing game in Python is a great way to practice control flow, user input, and random number generation. Below are some input ideas and the steps to implement this game.

Game Steps

  1. Welcome Message: Display a welcome message and explain the game rules to the user.
  2. Random Number Generation: Generate a random number within a specified range (e.g., 1 to 100).
  3. User Input: Prompt the user to guess the number.
  4. Feedback: Provide feedback on whether the guess is too high, too low, or correct.
  5. Repeat: Allow the user to guess again until they find the correct number.
  6. End Game: Congratulate the user and display the number of attempts taken.

Input Ideas

Here are some specific inputs you can use to enhance the game:

  1. Range of Numbers: Allow the user to choose the range (e.g., 1 to 50, 1 to 100, or 1 to 1000) to adjust the difficulty.
  2. Maximum Attempts: Set a limit on the number of attempts (e.g., 5 or 10 attempts) to increase the challenge.
  3. Hints: Provide hints after a certain number of wrong guesses, such as whether the number is even or odd.
  4. Difficulty Levels: Offer different difficulty levels (easy, medium, hard) that affect the range of numbers and the number of attempts allowed.
  5. Play Again Option: After the game ends, ask if the user wants to play again.
  6. Input Validation: Ensure the user inputs a valid number within the specified range.

Additional Features

  • Scoring System: Introduce a scoring system based on the number of attempts.
  • Leaderboard: Track and display the top scores or fastest guesses.
  • Graphical Interface: Use a library like Tkinter to create a GUI version of the game.

Solutions

A. Simple CLI Game

The game generates a random number between 1 and 100 using random.randint(1, 100).

User Input: The user is prompted to guess the number.

Feedback: The game provides feedback after each guess:

  • If the guess is too low, it prompts the user to guess higher.
  • If the guess is too high, it prompts the user to guess lower.

Win Condition: When the user guesses the correct number, the game congratulates the player and displays the number of attempts it took to guess correctly.

import random

def get_user_guess():
    try:
        guess = int(input("Enter your guess: "))
    except ValueError:
            print("Invalid input. Please enter a valid integer.")
            get_user_guess()
    return guess

def get_random_number():
    secret_number = random.randint(1, 100)
    return secret_number

def number_guessing_game():
    """
    A simple number guessing game where the user tries to guess a randomly
    generated number between 1 and 100.
    """

    # Generate a random number between 1 and 100
    secret_number = get_random_number()
    attempts = 0

    print("Welcome to the Number Guessing Game!")
    print("I'm thinking of a number between 1 and 100.")

    while True:
        attempts += 1
        guess = get_user_guess()

        # Check if the guess is correct
        if guess < secret_number:
            print("Too low! Try again.")
        elif guess > secret_number:
            print("Too high! Try again.")
        else:
            print(f"Congratulations! You guessed the number in {attempts} attempts.")
            break

if __name__ == "__main__":
    number_guessing_game()

B. With Difficulty Levels, Play Again Option, Input Validations

  • Range of Numbers: The range of numbers is adjusted based on the selected difficulty level.
  • Maximum Attempts: The number of attempts is limited based on the difficulty level.
  • Hints: A hint is provided after half of the maximum attempts if the player has not yet guessed the correct number.
  • Difficulty Levels: Three difficulty levels (Easy, Medium, Hard) determine the range of numbers and the number of attempts.
  • Play Again Option: After the game ends, the player is asked if they want to play again.
  • Input Validation: The game ensures that the user inputs a valid number within the specified range.

import random

def print_menu():
    """
    Function to print menu"""
    print("Welcome to the Enhanced Number Guessing Game!")

    # Select difficulty level
    print("Select difficulty level:")
    print("1. Easy (Range: 1-50, 10 attempts)")
    print("2. Medium (Range: 1-100, 7 attempts)")
    print("3. Hard (Range: 1-1000, 5 attempts)")

def get_min_max_total_attempts(difficulty):
    # Set parameters based on difficulty level
    if difficulty == 1:
        min_value, max_value, max_attempts = 1, 50, 10
    elif difficulty == 2:
        min_value, max_value, max_attempts = 1, 100, 7
    else:
        min_value, max_value, max_attempts = 1, 1000, 5
    return min_value, max_value, max_attempts

def play(secret_number, min_value, max_value, max_attempts):
    attempts = 0
    hints_given = False

    print(f"\nI'm thinking of a number between {min_value} and {max_value}.")
    print(f"You have {max_attempts} attempts to guess it.\n")

    while attempts < max_attempts:
        guess = get_valid_number(f"Attempt {attempts + 1}: Enter your guess: ", min_value, max_value)
        attempts += 1

        if guess < secret_number:
            print("Too low! Try again.")
        elif guess > secret_number:
            print("Too high! Try again.")
        else:
            print(f"Congratulations! You guessed the number in {attempts} attempts.")
            break

        # Provide a hint after half the attempts if the user hasn't guessed correctly
        if attempts == max_attempts // 2 and not hints_given:
            hint = "even" if secret_number % 2 == 0 else "odd"
            print(f"Hint: The number is {hint}.")
            hints_given = True

    if attempts == max_attempts and guess != secret_number:
        print(f"\nSorry, you've used all {max_attempts} attempts. The correct number was {secret_number}.")



def get_valid_number(prompt, min_value, max_value):
    """Helper function to get a valid number within a specified range."""
    while True:
        try:
            num = int(input(prompt))
            if min_value <= num <= max_value:
                return num
            else:
                print(f"Please enter a number between {min_value} and {max_value}.")
        except ValueError:
            print("Invalid input. Please enter a valid integer.")

def play_again():
    # Ask the user if they want to play again
    play_again = input("\nDo you want to play again? (yes/no): ").strip().lower()
    if play_again == 'yes':
        number_guessing_game()
    else:
        print("Thank you for playing! Goodbye.")

def number_guessing_game():
    """
    An enhanced number guessing game where the user selects a difficulty level
    and attempts to guess a randomly generated number within a range.
    """

    print_menu()
    difficulty = get_valid_number("Choose a difficulty level (1, 2, or 3): ", 1, 3)
    min_value, max_value, max_attempts = get_min_max_total_attempts(difficulty)
    secret_number = random.randint(min_value, max_value)
    play(secret_number, min_value, max_value, max_attempts)
    play_again()
    

if __name__ == "__main__":
    number_guessing_game()

C. With Leaderboard



import os
import random


LEADERBOARD_FILE = "leaderboard.txt"

def load_leaderboard():
    """Load the leaderboard from a file."""
    if os.path.exists(LEADERBOARD_FILE):
        with open(LEADERBOARD_FILE, "r") as file:
            leaderboard = [line.strip().split(",") for line in file.readlines()]
            leaderboard = [(name, int(score)) for name, score in leaderboard]
            return sorted(leaderboard, key=lambda x: x[1])
    return []

def save_leaderboard(leaderboard):
    """Save the leaderboard to a file."""
    with open(LEADERBOARD_FILE, "w") as file:
        for name, score in leaderboard:
            file.write(f"{name},{score}\n")

def display_leaderboard(leaderboard):
    """Display the top scores from the leaderboard."""
    print("\n--- Leaderboard ---")
    if leaderboard:
        for i, (name, score) in enumerate(leaderboard[:10], start=1):
            print(f"{i}. {name}: {score} attempts")
    else:
        print("No scores yet. Be the first to play!")
    print("-------------------\n")

def update_leaderboard(player_name, attempts):
    """Update the leaderboard with the player's score."""
    leaderboard = load_leaderboard()
    leaderboard.append((player_name, attempts))
    leaderboard = sorted(leaderboard, key=lambda x: x[1])[:10]  # Keep only top 10 scores
    save_leaderboard(leaderboard)


def print_menu():
    """
    Function to print menu"""
    print("Welcome to the Enhanced Number Guessing Game!")

    # Select difficulty level
    print("Select difficulty level:")
    print("1. Easy (Range: 1-50, 10 attempts)")
    print("2. Medium (Range: 1-100, 7 attempts)")
    print("3. Hard (Range: 1-1000, 5 attempts)")

def get_min_max_total_attempts(difficulty):
    # Set parameters based on difficulty level
    if difficulty == 1:
        min_value, max_value, max_attempts = 1, 50, 10
    elif difficulty == 2:
        min_value, max_value, max_attempts = 1, 100, 7
    else:
        min_value, max_value, max_attempts = 1, 1000, 5
    return min_value, max_value, max_attempts

def play(secret_number, min_value, max_value, max_attempts):
    attempts = 0
    hints_given = False

    print(f"\nI'm thinking of a number between {min_value} and {max_value}.")
    print(f"You have {max_attempts} attempts to guess it.\n")

    while attempts < max_attempts:
        guess = get_valid_number(f"Attempt {attempts + 1}: Enter your guess: ", min_value, max_value)
        attempts += 1

        if guess < secret_number:
            print("Too low! Try again.")
        elif guess > secret_number:
            print("Too high! Try again.")
        else:
            print(f"Congratulations! You guessed the number in {attempts} attempts.")
            player_name = input("Enter your name for the leaderboard: ").strip()
            update_leaderboard(player_name, attempts)
            break

        # Provide a hint after half the attempts if the user hasn't guessed correctly
        if attempts == max_attempts // 2 and not hints_given:
            hint = "even" if secret_number % 2 == 0 else "odd"
            print(f"Hint: The number is {hint}.")
            hints_given = True

    if attempts == max_attempts and guess != secret_number:
        print(f"\nSorry, you've used all {max_attempts} attempts. The correct number was {secret_number}.")



def get_valid_number(prompt, min_value, max_value):
    """Helper function to get a valid number within a specified range."""
    while True:
        try:
            num = int(input(prompt))
            if min_value <= num <= max_value:
                return num
            else:
                print(f"Please enter a number between {min_value} and {max_value}.")
        except ValueError:
            print("Invalid input. Please enter a valid integer.")

def play_again():
    # Ask the user if they want to play again
    play_again = input("\nDo you want to play again? (yes/no): ").strip().lower()
    if play_again == 'yes':
        number_guessing_game()
    else:
        print("Thank you for playing! Goodbye.")

def number_guessing_game():
    """
    An enhanced number guessing game where the user selects a difficulty level
    and attempts to guess a randomly generated number within a range.
    """

    display_leaderboard(load_leaderboard())
    print_menu()
    difficulty = get_valid_number("Choose a difficulty level (1, 2, or 3): ", 1, 3)
    min_value, max_value, max_attempts = get_min_max_total_attempts(difficulty)
    secret_number = random.randint(min_value, max_value)
    play(secret_number, min_value, max_value, max_attempts)
    display_leaderboard(load_leaderboard())
    play_again()
    

if __name__ == "__main__":
    number_guessing_game()

D. With TkInter Gui

  • Graphical User Interface (GUI): Uses Tkinter to create a window-based interface for the game.
  • Difficulty Levels: Players can select from three difficulty levels that adjust the range of numbers and the number of attempts.
  • Scoring System: Keeps track of how many attempts it takes the player to guess the correct number.
  • Leaderboard: The top 10 scores are saved to a file and can be viewed through the GUI.
  • Hints: After half the maximum attempts, the player is provided a hint whether the number is even or odd.
  • Input Validation: Ensures that the player inputs a valid integer within the specified range.
  • Play Again: After a game ends, the interface resets, allowing the player to start a new game.

import tkinter as tk
import tkinter.simpledialog
from tkinter import messagebox
import random
import os

LEADERBOARD_FILE = "leaderboard.txt"

class NumberGuessingGame:
    def __init__(self, root):
        self.root = root
        self.root.title("Number Guessing Game")

        self.min_value = 1
        self.max_value = 100
        self.max_attempts = 10
        self.attempts = 0
        self.secret_number = 0
        self.player_name = ""
        self.hints_given = False

        self.create_widgets()

    def create_widgets(self):
        """Create the main interface widgets."""
        self.difficulty_label = tk.Label(self.root, text="Select Difficulty Level:")
        self.difficulty_label.pack(pady=10)

        self.difficulty_var = tk.IntVar(value=2)
        self.difficulty_easy = tk.Radiobutton(self.root, text="Easy (1-50)", variable=self.difficulty_var, value=1, command=self.set_difficulty)
        self.difficulty_medium = tk.Radiobutton(self.root, text="Medium (1-100)", variable=self.difficulty_var, value=2, command=self.set_difficulty)
        self.difficulty_hard = tk.Radiobutton(self.root, text="Hard (1-1000)", variable=self.difficulty_var, value=3, command=self.set_difficulty)

        self.difficulty_easy.pack()
        self.difficulty_medium.pack()
        self.difficulty_hard.pack()

        self.start_button = tk.Button(self.root, text="Start Game", command=self.start_game)
        self.start_button.pack(pady=10)

        self.guess_label = tk.Label(self.root, text="Enter your guess:")
        self.guess_entry = tk.Entry(self.root)

        self.submit_button = tk.Button(self.root, text="Submit Guess", command=self.check_guess)
        self.attempts_label = tk.Label(self.root, text="Attempts left: N/A")

        self.leaderboard_button = tk.Button(self.root, text="Show Leaderboard", command=self.show_leaderboard)
        self.leaderboard_button.pack(pady=10)

    def set_difficulty(self):
        """Set the game difficulty based on user selection."""
        difficulty = self.difficulty_var.get()
        if difficulty == 1:
            self.min_value, self.max_value, self.max_attempts = 1, 50, 10
        elif difficulty == 2:
            self.min_value, self.max_value, self.max_attempts = 1, 100, 7
        else:
            self.min_value, self.max_value, self.max_attempts = 1, 1000, 5

    def start_game(self):
        """Start the game by generating a random number and resetting the attempts."""
        self.secret_number = random.randint(self.min_value, self.max_value)
        self.attempts = 0
        self.hints_given = False

        self.difficulty_label.pack_forget()
        self.difficulty_easy.pack_forget()
        self.difficulty_medium.pack_forget()
        self.difficulty_hard.pack_forget()
        self.start_button.pack_forget()

        self.guess_label.pack(pady=10)
        self.guess_entry.pack(pady=10)
        self.submit_button.pack(pady=10)
        self.attempts_label.pack(pady=10)
        self.update_attempts_label()

    def update_attempts_label(self):
        """Update the label that shows how many attempts are left."""
        attempts_left = self.max_attempts - self.attempts
        self.attempts_label.config(text=f"Attempts left: {attempts_left}")

    def check_guess(self):
        """Check the user's guess and provide feedback."""
        try:
            guess = int(self.guess_entry.get())
            if guess < self.min_value or guess > self.max_value:
                messagebox.showerror("Error", f"Please enter a number between {self.min_value} and {self.max_value}.")
                return
        except ValueError:
            messagebox.showerror("Error", "Please enter a valid integer.")
            return

        self.attempts += 1
        self.update_attempts_label()

        if guess < self.secret_number:
            messagebox.showinfo("Result", "Too low! Try again.")
        elif guess > self.secret_number:
            messagebox.showinfo("Result", "Too high! Try again.")
        else:
            messagebox.showinfo("Congratulations!", f"You guessed the number in {self.attempts} attempts.")
            self.record_score()
            self.reset_game()
            return

        if self.attempts == self.max_attempts:
            messagebox.showinfo("Game Over", f"Sorry, you've used all {self.max_attempts} attempts. The correct number was {self.secret_number}.")
            self.reset_game()

        if self.attempts == self.max_attempts // 2 and not self.hints_given:
            hint = "even" if self.secret_number % 2 == 0 else "odd"
            messagebox.showinfo("Hint", f"Hint: The number is {hint}.")
            self.hints_given = True

    def record_score(self):
        """Record the player's score and update the leaderboard."""
        self.player_name = tkinter.simpledialog.askstring("Name", "Enter your name for the leaderboard:")
        if not self.player_name:
            self.player_name = "Anonymous"

        leaderboard = self.load_leaderboard()
        leaderboard.append((self.player_name, self.attempts))
        leaderboard = sorted(leaderboard, key=lambda x: x[1])[:10]  # Keep only top 10 scores
        self.save_leaderboard(leaderboard)

    def load_leaderboard(self):
        """Load the leaderboard from a file."""
        if os.path.exists(LEADERBOARD_FILE):
            with open(LEADERBOARD_FILE, "r") as file:
                leaderboard = [line.strip().split(",") for line in file.readlines()]
                return [(name, int(score)) for name, score in leaderboard]
        return []

    def save_leaderboard(self, leaderboard):
        """Save the leaderboard to a file."""
        with open(LEADERBOARD_FILE, "w") as file:
            for name, score in leaderboard:
                file.write(f"{name},{score}\n")

    def show_leaderboard(self):
        """Display the top scores from the leaderboard."""
        leaderboard = self.load_leaderboard()
        if leaderboard:
            leaderboard_str = "\n".join([f"{i+1}. {name}: {score} attempts" for i, (name, score) in enumerate(leaderboard)])
        else:
            leaderboard_str = "No scores yet. Be the first to play!"
        messagebox.showinfo("Leaderboard", leaderboard_str)

    def reset_game(self):
        """Reset the game to the initial state."""
        self.guess_label.pack_forget()
        self.guess_entry.pack_forget()
        self.submit_button.pack_forget()
        self.attempts_label.pack_forget()

        self.difficulty_label.pack(pady=10)
        self.difficulty_easy.pack()
        self.difficulty_medium.pack()
        self.difficulty_hard.pack()
        self.start_button.pack(pady=10)

if __name__ == "__main__":
    root = tk.Tk()
    app = NumberGuessingGame(root)
    root.mainloop()