2
\$\begingroup\$

I tried to create a "guess the word" game. I would appreciate to get some suggestion to improve myself, because I'm new at Python.

from random_word import RandomWords
import random
from translate import Translator
import time
import sys

def hangman():
    SENTINEL = 1   
    lang = input('What language do you choose?\nit, es, en, etc..\n>: ')
    while SENTINEL == 1:
        hints = 3
        answers = {'Yes':1, 'No':0}
        source = RandomWords()
        word = source.get_random_word() 
        translator= Translator(to_lang = lang)      #translates every word to a choosen language
        itWord = translator.translate(word).lower()
        print('Hello everybody and Welcome to the hangman game! Try guessing the right word before finishing your possibilities!')
        time.sleep(1)         #I added time sleep just for slowing some parts
        print('Ready?')
        time.sleep(1)
        print('Go')
        time.sleep(1)
        lenght = len(itWord) + 1       #get the lenght of the picked word
        choice = int(lenght*(3/2))     #possibilities depend by the word lenght
        showIt = '_'*(lenght-1)    
        newIt = show_it(showIt)
        showIt = list(newIt.split())   #creates a list that can be modified easier
        choices = []                   #creates an empty list that has to be filled with the used letters/words
        print('The word has lenght', lenght,'.\nYou can call the list of used  letters by writing "list", exit from the game by writing "exit", or  try to guess the word, by writing first "guess"')
        print('Write "help" to get some hints: but you have just 3 of it!')
        while choice >= 1:
            old = choice              #preserves the remained chances
            time.sleep(1)
            print('You have', choice,  'possibilities and', hints,'hints. Try it up!\n', newIt)
            guess = (input('>: ')).lower()
            if guess == 'help':       #help is needed to get hints
                if hints == 0:
                    print('Sorry, you don\'t have anymore hints')
                    continue
                else:
                    guess = random.choice(itWord) 
                    while guess in newIt:       #this is needed to get a letter that was not choosen
                        guess = random.choice(itWord)
                hints -= 1          #decreases the number of hints
            if guess in choices:    #checks if the guessed letter was not choosen yet
                print('You\'ve already used this letter/word. Please choose  another one') 
                continue
            else:
                try: 
                    if len(guess) == 1 :
                        if hints == 3 : choices.append(guess)     #it adds just the letter guessed by the user and not randomly picked  by the game
                    elif guess != 'help' and guess != 'exit' and guess != 'guess' and guess != 'list': raise ValueError
                except ValueError:
                    print('Please choose a letter, not a word')
                    continue
                if guess == 'list':    #needed to get the list of words/letters used
                    print(choices)
                    time.sleep(1)
                    continue
                elif guess == 'exit': 
                    sys.exit(print('Thanks for the game')) 
                    #this closes the game
                elif guess == 'guess': #this option is for trying guessing the entire word
                    guess = input('>: ')
                    if guess == itWord: #if the word is right it puts choice to 0 to go through the next while loop(But the choice value is preserved in the 'old' variable seen before)
                        newIt = guess
                        print('You did it!')
                        choice = 0
                    else:     #if the word is not right it decreases the possibility
                        print('No way, retry')
                        choice -= 1
                        continue
                elif guess in itWord.lower(): #if the guessed letter belongs to the randomly picked word, the program gets the index of the letter in it, the number of it and replaces the guessed letter in the right place of the showIt list
                    index = itWord.index(guess)
                    lettCount = itWord.count(guess) - 1
                    showIt[index] = guess
                    while lettCount != 0: #if there is more than one guessed letter in the randomly picked word, the program does the same thing as before for all of them
                        index = itWord.index(guess, index +1)
                        showIt[index] = guess
                        lettCount -= 1
                    newIt = show_it(showIt)
                else:
                    time.sleep(1)
                    print('Sorry, you wrong. Retry!')
                    choice -= 1
                if '_' not in newIt: 
                    #This removes all the spaces in newIt
                    newIt = remove_spaces(newIt)
                    print('All right, you found the word!', newIt.lower())
                    choice = 0
                while choice == 0 :
                #the old variable preserves all remained possibilities. The different answers depends by old value
                    time.sleep(1)
                    if old == 0: print('Sorry, you used all your possibility') 
                    time.sleep(1)
                    print('Do you want to restart the game?\n Yes/No')
                    choose = input('>: ').capitalize()
                    #from this point on, the user has the opportunity to restart the game with the previous word(if not guessed) or not, or close it
                    try:
                        SENTINEL = answers[choose]
                        if SENTINEL == 1:
                            print('Do you want to use the previous word?\n Yes/No')
                            choose = input('>: ').capitalize()
                            if choose == 'Yes':
                                choice = int(lenght*(3/2))
                                choices.clear()
                            elif choose == 'No': choice, SENTINEL = -1, 1
                        else: SENTINEL, choice = 0, -1
                    except KeyError:
                        print('Please, insert a correct answer')
                        choice = 0
    sys.exit(print('Thanks for the game'))





