2
\$\begingroup\$

I have made this small game called 'Guess the word, save yourself'. I want to make the code shorter. But how can I do that? Please see the code and give suggestions. I will greatly appreciate it. Also how can I improve the display to make it seem more attractive?

import random
VODOO = [ '''
  +------+
  | [o]  |
  | /|\ |
  | / \ |
  |      |
  +------+ ''', '''
+------+
| [o   |
| /|\ |
| / \ |
|      |
+------+''', '''
+------+
|  o   |
| /|\ |
| / \ |
|      |
+------+''','''
+------+
|  o   |
| /|\ |
| /    |
|      |
+------+''', '''
+------+
|  o   |
| /|\ |
|      |
|      |
+------+''', '''
+------+
|  o   |
| /|   |
|      |
|      |
+------+''', '''
+------+
|  o   |
|  |   |
|      |
|      |
+------+''', '''
+------+
|  o   |
|      |
|      |
|      |
+------+''', '''

+------+
| X  X |
|      |
|  U   |
+------+''',


    ]








words = {'Colors':'red orange yellow green blue indigo violet white black brown'.split(),
'Shapes':'square triangle rectangle circle ellipse rhombus trapazoid chevron pentagon hexagon septagon octogon'.split(),
'Fruits':'apple orange lemon lime pear watermelon grape grapefruit cherry banana cantalope mango strawberry tomato'.split(),
'Animals':'bat bear beaver cat cougar crab deer dog donkey duck eagle fish frog goat leech lion lizard monkey moose \
mouse otter owl panda python rabbit rat shark sheep skunk squid tiger turkey turtle weasel whale wolf wombat zebra'.split()}
def getRandomWord(wordDict):
    wordKey = random.choice(list(wordDict.keys()))
    wordIndex = random.randint(0,len(wordDict[wordKey])-1)
    return [wordDict[wordKey][wordIndex],wordKey]
def displayBoard(VODOO,missedLetters,correctLetters,secretWord):
    print VODOO[len(missedLetters)]
    print '\n'
    print 'MIssed Letters:',
    for letter in missedLetters:
        print letter,
    print '\n'
    blanks = '_'*len(secretWord)
    for i in range(len(secretWord)):
        if secretWord[i] in correctLetters:
            blanks = blanks[:i] + secretWord[i] + blanks[i+1:]
    for letter in blanks:
        print letter,
    print '\n'
def getGuess(alreadyGuessed, flag):
    while True:
        guess = raw_input()
        guess.lower()
        if guess == 'clue' and not flag:
            return guess
        if len(guess) != 1 :
            print 'please enter a single letter.'
        elif guess in alreadyGuessed :
            print 'You have already guessed that letter. Choose again'
        elif guess not in 'abcdefghijklmnopqrstuvwxyz' :

            print 'please enter a LETTER'

        else:
            return guess
def playAgain():
    print 'Do you want to play again?'
    return raw_input().lower().startswith('y')
print '       + GUESS THE WORD , SAVE YOUR SELF  +      '
print 'A witch has cast a spell on you..To save yourself, you must guess the \
word. This will break the spell but wrong guess will make the parts of \
the vodoo doll disappear. If all the parts disappear, YOU DIE.'
print 'BONUS: if the total number of missed letters is less than 4, the witch dies.'
print 'NOTE: you can get a clue but at the cost of a doll part i.e. costing \
      you a chance to guess... for this simply write \'CLUE\' '

missedLetters = ''
correctLetters = ''
secretWord, secretKey = getRandomWord(words)
gameIsDone = False
guess_count = 0
flag = 0
while True:

    displayBoard(VODOO,missedLetters,correctLetters,secretWord)
    print 'Guess a letter: '


    guess = getGuess(missedLetters+correctLetters, flag)




    if guess in secretWord:
        correctLetters += guess
        foundAllLetters = True
        for i in range(len(secretWord)):
            if secretWord[i] not in correctLetters:
                foundAllLetters = False
                break

        if foundAllLetters :
            print 'Yes! the secret word is ',secretWord,'YOu have won!!'
            gameIsDone = True

    else:
        if guess == 'clue' and not flag:
            guess = '*'
            print 'CLUE: THe word is a ',secretKey
            flag = 1
        missedLetters += guess
        if len(missedLetters) == len(VODOO)-1:
             displayBoard(VODOO,missedLetters,correctLetters,secretWord)
             print 'You have run out of guesses,after',str(len(missedLetters)),\
                      'missed guesses and ',str(len(correctLetters)),'correct guesses,'\
                      'the word was,"',secretWord,'"'

             print ' Now you Die!'
             gameIsDone = True
    guess_count += 1
    if gameIsDone:
        print 'The number of guesses: ',guess_count
        if len(missedLetters) <4:
            print 'Congratulations! the witch is dead.. :)'




        if playAgain():
            guess_count = 0
            missedLetters = ''
            correctLetters = ''
            flag = 0
            secretWord, secretKey = getRandomWord(words)
            gameIsDone = False
        else:
            break
\$\endgroup\$
3
  • 2
    \$\begingroup\$ how can I improve the display to make it seem more attractive? -> That's asking for a new feature, not on topic for code review SE. \$\endgroup\$ – bhathiya-perera Sep 25 '14 at 7:06
  • \$\begingroup\$ Why not just call the game hangman? That's what it is. \$\endgroup\$ – Devon Parsons Sep 26 '14 at 13:04
  • \$\begingroup\$ It is a modified version of HANGMAN but it is not that game. \$\endgroup\$ – Ami Rukshar Sep 27 '14 at 19:15
3
\$\begingroup\$
  • code style is inconsistent. You use camelCase mixed with under_scores in variable names. Spaces / new lines are also inconsistent. To enforce good coding formatting you may use PEP8 and pylint. By PEP8 class names should be CamelCase and basically all other names under_scored.
  • Use if __name__ == "__main__": to avoid executing any code on import.
  • Naming is horrible; What is VOODOO? What is flag? Does wordKeyor wordIndex are better than just word (and maybe word_group)? playAgain() suggest doing something, not returning boolean. Common naming convention for boolean-returning methods is using is_something() form.
  • strings are iterables (for letter in string) is possible
  • guessed / missed letters should be lists (or sets) - intuitively they are collections. The fact that strings are iterables is just convinient :)
  • there is a lot of redundancy: missedLetters, correctLetters, word, guess_count - all these variables are related to each other
  • because all there variables are strictly related to each other, we want to encapsulate it in class and use abstract methods to modify state of single game. By this game logic is reuseable (what if you want to port your guessing game to browser, or gui application), and implementation details are hidden from users
  • big blocks of code, multiple indentation etc. are big no-no. You want to analyze small, single concept at one time. Why? First of all, it's easier to understand for reader. Second of all, YOU CAN TEST IT.
  • there is a lot of global variables. Global variables pollutes namespace, it's harder to trace their changes and tends to provide much trouble. We are gonna get rid of that.

What responsibilities can we point out: * logic behind single word guessing game * logic behind parsing console input * logic behind getting random words (and words groups)

My try is something like that:

import random
MAX_TRIES = 10

class WordContainer(object):
    words = {
        'Colors': 'red orange yellow green blue indigo violet white black brown'.split(),
        'Shapes': 'square triangle rectangle circle ellipse rhombus trapazoid chevron pentagon hexagon septagon octogon'.split(),
        'Fruits': 'apple orange lemon lime pear watermelon grape grapefruit cherry banana cantalope mango strawberry tomato'.split(),
        'Animals': ('bat bear beaver cat cougar crab deer dog donkey duck eagle fish '
                    'frog goat leech lion lizard monkey moose mouse otter owl panda '
                    'python rabbit rat shark sheep skunk squid tiger turkey turtle '
                    'weasel whale wolf wombat zebra').split()
    }

    def get_random(self):
        group = random.choice(self.words.keys())
        word = random.choice(self.words[group])
        return group, word

