For Loop Functions in Python
Question: I am continuing with a Hangman project, and I have encountered a problem with
a for loop and performing a function inside it. For example, if you press in a
level named 'CANADA', and you press 'B', since there are no Bs in Canada, it
should draw the first line of the Hangman. This is what I've done so far:

    
    
    def hangman1():
        pygame.draw.line(screen, black, (775, 250), (775, 50), (4))
    def hangman2():
        pygame.draw.line(screen, black, (750, 250), (800, 250), (4))
    def hangman3():
        pygame.draw.line(screen, black, (775, 50), (925, 50), (4))
    def hangman4():
        pygame.draw.line(screen, black, (925, 50), (925, 175), (4))
    def hangman5():
        pygame.draw.circle(screen, black, (925, 100), 30, (0))
    def hangman6():
        pygame.draw.line(screen, black, (925, 125), (925, 200), (4))
    def hangman7():
        pygame.draw.line(screen, black, (885, 160), (965, 160), (4))
    def hangman8():
        pygame.draw.line(screen, black, (925, 200), (900, 225), (4))
    def hangman9():
        pygame.draw.line(screen, black, (925, 200), (950, 225), (4))
    

After a bit more code...

    
    
    letters = list('abcdefghijklmnopqrstuvwxyz')
    a = font2.render(str(letters[0]), True, (black))
    b = font2.render(str(letters[1]), True, (black))
    c = font2.render(str(letters[2]), True, (black))
    d = font2.render(str(letters[3]), True, (black))
    e = font2.render(str(letters[4]), True, (black))
    f = font2.render(str(letters[5]), True, (black))
    g = font2.render(str(letters[6]), True, (black))
    h = font2.render(str(letters[7]), True, (black))
    i = font2.render(str(letters[8]), True, (black))
    j = font2.render(str(letters[9]), True, (black))
    k = font2.render(str(letters[10]), True, (black))
    l = font2.render(str(letters[11]), True, (black))
    m = font2.render(str(letters[12]), True, (black))
    n = font2.render(str(letters[13]), True, (black))
    o = font2.render(str(letters[14]), True, (black))
    p = font2.render(str(letters[15]), True, (black))
    q = font2.render(str(letters[16]), True, (black))
    r = font2.render(str(letters[17]), True, (black))
    s = font2.render(str(letters[18]), True, (black))
    t = font2.render(str(letters[19]), True, (black))
    u = font2.render(str(letters[20]), True, (black))
    v = font2.render(str(letters[21]), True, (black))
    w = font2.render(str(letters[22]), True, (black))
    x = font2.render(str(letters[23]), True, (black))
    y = font2.render(str(letters[24]), True, (black))
    z = font2.render(str(letters[25]), True, (black))
    

Then...

    
    
        hangman = [hangman1, hangman2, hangman3, hangman4, hangman5, hangman6, hangman7, hangman8, hangman9]
        for linebyline in hangman:  
    

Later...

    
    
                        elif b1.collidepoint(pygame.mouse.get_pos()):
                            letter = letters[1]
                            check = country.count(letter)
                            if check >= 1:
                                if letter == letters[0]:
                                    aPosition = 325, 235
                                    a3 = screen.blit((a), (375, 235))
                                    a4 = screen.blit((a), (425, 235))
                                    a1.x, a1.y = -500, -500
                                elif letter == letters[2]:
                                    cPosition = 300, 235
                                    c1.x, c1.y = -500, -500
                                elif letter == letters[13]:
                                    nPosition = 450, 235
                                    n1.x, n1.y = -500, -500
                                elif letter == letters[3]:
                                    dPosition = 600, 235
                                    d1.x, d1.y = -500, -500
                            else:
                                b2 = font.render(str(letters[1]), True, (red))
                                screen.blit(b2, (485, 325))
                                linebyline()
                                time.sleep(0.5)
                                bPosition = -500, -500
                                b1.x, b1.y = -500, -500
    

When I press B, it turns red, and in 0.5 seconds it disappears, but it doesn't
draw the line. Any help?

