Python Forum
Tkinter number guessing game - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: General (https://python-forum.io/forum-1.html)
+--- Forum: Code sharing (https://python-forum.io/forum-5.html)
+--- Thread: Tkinter number guessing game (/thread-36712.html)



Tkinter number guessing game - menator01 - Mar-21-2022

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()



RE: Tkinter number guessing game - BashBedlam - Mar-21-2022

Very Nice.


RE: Tkinter number guessing game - menator01 - Mar-24-2022

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()