Hangman

December 20, 2011

In today’s exercise we implement the classic Unix V7 game hangman for guessing words. The player is given a series of blanks, one per letter of a word to be guessed. At each turn the player guesses a letter; if it is in the word, all occurrences of the letter are filled in the appropriate blank, but if the guess is wrong, another body part — traditionally, the head, torso, two arms and two legs, for a total of six — is added to the gibbet. The player wins by guessing all the letters of the word before the hangman adds all the pieces of his body.

Your task is to implement the interactive game of hangman. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.

Advertisement

Pages: 1 2

6 Responses to “Hangman”

  1. Axio said

    Not much to see here.

    (define hangman-database
      ;; size of db, db.  Real game would read /usr/share/dict...
      '(5 ("hello" "ectoplasm" "mauger" "subterfuge" "lucent")))
    
    (define (select-word #!optional (db hangman-database))
      (list-ref (cadr db) (random-integer (car db))))
    
    (define (game)
      (let* ((w (select-word))
             (l (string-length w))
             (str (make-string l #\_))
             (letters (nub (string->list w) test: char=?))
             (hangman '(head torso left-arm right-arm left-leg right-leg)))
        (let loop ((h hangman) (letters letters))
          (cond
            ((null? h)
             (pretty-print "Hang on your head Tom Dooley!")
             (pretty-print (list "The word was:" w)))
            ((null? letters)
             (pretty-print w)
             (pretty-print "Lucky guy!"))
            (else 
              (pretty-print str)
              (let ((c (read-char)))
                (cond
                  ((eof-object? c) (pretty-print "Bye"))
                  ((char=? c #\newline) (loop h letters))
                  (else
                    (if (member c letters)
                      (begin
                        (for-each
                          (lambda (i)
                            (when (char=? c (string-ref w i))
                              (string-set! str i c)))
                          (iota 0 (1- l)))
                        (loop h (filter (lambda (d) (not (char=? c d))) letters)))
                      (begin
                        (pretty-print (car h))
                        (loop (cdr h) letters)))))))))))
    
  2. C++ solution, slightly less interactive and rather longer. Procedural, nothing more fancy than a string.

    #include <iostream>
    #include <string>
    #include <cstdlib>
    #include <ctime>
    
    const int LEVELS = 6;
    
    //#define TEST
    
    #ifdef TEST
    char getCh()
    {
      static int k = 0;
      char inputs[]= "EAEHMSTROGB_HNGMNA.";
      return inputs[k++]; 
    }
    
    std::string getNextWord()
    {
      return "HANGMAN";
    }
    
    #else
    char getCh()
    {
      std::string s;
      std::cin >> s;
      if (s.length() > 0)
      {
        return s[0];
      }
      return ' ';
    }
    
    std::string getNextWord()
    {
      char words[][20] = { "THE", "QUICK", "BROWN", "FOX", "JUMPS", "OVER", "LAZY", "DOG", "RED", "GREEN", "GROSS", "GRAAL" };
      unsigned int sz = sizeof(words) / sizeof(words[0]);
      return words[rand() % sz];
    }
    
    #endif
    
    void display(const std::string& word_with_blanks, int level)
    {
      //                    012345678901
      char pict[6][13] = { "+--------  \n",
                           "|/      |  \n",
                           "|       o  \n",
                           "|      /#\\ \n",
                           "|      / \\ \n",
                           "|          \n" };
      int blank_line_pos[LEVELS * 2] = { 2, 8,  3, 8,  3, 7,  3, 9,  4, 7,  4, 9};
      for (int i = level; i < LEVELS; ++i)
      {
        pict[blank_line_pos[2 * i]][blank_line_pos[2 * i + 1]] = ' ';
      }
      for (unsigned int i = 0; i < sizeof(pict) / sizeof(pict[0]); ++i)
      {
        std::cout << pict[i];
      }
      for (unsigned int i = 0; i < word_with_blanks.length(); ++i)
      {
        std::cout << word_with_blanks[i] << ' ';
      }
      std::cout << std::endl;
    }
    
    int blankWord(std::string& word, const char blank_char, const std::string& uncover)
    {
      std::string::size_type i = 0;
      int changes = 0;
      while (i != std::string::npos)
      {
        i = word.find_first_not_of(uncover, i);
        if (i == std::string::npos)
        {
          break;
        }
        word[i++] = blank_char;
        ++changes;
      }
      return changes;
    }
    
    bool endGame(const std::string& word, const std::string& message, const int level)
    {
      display(word, level);
      std::cout << message << std::endl;
      std::cout << "Enter . to quit, other input to continue" << std::endl;
      return (getCh() == '.');
    }
    
    int main(int argc, char** argv)
    {
      time_t tm;
      std::time(&tm);
      std::srand(static_cast<unsigned int>(tm));
      bool quit = false;
      do 
      {
        int level = 0;
        std::string word = getNextWord();
        std::string blanked = word;
        std::string used = "";
        blankWord(blanked, '_', used);
        while (level < LEVELS)
        {
          display(blanked, level);
          std::cout << "Enter a letter, . to quit." << std::endl;
          char ch = getCh();
          std::cout << "Your input was: " << ch << std::endl;
          if (ch == '.')
          {
            quit = true;
            break;
          }
          if (ch >= 'A' && ch <= 'Z')
          {
            if (used.find_first_of(ch) != std::string::npos)
            {
              std::cout << "You have already used that letter" << std::endl;
              continue;
            }
            used += ch;
    
            if (word.find_first_of(ch) != std::string::npos)
            {
              blanked = word;
              if (!blankWord(blanked, '_', used))
              {
                quit = endGame(blanked, "You won! Congratulations!", level);
                break;
              }
            }
            else
            {
              if (++level == LEVELS)
              {
                quit = endGame(word, "You lost!", level);
                break;
              }
            }
          }
        }
    
      } while (!quit); 
    
      return 0;
    } 
    
    
    
  3. […] today a task that was more practical engineering than theoretical science. Write a game of Hangman. The language is C++ although, for the sake of simplicity, I avoided classes and algorithms. […]

  4. Mike said

    Python 3 version with ascii graphics.

    Uses wordlist from 12dicts, which can be found at http://wordlist.sourceforge.net/

    from operator import methodcaller
    from random import randrange
    from string import ascii_lowercase
    
    def random_word():
        with open("12dicts/2of12inf.txt", "rt") as wordlist:
            for n, word in enumerate(wordlist, 1):
                if not randrange(n):
                    pick = word
    
        return pick.strip(' %\n')
    
    def game():
        game_word = random_word()
        letters = set(game_word)
    
        word_display = ' '.join(map('{{{}}}'.format, game_word))
    
        game_display = '''
             +--+     used: {a} {b} {c} {d} {e} {f} {g} {h} {i}
             |  |           {j} {k} {l} {m} {n} {o} {p} {q} {r}
             |  {hd}           {s} {t} {u} {v} {w} {x} {y} {z}
             | {la}{bd}{ra}      
             | {ll} {rl}    word: ''' + word_display  + '''
             |
            ========
            '''
    
        bodyparts = [('hd', 'O'), ('bd', '|'), ('la', '/'),
                     ('ra', '\\'), ('ll', '/'), ('rl', '\\')]
    
    
        status = {c:'_' for c in ascii_lowercase}
        status.update((p,' ') for p,_ in bodyparts)
    
        while letters and bodyparts:
            print(game_display.format(**status))
            
            guess = input('\nEnter a letter: ').strip()
            
            status[guess] = guess
    
            if guess in letters:
                letters.remove(guess)
                
            else:
                part,char = bodyparts.pop(0)
                status[part] = char
            
        else:
            print(game_display.format(**status))
            
            if bodyparts:
                print("You win!")
            else:
                print("Sorry, you lose!  The word was", game_word)
    
    play = 'y'
    while play == 'y':
        game()
    
        play = input('\nPlay again? ')[0].strip().lower()
    
    print('Thanks for playing!')
    
    
  5. Diego Giuliani said

    My attempt using python. It uses the description of the exercise as a source of words, but this can be easily changed.

    
    from random import randint
    import re
    
    cad =  """
    The player is given a series of blanks, one per letter of a word to be guessed. 
    At each turn the player guesses a letter; if it is in the word, 
    all occurrences of the letter are filled in the appropriate blank, 
    but if the guess is wrong, another body part - traditionally, the head, torso, 
    two arms and two legs, for a total of six - is added to the gibbet. 
    The player wins by guessing all the letters of the word before the hangman adds all the pieces of his body.
    """
    
    def printLine(word,letters):
        cad = ""
        for i in range(len(word)):
            cad += (word[i] + "." ) if word[i] in letters else "_."
        print cad
        
    def printGibbet(n):
        gibbet = ["head","torso","left Arm","right Arm","left leg","right leg"]
        print ",".join(gibbet[:n])
        
    def addLetters(word,letters,c):
        for i in range(len(word)):        
            if (word[i] == c): 
                letters.insert(i,c)
        return letters
        
    def game():
        # We take a list of words from the first line of text to have something to play with,
        # we could change this to load from a file or hardcode a list of words
        
        pattern = "^(\w{0,20})([;|,|.]*)$"
        word_list = [re.search(pattern,c).group(1) for c in cad.split() if ((len(c) >= 5) & (re.search(pattern,c) != None ))]
        
        word = word_list[randint(0,len(word_list) - 1 ) ]
        n = 0    
        letters = []   
        while True:
            printLine(word,letters)
            c = raw_input("Guess a letter: ")
            if (re.match("^[a-zA-Z]$",c)== None):
                continue
            
            if ((c in word) and ( not c in letters)):
                letters = addLetters(word,letters,c)
            else:
                n +=1
                printGibbet(n)
                if (n == 6) :
                    print "You loose the word was %s" % word
                    return False
                    
            print "".join(letters)    
            if  ("".join(letters)== word ):
                print "You win! the word was %s " % word
                return True
    
  6. Mahesh said

    I have hardcoded the

    class HangMan(object):
    “””
    “””
    def __init__(self):
    “””
    “””
    self.hangman = [‘head’, ‘torso’, ‘leftarm’, ‘rightarm’, ‘leftleg’, ‘rightleg’]
    self.wordsList = [‘head’, ‘torso’, ‘leftarm’, ‘rightarm’, ‘leftleg’, ‘rightleg’]

    def GetNextHangManPart(self):
    “””
    “””
    for word in self.hangman:
    ostr = “Wrong guess! you got %s Try Again!” %(word)
    yield ostr

    def PlayGame(self):
    “””
    “””
    for word in self.wordsList:
    letterCount = 0
    GuessCount = 0
    hangmanGenerator = self.GetNextHangManPart()
    print “New word with %d characters” %(len(word))
    try:
    while(letterCount < len(word)):
    input = raw_input("Guess the %d letter: " %(letterCount+1))
    if word[letterCount] == input:
    print "Good Work"
    letterCount +=1
    else:
    print hangmanGenerator.next()
    except:
    print "You lost! better luck next time"
    print "Game Completed!"

    if __name__ == '__main__':
    hm = HangMan()
    hm.PlayGame()

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: