Python Forum
Tkinter number guessing game
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Tkinter number guessing game
#1
Another number guessing game

import tkinter as tk
from random import randint

'''
Define some global variables
start is the low number end is the high number
e.g. guess a number between 1 and 20
count is the number of tries
'''
start, end = 1, 20
count = 5


class Window:
    '''
    Define the class window. Set some variables for our game.
    Setup the layout with tkinter
    self.number is a random generated number
    self.count is the number of tries to guess the number
    '''
    def __init__(self, parent):
        self.number = self.random_number()
        self.counter = count

        parent.columnconfigure(0, weight=1)
        parent.rowconfigure(0, weight=1)

        container = tk.Frame(parent)
        container.grid(column=0, row=0, sticky='new')
        container.grid_columnconfigure(0, weight=3)

        header = tk.Label(parent, text='Number Guessing Game')
        header['font'] = ('times 16 bold')
        header['relief'] = 'groove'
        header.grid(column=0, row=0, sticky='new', pady=4, ipady=4)

        instructions = tk.Label(parent)
        instructions['text'] = f'Choose a number between {start} and {end}'
        instructions['bg'] = 'lightyellow'
        instructions['relief'] = 'groove'
        instructions.grid(column=0, row=1, sticky='new', pady=4)

        self.msgbox = tk.Label(parent, anchor='w')
        self.msgbox['relief'] = 'groove'
        self.msgbox['text'] = f'Tries Left: {self.counter}'
        self.msgbox.grid(column=0, row=2, sticky='new', pady=8)

        self.entry = tk.Entry(parent)
        self.entry.grid(column=0, row=3, sticky='new', pady=8)

        btn_frame = tk.Frame(parent)
        btn_frame.grid(column=0, row=4, sticky='new')
        for i in range(2):
            btn_frame.grid_columnconfigure(i, weight=3, uniform='btns')

        self.btn = tk.Button(btn_frame)
        self.btn['text'] = 'Submit'
        self.btn['command'] = lambda: self.check(self.entry.get())
        self.btn.grid(column=0, row=0, sticky='new')

        self.reset_btn = tk.Button(btn_frame)
        self.reset_btn['text'] = 'Reset'
        self.reset_btn['state'] = 'disabled'
        self.reset_btn.grid(column=1, row=0, sticky='new')

        msg_container = tk.Frame(parent)
        msg_container['relief'] = 'groove'
        msg_container['highlightbackground'] = 'lightgray'
        msg_container['highlightcolor'] = 'lightgray'
        msg_container['highlightthickness'] = 1
        msg_container.grid(column=0, row=5, sticky='new', pady=4)
        msg_container.grid_columnconfigure(0, weight=0)
        msg_container.grid_columnconfigure(1, weight=3)

        self.label = tk.Label(msg_container, text='MSG:', anchor='w')
        self.label.grid(column=0, row=0, sticky='new')

        self.msg_label = tk.Label(msg_container, anchor='w')
        self.msg_label.grid(column=1, row=0, sticky='new')
        self.entry.focus()
        self.entry.bind('<Return>', lambda num: self.check(self.entry.get()))


    def random_number(self):
        '''
        random_number is a function for generating a random number
        between the start and ending numbers defined above
        '''
        number = randint(start, end)
        return number

    def check(self, guess):
        '''
        Check does several things
        With a try statement we check to make sure only whole numbers are entered.
        If not display an error message
        '''
        try:
            guess = int(guess) # Convert gues to int
            self.counter = self.counter - 1 # Subtract 1 from the counter
            self.msgbox['text'] = f'Tries Left: {self.counter}'

            self.entry.delete(0, tk.END) # Clear the entry
            if guess > end or guess < start: # Check if the guess is within bounds
                if self.counter > count: # If the counter > than tries, set to default tries
                    self.counter = count
                else: # Counter is below default. Add 1 back due to incorrect input
                    self.counter = self.counter + 1
                # Display error
                self.msg_label['text'] = f'Please choose a number between {start} and {end}'
                self.msg_label['fg'] = 'red'
                self.msgbox['text'] = f'Tries Left: {self.counter}'

            elif guess > self.number: # Display message that the guess was too high
                self.msg_label['text'] = f'{guess} is too high.'
                self.msg_label['fg'] = 'red'
            elif guess < self.number: # Display message that guess was too low
                self.msg_label['text'] = f'{guess} is too low.'
                self.msg_label['fg'] = 'darkorange'
            else: # Display message that guess was correct
                self.msg_label['text'] = f'You win! {guess} is correct.'
                self.msg_label['fg'] = 'green'
                self.btn['state'] = 'disabled'
                self.reset_btn['state'] = 'normal'
                self.reset_btn['command'] = self.reset

            if self.counter == 0: # Number of tries has been reached. Disable submit button and enable reset
                self.msg_label['text'] = 'You have no tries left.'
                self.msg_label['fg'] = 'tomato'
                self.btn['state'] = 'disabled'
                self.reset_btn['state'] = 'normal'
                self.reset_btn['command'] = lambda: self.reset()

        except ValueError: # Display error - input is not correct
            self.msg_label['text'] = 'Error! Please enter only whole numbers.'
            self.msg_label['fg'] = 'red'
            self.entry.delete(0, tk.END)


    def reset(self):
        '''
        Resets all the default values and generates a new random number
        '''
        self.counter = count
        self.btn['state'] = 'normal'
        self.reset_btn['state'] = 'disabled'
        self.msg_label['text'] = ''
        self.msg_label['fg'] = 'black'
        self.msgbox['text'] = f'Tries Left: {count}'
        self.number = self.random_number()

