Python Forum
Word game revisited
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Word game revisited
#1
hangman/word guessing game with tkinter

words.txt:
https://my-python.org/files/python/words.txt

#! /usr/bin/env python3
import random as rnd
import tkinter as tk
from tkinter import messagebox
import os
import sys
import requests

class Stats:
    wins = 0
    loss = 0
    games = 0
    tries = 0
    remaining = 0

    def __str__(self):
        return f'Stats: Wins: {Stats.wins} / Loss: {Stats.loss} / Games Played: {Stats.games} / Tries Left: {Stats.tries}'

class Model:
    url = 'https://raw.githubusercontent.com/dwyl/english-words/master/words_alpha.txt'
    response = requests.get(url)
    words = response.text.split('\r\n')


    def __init__(self):
        self.wordlist = Model.words

    def random_word(self):
        word = self.wordlist.pop(rnd.randint(0, len(self.wordlist)-1))
        return word

    def find(self, letter, word, letters):
        match = [i for i, x in enumerate(word) if x == letter]
        if match:
            for i in match:
                letters[i] = letter
                Stats.remaining -= 1
        return ' '.join(letters)

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


        # Setup the header
        header_box = tk.Frame(self.parent)
        header_box.grid(column=0, row=0, sticky='new', padx=5, pady=5)
        header_box.grid_columnconfigure(0, weight=3)

        header = tk.Label(header_box, text='Tkinter Word Game')
        header['font'] = (None, 28, 'bold')
        header.grid(column=0, row=0, sticky='new')

        # Setup stats row
        stats_box = tk.Frame(self.parent)
        stats_box['highlightbackground'] = 'black'
        stats_box['highlightcolor'] = 'black'
        stats_box['highlightthickness'] = 1
        stats_box.grid(column=0, row=1, sticky='new', padx=5, pady=5)
        stats_box.grid_columnconfigure(0, weight=3)

        self.stats = tk.Label(stats_box, anchor='w')
        self.stats['font'] = (None, 10, 'bold')
        self.stats['text'] = Stats()
        self.stats.grid(column=0, row=0, sticky='new', padx=5, pady=5)

        word_box = tk.Frame(self.parent)
        word_box['highlightbackground'] = 'black'
        word_box['highlightcolor'] = 'black'
        word_box['highlightthickness'] = 1
        word_box.grid(column=0, row=2, sticky='new', padx=5, pady=5)
        word_box.grid_columnconfigure(0, weight=0)
        word_box.grid_columnconfigure(1, weight=3)

        label = tk.Label(word_box, text='Random Word: ', anchor='w')
        label['font'] = (None, 10, 'bold')
        label.grid(column=0, row=0, sticky='new', padx=2.5, pady=5)

        self.word = tk.Label(word_box, anchor='w')
        self.word['font'] = (None, 10, 'normal')
        self.word.grid(column=1, row=0, sticky='new', padx=(2.5,5), pady=5)

        # Letter/Button row
        btn_box = tk.Frame(self.parent)
        btn_box['highlightbackground'] = 'black'
        btn_box['highlightcolor'] = 'black'
        btn_box['highlightthickness'] = 1
        btn_box.grid(column=0, row=3, sticky='new', padx=5, pady=5)

        btn_box.grid_columnconfigure(0, weight=1, uniform='btn')
        btn_box.grid_columnconfigure(2, weight=1, uniform='btn')
        btn_box.grid_columnconfigure(3, weight=1, uniform='btn')
        btn_box.grid_columnconfigure(1, weight=3)

        self.letter_btn = tk.Button(btn_box, text='Submit Letter', bg='powderblue')
        self.letter_btn['cursor'] = 'hand2'
        self.letter_btn['activebackground'] = 'skyblue'
        self.letter_btn['activeforeground'] = 'blue'
        self.letter_btn.grid(column=0, row=0, sticky='new', pady=5, padx=(5,2.5))

        self.entry = tk.Entry(btn_box)
        self.entry['font'] = (None, 14, 'normal')
        self.entry.grid(column=1, row=0, sticky='new', pady=5, padx=2.5)
        self.entry.focus()

        self.btn_new = tk.Button(btn_box, text='New Word', bg='powderblue')
        self.btn_new['cursor'] = 'hand2'
        self.btn_new['activebackground'] = 'skyblue'
        self.btn_new['activeforeground'] = 'blue'
        self.btn_new.grid(column=2, row=0, sticky='new', pady=5, padx=2.5)

        self.exit_btn = tk.Button(btn_box, text='Quit', bg='tomato')
        self.exit_btn['activebackground'] = 'red'
        self.exit_btn['activeforeground'] = 'white'
        self.exit_btn['cursor'] = 'hand2'
        self.exit_btn.grid(column=3, row=0, sticky='new', pady=5, padx=(2.5, 5))