def show_it(word1): 
    #The function creates dashes format like _ _ _
    newIt = ''
    for x in word1: newIt = newIt + x + ' '
    newIt = newIt.rstrip(' ')
    return newIt

def remove_spaces(word):
    string = ''
    for x in word:
        if x != ' ': string += x
    return string
\$\endgroup\$
5
\$\begingroup\$

Few things that I think will help ya in the long run...

multi_line_string = """
Do you want to use the previous word?
    Yes/No
"""


print(multi_line_string)

Multi-line strings maybe assigned via triple quote (either """ or ''') blocks; having the stray apostrophe becomes no big deal. Furthermore one can utilize dedent from textwrap while printing such that the flow isn't broken...

from textwrap import dedent


some_string = """
    Sorry, you've used all available possibilities!
        ... taking a nap...
"""

print(dedent(some_string))

Functions/Methods (definitions) are super useful for repeated snippets of code...

from textwrap import dedent
from translate import Translator


to_lang = 'en'

translator = None
if to_lang != 'en':
    translator = Translator(to_lang = to_lang)


def accessibility(msg, **kwargs):
    """
    Side note, this is a `__doc__ string` that _documents_
    how something is intended to be used. And is accessible
    via commands such as;

        print(accessibility.__doc__)
        help(accessibility)

    This is a shortcut for formatting and then translating
    passed `msg` string prior to `return`ing.
    """
    if kwargs:
        msg = msg.format(**kwargs)

    if translator:
        msg = translator.translate(msg)

    return msg


def whats_wanted(msg, **kwargs):
    """
    Simple short-cut for getting user input while also
    translating `msg` if necessary.
    """
    return input(accessibility(msg, **kwargs))


## ... also do __not__ be afraid of blank lines, two between
##     classes and functions, and one between blocks and/or
##     where it makes since to do so is the _rule of thumb_
##     that I use when writing. Also it's a good idea to
##     avoid comment blocks of this length, keep things _tight_
##     and use `__doc__ strings` to avoid feeling the need for'em ;-)
the_question = "Shall we continue {user}?"
an_answer = whats_wanted(the_question,
                         user = 'yourName here')


## ... Note, defaulting instead of else-ing on a single line
##     often looks _cleaner_ when re-reading code later.
the_answer = an_answer
if translator:
    reverse_translator = Translator(to_lang = 'en')
    the_answer = reverse_translator.translate(an_answer)

if the_answer.lower() == 'yes':
    msg = accessibility("I think I heard a 'yes' from {user}",
                        user = 'yourName here')
else:
    msg = accessibility('What are you talking about?')

print(msg)

The try blocks may need a little rewriting, following is a starting point...

    ## ... Other code trimmed...

    if old == 0:
        _msg = """
        Sorry, you used all your possibility

        Do you want to restart the game?
            Yes/No
        """
        print(textwrap.dedent(_msg))
        choose = input('>: ').capitalize()

        # from this point on, the user has the opportunity
        # to restart the game with the previous word(if not
        # guessed) or not, or close it

        try:  ## To do something that may result in an exception
            SENTINEL = answers[choose]
        except KeyError:  ## that some errors will occur
            msg = 'Please, insert a correct answer'
            choice = 0
            print(textwrap.dedent(msg))
        else:  ## Do things when there was no exceptions
            msg = '''
                Do you want to use the previous word?
                    Yes/No
            '''
            print(textwrap.dedent(msg))
            choose = input('>: ').capitalize()
        finally:  ## Do things when there where no un-caught exceptions
            if choose == 'Yes':
                choice = int(lenght*(3/2))
                choices.clear()
            elif choose == 'No':
                choice = -1
                SENTINEL = 1
            else:
                choice = -1
                SENTINEL = 0

## ... other stuff...

... which'll hopefully expose bugs a bit easier; some will remain hiding though because you've got some formatting issues elsewhere as @Josay already pointed out.

With a little debugging and some tasteful use of abstraction you'll get there, so keep at it and in a few months time your code wont be as much of a danger to sobriety.

Updates

Some other bits that sorta stood out to me...

# ... Things such as
elif guess != 'help' and guess != 'exit' and guess != 'guess' and guess != 'list':

# ... look _neater_ with
elif guess not in ['help', 'exit', 'guess', 'list']:

And things like...

def show_it(word1): 
    # The function creates dashes format like _ _ _
    newIt = ''
    for x in word1:
        newIt = newIt + x + ' '
    newIt = newIt.rstrip(' ')
    return newIt

def remove_spaces(word):
    string = ''
    for x in word:
        if x != ' ':
            string += x
    return string

... should be assigned prior to being used, and they may be written in a more shorthand way via...

remove_spaces = lambda word: word.replace(' ', '')


def show_it(word): 
    """
    The function creates dashes format like _ _ _
    """
    newIt = ' '.join('_' for _ in word)
    return newIt.rstrip(' ')

I don't understand what lambda does. Can you explain me ?

I can try to explain lambda, when programming their a handy way of writing one-line functions. Borrowing a few examples from a past iteration...

edge_cost = lambda base_cost, drivers: base_cost * drivers

edge_cost(base_cost = 0.7, drivers = 2)
## -> 1.4
  • lambda base_cost, drivers declares arguments names that maybe used on the other side of the : (colon)

  • what ever is done on the other side of the colon (:) should return something as part of the operations, in this case base_cost * drivers

  • and the edge_cost = ... is defining the name that this operation can be called up with

I'll encourage you to try'em out in an active terminal session...

user@host ~ $ python
>>> remove_spaces = lambda word: word.replace(' ', '')
>>> remove_spaces(word = 'lamb spam and ham')
'lambspamandham'

>>> Enter

>>> first_class = lambda base_cost: base_cost + 0.5
>>> business_class = lambda base_cost: base_cost + 0.2
>>> first_class(0.4)
0.9
>>> business_class(0.4)
0.6

One thing to keep in mind when using'em is that their intended to be functional in the programming since, so please do not ever do...

bad_idea = lambda d: [d.update({k: v * 2}) for k, v in d.items()]

some_data = {'first': 1, 'second': 2, 'third': 3}
worse_idea = bad_idea(some_data)

... because worse_idea will then equal [None, None, None], nearly useless output, and some_data will have mutated into something like {'second': 4, 'third': 6, 'first': 2}. This type of usage of lambda will only lead to loss of time (if not hair) during the eventual debugging process.

Another not so good example would be...

show_it = lambda i: ' '.join('_' for _ in range(0, i))

... which'll output _ _ _ _ _ via show_it(5), but unless you're changing changing i constantly it's far better to assign a variable that calculates things once...

show_it = ' '.join('_' for _ in range(0, 5))

... Reasons that remove_spaces should be a function and not show_it is wonderfully obvious in this case, in the future figuring out how things should be assigned can be a little murky.

Other than that though their supper useful for simple things that get reused elsewhere, as well as for prototyping because it's less key-strokes to some kind of result... Oh and ya may run across lambda being used in Math (if I remember correctly this is where such ideas originated), and in most cases they'll operate very much the same, however, they're not equivalent so that can sometimes cause troubles when translating from one to the other.


Even more updates

So I spent a little more time examining your code and other things popped out...

## ...
            if guess == 'help':
                if hints == 0:
                    print('Sorry, you don\'t have anymore hints')
                    continue
                else:
                    guess = random.choice(itWord) 
                    while guess in newIt:
                        guess = random.choice(itWord)
## ...

... doesn't need an else, because when the if statement trips continue things move on to the next iteration. Following is a more correct way of expressing your intentions with a computer...

## ...
            if guess == 'help':
                if hints == 0:
                    print("Sorry, you don't have anymore hints")
                    continue

                guess = random.choice(itWord) 
                while guess in newIt:
                    guess = random.choice(itWord)
## ...

... with a little editing other places that the code is using continue like this, the amount of tabbing in that's required (AKA cyclomatic complexity) could be significantly reduced.

\$\endgroup\$
  • \$\begingroup\$ Thank you. It is really helpful.. But I don't understand what lambda does. Can you explain me ? \$\endgroup\$ – Roberta Belladonna Jun 1 at 11:30
  • 1
    \$\begingroup\$ Most welcome @RobertaBelladonna and lambda is another way to define a function (an anonymous one in this case) in Python; and some other languages. See the updates for example usage and a few warnings too. Generally I use lambda for short-cuts when it doesn't subtract from readability, and/or labeling some kind of transformation or math to be outputed. \$\endgroup\$ – S0AndS0 Jun 1 at 21:59

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.