Hangman
December 20, 2011
We’ll assume a plain ascii VT-100 terminal that responds to the following commands:
(define esc (integer->char 27))
(define (cls) (for-each display `(,esc #\[ #\2 #\J)))
(define (goto r c) (for-each display `(,esc #\[ ,r #\; ,c #\H)))
(define (erase-eol) (for-each display `(,esc #\[ #\K)))
Our game looks like this:
0----+----1----+----2----+----3
|
2 +----+
| | |
4 | O YOU xxx!!!
| | \|/
6 | / \
| |
8 +-------
|
10 _ _ _ _ _ _ _ _ _ _ _ _ _
|
12 abcdefghijklmnopqrstuvwxyz
| Play again (y/n)? X
Simple functions display the gibbet, the current state of the word being guessed, the remaining letters of the alphabet, and the answer:
(define (display-gibbet)
(cls)
(goto 2 5) (display "+----+")
(goto 3 5) (display "| |")
(goto 4 5) (display "|")
(goto 5 5) (display "|")
(goto 6 5) (display "|")
(goto 7 5) (display "|")
(goto 8 5) (display "+-------"))
(define (display-word word)
(goto 10 4) (erase-eol)
(for-each (lambda (c) (display #\space) (display c)) word))
(define (display-alphabet alphabet)
(goto 12 5) (erase-eol)
(for-each display alphabet))
(define (display-answer answer)
(goto 5 19) (erase-eol)
(for-each (lambda (c) (display (char-upcase c))) answer))
Displaying the win-or-die message at the end of the game is a bit more involved because that function also asks if the player want to play another game:
(define (display-message win? answer)
(goto 4 19)
(if win? (display "YOU WIN!!!")
(begin (display "YOU DIE!!!")
(display-answer answer)))
(goto 13 5) (display "Play again (y/n)? ")
(let loop ((c (get-key)))
(cond ((char=? c #\y) #t)
((char=? c #\n) #f)
(else (goto 13 24) (erase-eol)
(loop (get-key))))))
Get-key and get-letter fetch input from the player:
(define (get-key) (char-downcase (read-char)))
(define (get-letter alphabet)
(goto 4 19) (erase-eol)
(let loop ((c (get-key)))
(cond ((member c alphabet) c)
(else (goto 4 19) (erase-eol) (loop (get-key))))))
Let’s look now at the top-level program:
(define (hangman)
(rand (time-second (current-time)))
(let ((words (read-words "/usr/share/dict/words")))
(let play ((answer (fortune words)))
(display-gibbet)
(let loop ((man 0) (word (make-list (length answer) #\_))
(alphabet (string->list "abcdefghijklmnopqrstuvwxyz")))
(display-man man) (display-word word) (display-alphabet alphabet)
(cond ((not (member #\_ word))
(when (display-message #t answer) (play (fortune words))))
((= 6 man)
(when (display-message #f answer) (play (fortune words))))
(else (let ((c (get-letter alphabet)))
(if (member c answer)
(loop man (insert answer c word) (remove c alphabet))
(loop (+ man 1) word (remove c alphabet))))))))))
The program begins by seeding the random number generator; the method used here is specific to Chez Scheme. After reading the word list, the program enters the play
loop, which chooses a word and plays a single game. For each game the gibbet is displayed, then the program enters a loop
that asks the player for a letter and processes it, as appropriate, until the game is over, updating the display before each letter. Hangman
calls read-words
to get the wordlist and insert
to replace the blanks with a correctly-guessed letter:
(define (read-words filename)
(with-input-from-file filename (lambda ()
(let loop ((word (read-line)) (words (list)))
(if (eof-object? word) (reverse words)
(let ((word (map char-downcase (string->list word))))
(if (all? char-alphabetic? word)
(loop (read-line) (cons word words))
(loop (read-line) words))))))))
(define (insert answer c word)
(let loop ((word word) (answer answer) (new (list)))
(if (null? word) (reverse new)
(loop (cdr word) (cdr answer)
(cons (if (char=? c (car answer))
(char-upcase c) (car word)) new)))))
We used filter
, all?
, read-line
, rand
and fortune
from the Standard Prelude. You can see the complete program at http://programmingpraxis.codepad.org/sNnrfd58.
Not much to see here.
C++ solution, slightly less interactive and rather longer. Procedural, nothing more fancy than a string.
[…] 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. […]
Python 3 version with ascii graphics.
Uses wordlist from 12dicts, which can be found at http://wordlist.sourceforge.net/
My attempt using python. It uses the description of the exercise as a source of words, but this can be easily changed.
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()