**EDIT:** I did some testing with another module, and the function thing works
perfectly fine with printing normal text. But when I tested it again with
drawing (Pygame), it worked, but when combined other things (like
`time.sleep()`), it shows a white screen. When combined with a `print` the
drawing thing doesn't work but the printing does. Also, if I added a
`time.sleep(1)`, it would have a black screen for exactly nine seconds,
without doing anything else. This is my test code:

    
    
    import pygame, sys, random, time
    from pygame.locals import *
    pygame.init()
    
    screen = pygame.display.set_mode((1000, 700))
    pygame.display.set_caption("Hangman: Countries")
    black = 0, 0, 0
    def hangman1():
        pygame.draw.line(screen, black, (775, 250), (775, 50), (4))
        print 'test'
    def hangman2():
        pygame.draw.line(screen, black, (750, 250), (800, 250), (4))
        print 'test somethin'
    def hangman3():
        pygame.draw.line(screen, black, (775, 50), (925, 50), (4))
        print 'test something else'
    def hangman4():
        pygame.draw.line(screen, black, (925, 50), (925, 175), (4))
        print 'eggs'
    def hangman5():
        pygame.draw.circle(screen, black, (925, 100), 30, (0))
        print 'hangman'
    def hangman6():
        pygame.draw.line(screen, black, (925, 125), (925, 200), (4))
        print 'facebook'
    def hangman7():
        pygame.draw.line(screen, black, (885, 160), (965, 160), (4))
        print 'internet'
    def hangman8():
        pygame.draw.line(screen, black, (925, 200), (900, 225), (4))
        print 'more tests'
    def hangman9():
        pygame.draw.line(screen, black, (925, 200), (950, 225), (4))
        print 'cheese'
    
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
    
        screen.fill((255, 255, 255))
        list1 = [hangman1(), hangman2(), hangman3(), hangman4(), hangman5(), hangman6(), hangman7(), hangman8(), hangman9()]
        for greet in list1:
            greet
            time.sleep(1)
        pygame.display.flip()
    

It prints a bunch of words at the same time in the shell, and then after nine
seconds the screen changes into the hangman fully drawn, and the same set of
words come back on. Any ideas, anyone of you with experience at Pygame?


Answer: I made a functioning hangman program just now. Perhaps looking at this logic
will help:

Ignore this:

    
    
    #!/usr/bin/python
    
    import string
    alphabet = string.ascii_lowercase
    
    # represents drawing routines
    def hangman1():
        print(1)
    def hangman2():
        print(2)
    def hangman3():
        print(3)
    # ignore this, never do this:
    def myprint(x): #python2 hack, unnecessary in python3
        print(x)
    for i in range(1,10):
        globals()['hangman{}'.format(i)] = lambda i=i: myprint('bodypart#{}'.format(i))
    
    # ignore this
    class EnumItem(object):
        def __init__(self, namespace, namespace_name, value):
            self.namespace = namespace
            self.namespace_name = namespace_name
            self.value = value
        def __repr__(self):
            return '{}.{}'.format(self.namespace_name, self.value)
    class Enumeration(object):
        def __init__(self, prefix, names):
            prefix = prefix.upper().replace(' ','_')
            globals()[prefix] = self  #don't do this with locals()
    
            self.items = names
            for i,name in enumerate(names.strip().splitlines()):
                name = name.strip().upper().replace(' ','_')
                value = EnumItem(self, prefix, name)
                setattr(self, name, value)
                #globals()[name] = value  #optional, also don't do this with locals()
    

Some enums:

    
    
    Enumeration('GAME_STATE', '''
        active 
        lost 
        won
    ''')
    Enumeration('GUESS', '''
        invalid not a letter
        invalid already guessed
        correct
        correct win
        incorrect
        incorrect loss
    ''')
    

**Game logic** \- look at this part if you have trouble thinking through the
rules of hangman (I left out a few things to make it run entirely correctly
with spaces, to make it easier to understand):

    
    
    class HangmanGame(object):
        MAX_INCORRECT_GUESSES = 10
    
        _bodyparts = [
            hangman1, hangman2, hangman3 #...
        ]
    
        def __init__(self, hidden_goal_phrase):
            self.phrase = hidden_goal_phrase.lower()    # e.g. batman
            self.revealed = '?'*len(hidden_goal_phrase) # e.g. ??????
            self.guessed = set()                        # e.g. {'b', 't'}
            self.num_incorrect_guesses = 0
            self.game_state = GAME_STATE.ACTIVE
    
        def guess(self, letter):
            """
                Interact with game by calling this function repeatedly with user's guesses
                 letter - the letter the player has guessed
            """
            if not letter in alphabet or not len(letter)==1:
                return GUESS.INVALID_NOT_A_LETTER
    
            if letter in self.guessed:
                return GUESS.INVALID_ALREADY_GUESSED  # or throw a custom exception class HangmanIncorrectGuessException(Exception): pass
    
            # else guess is legitimate
    
            self.guessed.add(letter)
            if letter in self.phrase:  # if guess was correct
                # update internal state
                self.revealed = ''.join((c if c in self.guessed else (' ' if c==' ' else '?')) for c in self.phrase)
    
                # check for win
                print(set(self.guessed), set(self.phrase))
                if self.guessed>=set(self.phrase):  # non-strict superset, see set.__ge__ etc.
                    self.game_state = GAME_STATE.WON
                    self.redraw
                    return GUESS.CORRECT_WIN
                else:
                    return GUESS.CORRECT
            else:  # if guess was incorrect
                self.num_incorrect_guesses += 1
    
                # check for loss
                if self.num_incorrect_guesses==HangmanGame.MAX_INCORRECT_GUESSES:
                    self.game_state = GAME_STATE.LOST
                    self.redraw()
                    return GUESS.INCORRECT_LOSS
                else:
                    self.redraw()
                    return GUESS.INCORRECT
    
        def redraw(self):
            '''
                updates canvas to reflect current game state
            '''
            # pygame.clearcanvasorsomething()
            for bodypart in HangmanGame._bodyparts[:self.num_incorrect_guesses]:
                bodypart()
    
            if self.game_state==GAME_STATE.LOST:
                pass #draw appropriate GAME OVER
            elif self.game_state==GAME_STATE.WON:
                pass #draw appropriate CONGRATULATIONS
    

Interactive loop:

    
    
    while True:
        print('NEW GAME')
        game = HangmanGame('penguin')
        while game.game_state==GAME_STATE.ACTIVE:
            result = game.guess(raw_input('Guess a letter: '))
            print(game.revealed, result)
            print('')
    

Demo games:

    
    
    NEW GAME                                                                                                                                    
    Guess a letter: p
    (set(['p']), set(['e', 'g', 'i', 'n', 'p', 'u']))
    ('p??????', GUESS.CORRECT)
    
    Guess a letter: e
    (set(['p', 'e']), set(['e', 'g', 'i', 'n', 'p', 'u']))
    ('pe?????', GUESS.CORRECT)
    
    Guess a letter: n
    (set(['p', 'e', 'n']), set(['e', 'g', 'i', 'n', 'p', 'u']))
    ('pen???n', GUESS.CORRECT)
    
    Guess a letter: guin
    ('pen???n', GUESS.INVALID_NOT_A_LETTER)
    
    Guess a letter: 7
    ('pen???n', GUESS.INVALID_NOT_A_LETTER)
    
    Guess a letter:  
    ('pen???n', GUESS.INVALID_NOT_A_LETTER)
    
    Guess a letter: z
    bodypart#1
    ('pen???n', GUESS.INCORRECT)
    
    Guess a letter: x
    bodypart#1
    bodypart#2
    ('pen???n', GUESS.INCORRECT)
    
    Guess a letter: c
    bodypart#1
    bodypart#2
    bodypart#3
    ('pen???n', GUESS.INCORRECT)
    
    Guess a letter: i
    (set(['p', 'c', 'e', 'i', 'x', 'z', 'n']), set(['e', 'g', 'i', 'n', 'p', 'u']))
    ('pen??in', GUESS.CORRECT)
    
    Guess a letter: u
    (set(['c', 'e', 'i', 'n', 'p', 'u', 'x', 'z']), set(['e', 'g', 'i', 'n', 'p', 'u']))
    ('pen?uin', GUESS.CORRECT)
    
    Guess a letter: g
    (set(['c', 'e', 'g', 'i', 'n', 'p', 'u', 'x', 'z']), set(['e', 'g', 'i', 'n', 'p', 'u']))
    ('penguin', GUESS.CORRECT_WIN)
    
    NEW GAME
    Guess a letter: q
    bodypart#1
    ('???????', GUESS.INCORRECT)
    
    Guess a letter: w
    bodypart#1
    bodypart#2
    ('???????', GUESS.INCORRECT)
    
    Guess a letter: r
    bodypart#1
    bodypart#2
    bodypart#3
    ('???????', GUESS.INCORRECT)
    
    ...more incorrect guesses...
    
    ('???????', GUESS.INCORRECT_LOSS)
    