class Controller:
    def __init__(self, view, model):
        self.view = view
        self.model = model
        self.word = self.model.random_word()
        self.letters = ['-']*len(self.word)
        self.view.word['text'] = self.letters
        Stats.tries = len(self.word) - round(len(self.word)/2)
        Stats.remaining = len(self.word)
        self.view.stats['text'] = Stats()

        # Buttons
        self.view.letter_btn['command'] = lambda: self.find(self.view.entry.get())
        self.view.btn_new['command'] = self.new
        self.view.exit_btn['command'] = self.view.parent.destroy

        # Button Binds
        self.view.parent.bind('<Return>', lambda letter: self.find(letter=self.view.entry.get()))
        self.view.parent.bind('<KP_Enter>', lambda letter: self.find(letter=self.view.entry.get()))


    def new(self):
        Stats.alist = []
        self.view.letter_btn['state'] = 'normal'
        self.view.entry['state'] = 'normal'
        self.word = self.model.random_word()
        self.letters = ['-']*len(self.word)
        self.view.word['text'] = self.letters
        Stats.tries = len(self.word) - round(len(self.word)/2)
        Stats.remaining = len(self.word)
        self.view.stats['text'] = Stats()
        self.view.entry.delete(0, tk.END)

    def find(self, letter):
        done = False
        if not letter or letter.isdigit():
            messagebox.showerror('Error', 'You must enter a letter')
            self.view.entry.delete(0, tk.END)
            Stats.tries += 1

        if len(letter) > 1:
            messagebox.showerror('Error!', 'Only one character can be entered at a time.')
            self.view.entry.delete(0, tk.END)
            Stats.tries += 1

        letter = letter.lower()
        found = self.model.find(letter, self.word, self.letters)

        if letter not in found:
            Stats.tries -= 1
            self.view.entry.delete(0, 'end')

            if Stats.tries == 0:
                self.view.letter_btn['state'] = 'disabled'
                self.view.entry['state'] = 'disabled'
                Stats.games += 1
                done = True
                self.game_over()
        else:
            self.view.entry.delete(0, 'end')
            if Stats.remaining == 0:
                self.view.letter_btn['state'] = 'disabled'
                self.view.entry['state'] = 'disabled'
                Stats.games += 1
                self.game_win()

        self.view.stats['text'] = Stats()
        if done:
            self.view.word['text'] = self.word
        else:
            self.view.word['text'] = found

    def game_over(self):
        messagebox.showinfo('Game Over!', 'You are out of guesses.')
        Stats.loss += 1
        

    def game_win(self):
        messagebox.showinfo('Congradulations!', 'You got the word!')
        Stats.wins += 1


if __name__ == '__main__':
    root = tk.Tk()
    root.title('Word Guessing Game')
    root.geometry('+300+300')
    controller = Controller(View(root), Model())
    root.mainloop()
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
It's pretty good. Where the heck did you get that word list? lol

The only thing I could suggest is giving a pop-up when the person enters more than one letter at a time, or have it accept the letter on keypress. But it's a perfectly good hangman game.
Reply


Forum Jump:

User Panel Messages

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