class WordGuessingGame(object):
    def __init__(self, word, word_group, max_tries=MAX_TRIES):
        self.word = word
        self.word_group = word_group
        self.guessed_letters = []
        self.max_tries = max_tries
        self.was_clue_given = False

    def guess(self, letter):
        if letter not in self.guessed_letters:
            self.guessed_letters.append(letter)

    def is_over(self):
        return len(self.guessed_letters) >= self.max_tries or not self.get_missing_letters()

    def is_won(self):
        return not self.get_missing_letters()

    def is_lost(self):
        return self.is_over() and self.get_missing_letters()

    def get_missing_letters(self):
        return [l for l in self.word if l not in self.guessed_letters]

    def get_filtered_word(self):
        filtered_word = self.word
        for letter in self.word:
            if letter not in self.guessed_letters:
                filtered_word = filtered_word.replace(letter, "_")
        return filtered_word

    def give_clue(self):
        if not self.was_clue_given:
            self.max_tries -= 1
            self.was_clue_given = True

    def get_tries_left(self):
        return self.max_tries - len(self.guessed_letters)

    def __str__(self):
        repr = """Word: {0}. Guessed letters: {1}. Tries left: {2}/{3}."""
        if self.was_clue_given:
            repr += " Clue: {4}"
        return repr.format(self.get_filtered_word(), self.guessed_letters,
                           self.get_tries_left(), self.max_tries, self.word_group)

class ConsoleGameRunner(object):
    game = None
    word_container = WordContainer()
    is_running = True

    def run(self):
        while self.is_running:
            self.runSingleGame()
            self._handleConsoleContinueInput()

    def runSingleGame(self):
        self.setUpGame()
        while True:
            print self.game
            self._handleConsoleGameInput()
            if self.game.is_over():
                break;

        print "WON" if self.game.is_won() else "LOST"

    def setUpGame(self):
        word_group, word = self.word_container.get_random()
        self.game = WordGuessingGame(word, word_group)

    def _handleConsoleGameInput(self):
        input = raw_input()
        if not input:
            return;

        if input == 'clue':
            self.game.give_clue()
        elif len(input) == 1 and input in "abcdefghijklmnopqrstuvwxyz":
            self.game.guess(input)
        else:
            pass  # ignore wrong input

    def _handleConsoleContinueInput(self):
        print "Wanna continue?"
        if "y" not in raw_input():
            self.is_running = False

if __name__ == "__main__":
    ConsoleGameRunner().run()

Note, that there is no fancy ascii-art strings all along. They are not part of game logic. ConsoleGameRunner() is just a simple class to show you how to separate game logic from input and output. With code like that you can test whole logic without mixing it up with console, or picking random word.

I noticed I misused max_tries. It probably should be named as max_errors, and error count should be related to wrong tries.

def get_error_count(self):
    return len([l for l in self.guessed_letters if l not in self.word])
\$\endgroup\$
4
  • \$\begingroup\$ VOODOO means black magic. As the doll represents dark magic, I named that list voodoo. \$\endgroup\$ – Ami Rukshar Sep 27 '14 at 19:20
  • \$\begingroup\$ You are so eloquent. Thank you. This was the answer I was looking. I learned several new things. I should post more such things to clear my confusion :) \$\endgroup\$ – Ami Rukshar Sep 27 '14 at 19:21
  • \$\begingroup\$ Oh, I notices I also mixed camelCase with underscores. That sucks :) . Sorry, in my main project at work we use camelCase in python (team decision), that's why I'm a little confused. I'm glad I was able to help. Good luck with your code. \$\endgroup\$ – Łukasz Rogalski Sep 28 '14 at 12:24
  • \$\begingroup\$ I really like how you use classes. When I learned classes I didn't know how to use them in real situations. This is a good example. :) \$\endgroup\$ – Ami Rukshar Sep 29 '14 at 4:42

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.