def main():
    root = tk.Tk()
    root['padx'] = 8
    root['pady'] = 5
    root.geometry('400x210+250+250')
    root.resizable(False, False)
    Window(root)
    root.mainloop()

if __name__ == '__main__':
    main()
BashBedlam likes this post
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#2
Very Nice.
Reply
#3
Version 2
Trying to use dunder methods but, still doesn't seem right.
import tkinter as tk
import random as rnd

class Counter:
    def __init__(self, value):
        self.value = value

    def __sub__(self, other):
        return Counter(self.value - other.value)

class Number:
    def __init__(self, number):
        self.number = number

    def __lt__(self, number):
        return self.number < number

    def __gt__(self, number):
        return self.number > number

    def __eq__(self, number):
        return self.number == number

class Window:
    def __init__(self, parent):
        self.parent = parent
        self.parent.columnconfigure(0, weight=1)
        self.parent.rowconfigure(0, weight=1)

        # Set the counter
        self.counter = Counter(3)
        self.random_number = Number(rnd.randint(1, 20))

        # Main container holds all other containers and widgets
        container = tk.Frame(self.parent)
        container.grid(column=0, row=0, sticky='new')
        container.grid_columnconfigure(0, weight=3)

        # line1 container holds widgets on the first row
        line1_container = tk.Frame(container)
        line1_container['relief'] = 'groove'
        line1_container.grid(column=0, row=1, sticky = 'new', padx=4, pady=4, ipady=4, ipadx=2)
        line1_container.grid_columnconfigure(0, weight=0)
        line1_container.grid_columnconfigure(1, weight=0)
        line1_container.grid_columnconfigure(2, weight=3)

        # line2 container holds field and button widgets
        line2_container = tk.Frame(container)
        line2_container['relief'] = 'groove'
        line2_container.grid(column=0, row=2, sticky='new', padx=4, pady=4, ipady=4, ipadx=2)
        line2_container.grid_columnconfigure(0, weight=0)
        line2_container.grid_columnconfigure(1, weight=3)
        line2_container.grid_columnconfigure(2, weight=3)

        line3 = tk.Label(container, text='Choose a number between 1 and 20')
        line3['relief'] = 'groove'
        line3['font'] = 'times 10 italic'
        line3['fg'] = 'gray'
        line3.grid(column=0, row=3, sticky='new', padx=4, pady=4)

        # entry_frame is padding for the text field
        # (gives the appearence of not having the text right up against the left side)
        self.entry_frame = tk.Frame(line2_container)
        self.entry_frame['relief'] = 'sunken'
        self.entry_frame['borderwidth'] = 1
        self.entry_frame['bg'] = 'white'
        self.entry_frame.grid(column=0, row=0, sticky='new', padx=4, pady=4)

        # The header contains the game name
        header = tk.Label(container)
        header['text'] = 'Tkinter Number Guessing Game'
        header['font'] = 'Sans 16 bold'
        header['fg'] = 'darkslateblue'
        header['relief'] = 'groove'
        header.grid(column=0, row=0, sticky='new', padx=4, pady=4, ipady=4, ipadx=2)

        # Label for displaying number of guesses left
        self.guess_left = tk.Label(line1_container, anchor='w')
        self.guess_left['bg'] = 'lightgray'
        self.guess_left['relief'] = 'groove'
        self.guess_left['text'] = 'Guesses Left: 3'
        self.guess_left.grid(column=0, row=0, sticky='new', ipadx=5)

        # Label for message header
        msgbox = tk.Label(line1_container, anchor='w')
        msgbox['text'] = 'MSG:'
        msgbox['relief'] = 'groove'
        msgbox['bg'] = 'lightgray'
        msgbox.grid(column=1, row=0, sticky='new')

        # Label for displaying all messages
        self.msgtext = tk.Label(line1_container, anchor='w', padx=8)
        self.msgtext['text'] = 'Choose a number between 1 and 20'
        self.msgtext['relief'] = 'groove'
        self.msgtext['bg'] = 'lightgray'
        self.msgtext.grid(column=2, row=0, sticky='new')

        # Entry field for entering numbers
        self.entry = tk.Entry(self.entry_frame, width=4)
        self.entry['relief'] = 'flat'
        self.entry.grid(column=0, row=0, sticky='new', padx=4)
        self.entry.focus()
        self.entry.bind('<Return>', lambda num: self.check_number(self.entry.get()))


        # Button for submitting numbers
        self.submit_button = tk.Button(line2_container, text='Submit')
        self.submit_button['cursor'] = 'hand2'
        self.submit_button['command'] = lambda: self.check_number(self.entry.get())
        self.submit_button.grid(column=1, row=0, sticky='new', padx=2, pady=2)

        # Button for resetting everything to defaults
        self.reset_button = tk.Button(line2_container, text='Reset')
        self.reset_button['state'] = 'disabled'
        self.reset_button['cursor'] = 'no'
        self.reset_button.grid(column=2, row=0, sticky='new', padx=2, pady=2)

    # Method for checking guess against random number
    def check_number(self, guess):
        # Set some variables
        count = self.counter.value
        fgcolor = 'red'
        bgcolor = 'lightyellow'

        # Use a try statement in check for numbers only
        try:
            guess = int(guess)

            # We want the guess number to be between 1 and 20
            if guess > 20 or guess < 1:
                msg = 'Please choose a number between 1 and 20'
                count += 1
                setattr(self.counter, 'value', count)

            # Guess number is within spec
            # Compare the numbers and set the correct message
            else:
                if guess > self.random_number:
                    msg = f'{guess} is too high'
                if guess < self.random_number:
                    msg = f'{guess} is too low'

                # Correct number was guessed. Enable reset button and disable
                # the submit button. Also set some colors and other graphic views
                if guess == self.random_number:
                    self.submit_button['state'] = 'disabled'
                    self.entry.delete(0, tk.END)
                    self.entry['state'] = 'disabled'
                    self.entry_frame['bg'] = 'gray95'
                    self.submit_button['cursor'] = 'no'
                    self.reset_button['state'] = 'normal'
                    self.reset_button['cursor'] = 'hand2'
                    self.reset_button['command'] = lambda: self.reset()
                    self.reset_button.bind('<Return>', lambda num: self.reset())
                    self.reset_button.focus()
                    msg = f'Great job! {guess} was the correct number'
                    fgcolor = 'lime'
                    bgcolor = 'darkgreen'
        # A character other than a number was entered.
        # Display a error message
        except ValueError:
            msg = 'Please enter only whole numbers'
            count += 1
            setattr(self.counter, 'value', count)

        # Clear entry field
        self.entry.delete(0, tk.END)

        # If the last guess has been used, enable and disable the coreect buttons.
        # Also set some graphic views such as colors and cursor
        if count == 1:
            setattr(self.counter, 'value', 0)
            count = 0
            self.entry['state'] = 'disabled'
            self.entry_frame['bg'] = 'gray95'
            self.submit_button['state'] = 'disabled'
            self.reset_button['state'] = 'normal'
            self.reset_button['command'] = lambda: self.reset()
            self.reset_button['cursor'] = 'hand2'
            self.submit_button['cursor'] = 'no'
            self.reset_button.focus()
            self.reset_button.bind('<Return>', lambda num: self.reset())
            msg = msg + ' Press reset to play again.'

        # We still have guesses left
        else:
            count = self.counter.value - 1
        setattr(self.counter, 'value', count)
        self.guess_left['text'] = f'Guesses Left: {count}'
        self.msgtext['text'] = msg
        self.msgtext['fg'] = fgcolor
        self.msgtext['bg'] = bgcolor


    # Method for resetting game
    def reset(self):
        self.entry['state'] = 'normal'
        self.entry_frame['bg'] = 'white'
        self.entry.focus()
        self.submit_button['state'] = 'normal'
        self.reset_button['state'] = 'disabled'
        setattr(self.counter, 'value', 3)
        self.guess_left['text'] = f'Guesses Left: {self.counter.value}'
        self.reset_button['cursor'] = 'no'
        self.submit_button['cursor'] = 'hand2'
        self.msgtext['text'] = f'Game has been reset'
        self.msgtext['fg'] = 'red'
        self.msgtext['bg'] = 'lightyellow'
        self.random_number = Number(rnd.randint(1, 20))


def main():
    root = tk.Tk()
    root.geometry('460x160+250+250')
    root.resizable(False, False)
    Window(root)
    root.mainloop()

if __name__ == '__main__':
    main()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Guessing games pyzyx3qwerty 0 1,993 Apr-16-2020, 11:59 AM
Last Post: pyzyx3qwerty
  guess my number GAME ronblue77 2 2,765 Nov-24-2019, 04:23 PM
Last Post: CodingStranger

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020