2
\$\begingroup\$

I implemented a Hangman/guess the word clone in Python. This is my first attempt at making an actual interactive app instead of just a list of functions or a static program. It is also the first time I try to actually implement classes since I've been having a hard time wrapping my head around them. It's probably not the best or most practical application for a class, but it saved me the trouble of having to go around declaring globals and nonlocals throughout the code.

import json
import random
from os import system, name
import msvcrt
import colorama


class Hangman:
    def __init__(self):
        self.level = ""
        self.word = ""
        self.wrong_chars = []
        self.from_victory = 0
        self.from_defeat = 0
        self.game_state = ""
        self.on_repeat = True

    @staticmethod
    def cls():
        system('cls' if name == 'nt' else 'clear')

    def difficulty_scene(self):
        print("""
  DIFFICULTY LEVEL:
  ------------------

  (1) Very easy.
  Very long words (10+ characters) and 10 lives

  (2) Easy.
  Long words (8 or 9 characters) and 8 lives

  (3) Standard.
  Medium words (6 or 7 characters) and 6 lives

  (4) Hard.
  Short words (4 or 5 characters) and 4 lives

  (5) Impossible.
  Tiny words (3 characters) and 3 lives.

    """)
        self.level = input("  Select difficulty (1-5): ")

    def game_prep(self):
        self.word = word_list[-int(self.level) + 5][int(random.random()
                                                        * len(word_list))]
        self.wrong_chars = []
        self.from_victory = len(self.word)
        self.from_defeat = self.make_from_defeat()
        self.game_state = ""

    def make_from_defeat(self):
        return {
            "1": 10,
            "2": 8,
            "3": 6,
            "4": 4,
            "5": 3
        }.get(self.level, 6)

    def main_scene(self):
        colorama.init()

        print(f"\n  {'_' * len(self.word)}")

        while True:
            char = msvcrt.getch().decode('UTF-8')
            if char in self.word:
                for i in range(len(self.word)):
                    if char == self.word[i]:
                        print(f'\x1b[2;{i+3}H', end="")
                        print(char, end="")
                        self.from_victory -= 1
            else:
                self.wrong_chars.append(char)
                print(f'\x1b[4;{((len(self.wrong_chars) - 1) * 2) + 3}H', end="")
                print(char, end="")
                self.from_defeat -= 1
            if self.from_victory == 0 or self.from_defeat == 0:
                break

    def end_scene(self):
        if self.from_victory == 0:
            print("\n  YOU HAVE WON")
        elif self.from_defeat == 0:
            print("\n  YOU HAVE LOST")
            print(f"  your word was {self.word}")
        repeat = input("\n  Try again? (y/n):\n  ")
        if repeat == "y":
            self.on_repeat = True
        else:
            self.on_repeat = False

    def main(self):
        self.cls()
        self.difficulty_scene()
        self.cls()
        while self.on_repeat:
            self.game_prep()
            self.main_scene()
            self.cls()
            self.end_scene()
            self.cls()


game = Hangman()

if __name__ == '__main__':

    with open("words.json", "r") as read_file:
        word_list = json.load(read_file)

    game.main()

words.json is a 2-dimensional array of words sorted by length.

\$\endgroup\$
  • \$\begingroup\$ @Jamal Hey, I'm still new to Code Review, was there a point to editing the formatting of the code? why is one method preferred over the other? \$\endgroup\$ – Miguel Bartelsman Apr 7 '19 at 16:33
  • 1
    \$\begingroup\$ Not entirely, but I usually revert it to indented (the way it used to be) if there's something else to edit. It at least helps the code stand out from the text in the editor. \$\endgroup\$ – Jamal Apr 8 '19 at 2:56
  • \$\begingroup\$ Gotcha, thanks for the fixes \$\endgroup\$ – Miguel Bartelsman Apr 8 '19 at 7:11
2
\$\begingroup\$
  • Docstrings: You should include a docstring at the beginning of every method, class, and module you write. This will help any documentation identify what your code is supposed to do.
  • Simplified boolean comparisons:

You have code like:

if repeat == "y":
    self.on_repeat = True
else:
    self.on_repeat = False

Instead of having an if/else and setting the boolean condition based on that, you can simply set the value of self.on_repeat based off the first check, like so:

self.on_repeat = repeat == "y"
  • Meaningful method names: Method names should be based off what the code inside them does. While some of your methods do (main_scene, end_scene), some of them do not. make_from_defeat for example. I had no clue what the same was trying to suggest about the method, and it doesn't seem to correlate to what the method does. I left them unchanged so you could figure out for yourself what those method names should be.
  • Constant Variable Naming: All constant variables in your program should be UPPERCASE, to distinct them from non-constants and make it clear to you/a reviewer that they are constants.

Updated Code

"""
Module Doctring:
A description of your program goes here
"""
import json
import random
from os import system, name
import msvcrt
import colorama

class Hangman:
    """
    Class for storing properties of the object `Hangman`
    """
    def __init__(self):
        self.level = ""
        self.word = ""
        self.wrong_chars = []
        self.from_victory = 0
        self.from_defeat = 0
        self.game_state = ""
        self.on_repeat = True

    @staticmethod
    def cls():
        """ Clears the console """
        system('cls' if name == 'nt' else 'clear')

    def difficulty_scene(self):
        """ Prints the difficulty options """
        print("""
  DIFFICULTY LEVEL:
  ------------------

  (1) Very easy.
  Very long words (10+ characters) and 10 lives

  (2) Easy.
  Long words (8 or 9 characters) and 8 lives

  (3) Standard.
  Medium words (6 or 7 characters) and 6 lives

  (4) Hard.
  Short words (4 or 5 characters) and 4 lives

  (5) Impossible.
  Tiny words (3 characters) and 3 lives.

    """)
        self.level = input("  Select difficulty (1-5): ")

    def game_prep(self):
        """ Prepares the game """
        self.word = WORD_LIST[-int(self.level) + 5][int(random.random()
                                                        * len(WORD_LIST))]
        self.wrong_chars = []
        self.from_victory = len(self.word)
        self.from_defeat = self.make_from_defeat()
        self.game_state = ""

    def make_from_defeat(self):
        """ Retuns the length of the word to guess """
        return {
            "1": 10,
            "2": 8,
            "3": 6,
            "4": 4,
            "5": 3
        }.get(self.level, 6)

    def main_scene(self):
        """ Determines if letter is inside word """
        colorama.init()

        print(f"\n  {'_' * len(self.word)}")

        while True:
            char = msvcrt.getch().decode('UTF-8')
            if char in self.word:
                for i in range(len(self.word)):
                    if char == self.word[i]:
                        print(f'\x1b[2;{i+3}H', end="")
                        print(char, end="")
                        self.from_victory -= 1
            else:
                self.wrong_chars.append(char)
                print(f'\x1b[4;{((len(self.wrong_chars) - 1) * 2) + 3}H', end="")
                print(char, end="")
                self.from_defeat -= 1
            if self.from_victory == 0 or self.from_defeat == 0:
                break

    def end_scene(self):
        """ Determines if the player has won or list """
        if self.from_victory == 0:
            print("\n  YOU HAVE WON")
        elif self.from_defeat == 0:
            print("\n  YOU HAVE LOST")
            print(f"  your word was {self.word}")
        repeat = input("\n  Try again? (y/n):\n  ")
        self.on_repeat = repeat == "y"

    def run(self):
        """ Runs the game """
        self.cls()
        self.difficulty_scene()
        self.cls()
        while self.on_repeat:
            self.game_prep()
            self.main_scene()
            self.cls()
            self.end_scene()
            self.cls()

if __name__ == '__main__':

    GAME = Hangman()

    with open("words.json", "r") as read_file:
        WORD_LIST = json.load(read_file)

    GAME.run()
|improve this answer|||||
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.