14
\$\begingroup\$

Inspired by reddit.

Write a program which plays Hangman.

  • The program chooses a random word from a list of N words, where N > 2.
  • The word list can be provided to the program in any way you choose.
  • At each iteration

    • Print out the state of the game using underscores for letters not yet discovered:

    H _ N _ _ _ N

    • Print the number of remaining attempts

    10

    • Read a letter from stdin, and update the state of the game, subtracting an attempt if they guess an incorrect letter.

    A (input)

    H A N _ _ A N

    10

    • Repeat until all letters are guessed or attempts reaches 0
  • Use any language
  • Fewest number of characters wins.
  • Drawing the gallows is not necessary, but will earn you upvotes and kudos.
\$\endgroup\$
  • \$\begingroup\$ Can I let each word in the list be the same number of characters? \$\endgroup\$ – Peter Olson Apr 23 '11 at 15:04
  • \$\begingroup\$ Do the letters in the output have to be separated by spaces? \$\endgroup\$ – Lowjacker Apr 23 '11 at 15:44
  • \$\begingroup\$ @Peter Of The Corn: you should assume that the word list is arbitrary \$\endgroup\$ – drspod Apr 23 '11 at 16:28
  • \$\begingroup\$ @Lowjacker: the spaces improve the legibility of consecutive underscores, otherwise it's hard to count how many letters they represent. \$\endgroup\$ – drspod Apr 23 '11 at 16:29

10 Answers 10

6
\$\begingroup\$

Ruby 1.9, 134 132 120 117 108 107

Word list provided in ARGV. The words and the entered letters must match in case.

r=w=$*.sample
t=10
loop{puts [*w.tr(r,?_).chars]*' ',t
t>0&&r>''?w[l=STDIN.gets[0]]?r=r.tr(l,''):t-=1:exit}
\$\endgroup\$
8
\$\begingroup\$

Darn, I thought it said "fewest number of lines wins." I'm not going to win any fewest-character contests here, but this Common Lisp program is only one line.

(let ((words (list "that" "help" "rent" "chair" "octopus" "monitor" "manual" "speakers" "onomatopoeia" "regardless" "irresponsible" "cornerstone"))) (let ((word (nth (random (length words)) words))) (format t "~a~%" (funcall (defun play (word current remaining-attempts) (progn (if (not (find #\_ current)) (return-from play "You win!")) (if (equalp remaining-attempts 0) (return-from play "You lose!")) (format t "~a~%~d~%" current remaining-attempts) (let ((guess (char (read-line) 0)) (index 0) (found nil)) (loop for letter across word do (if (equalp guess letter) (progn (setf (char current index) letter) (setf found t))) (setf   index (+ index 1))) (if found (play word current remaining-attempts) (play word current (- remaining-attempts 1)))))) word (map 'string #'(lambda (c) #\_) word) 10))))
\$\endgroup\$
  • 1
    \$\begingroup\$ I'm upvoting you because I'm pretty sure you're being intentionally humorous :-) \$\endgroup\$ – Dr. Pain Apr 29 '11 at 19:25
8
\$\begingroup\$

Python 3.

from random,sys import *
w=choice(*argv)
L=set(w)
a=10
while L and a:
 print(" ".join("_"if x in L else x for x in w),a)
 try:L-=set(input()[0])
 except:a-=1

I prefer this one though: longer but nicer.

import random
w=random.choice(list(open("/usr/dict/words")))[:-1]
L=set(w)
a=10
while L and a:
 print(" ".join("_"if x in L else x for x in w),a)
 try:L.remove(input()[0])
 except:a-=1
print w
\$\endgroup\$
  • \$\begingroup\$ If I didn't have to print a as well, I could use the * twice: print(*("_"if x in L else x for x in w)) \$\endgroup\$ – badp Apr 28 '11 at 10:28
4
\$\begingroup\$

c++ (-headers)

struct h{h(char a):b(a){}char operator()(char c,char d){return d!='_'?d:c==b?c:'_';}char b;};

int main(int a,char**b){
srand(time(0));string c=*(b+rand()%(a-1)+1),d(c.size(),'_'),e;
char f=10,g;
while(f){
cout<<"> ";cin>>g;e=d;
transform(c.begin(),c.end(),d.begin(),d.begin(),h(g));if(e==d)--f;
cout<<d<<endl<<(int)f<<endl;if(d==c)break;
}return 0;}

cat /usr/dict/words | xargs hangman

\$\endgroup\$
  • \$\begingroup\$ The "> " prompt for input is not necessary for the solution, I just added it in the question to indicate that it was input, as many languages provide a prompt such as this. \$\endgroup\$ – drspod Apr 23 '11 at 16:54
  • \$\begingroup\$ @drspod You should edit the question to reflect that, then. \$\endgroup\$ – Lowjacker Apr 23 '11 at 19:24
  • \$\begingroup\$ edited to clarify \$\endgroup\$ – drspod Apr 23 '11 at 22:49
2
\$\begingroup\$

Python

import random

DEFAULT_ATTEMPTS = 10

def print_word(word, uncovered):
    for c in word:
        if c not in uncovered:
            c = '_'
        print c,
    print ''

def get_letter():
    letter = None
    while letter is None:
        letter = raw_input('> ')
        if len(letter) != 1:
            print 'Letters must be 1 character.  Try again.'
            letter = None
    return letter

if __name__ == '__main__':
    import sys

    if len(sys.argv) != 2: sys.exit(1)
    with open(sys.argv[1], 'r') as f:
        words = [word.strip() for word in f.readlines() if word.strip()]

    word = random.choice(words)
    uncovered = set([' '])
    attempts = DEFAULT_ATTEMPTS

    while attempts > 0 and any(letter not in uncovered for letter in word):
        print_word(word, uncovered)
        print attempts

        letter = get_letter()
        if letter in uncovered:
            print 'You have already tried that letter.'
        elif letter in word:
            print 'You got it!'
        else:
            print 'Wrong!'
            attempts -= 1

        uncovered.add(letter)

    if attempts == 0:
        print 'You lose!',
    else:
        print 'You win!'
    print 'The phrase was "%s".' % word

I didn't really try for the fewest characters, just wanted to make it as small as possible without sacrificing anything.

\$\endgroup\$
  • \$\begingroup\$ @user: You may be interested in George Edison's wonderful user script for this site which puts your code (as copied here by badp) at 1225 characters. \$\endgroup\$ – dmckee Apr 23 '11 at 14:36
  • \$\begingroup\$ I think that's cause I was using tabs and they were converted to spaces here. wc says it's 1034 with tabs. \$\endgroup\$ – Sergey G Apr 23 '11 at 14:43
  • \$\begingroup\$ @user: Yes. A well known difficulty with python submissions. \$\endgroup\$ – dmckee Apr 23 '11 at 14:47
2
\$\begingroup\$

Perl, 112 char. I feel like I can do better - perhaps I'll try again later

$_=$ARGV[rand@ARGV];$a=10;while($a&&/[a-z]/){print map/[A-Z]/?$_:'_',split'';$x=<STDIN>;chop$x;s/$x/$x/ig||$a--}

Words are given on the command line, letters typed upper case

\$\endgroup\$
  • \$\begingroup\$ Down to 107 $_=$ARGV[rand@ARGV];$a=10;while($a&&/[a-z]/){$y=$_;$y=~y/a-z/_/;print$y;$x=<STDIN>;chop$x;s/$x/$x/ig||$a--} \$\endgroup\$ – user1379 Apr 23 '11 at 21:54
  • 3
    \$\begingroup\$ You could just edit your original answer. \$\endgroup\$ – Lowjacker Apr 23 '11 at 22:03
  • 1
    \$\begingroup\$ This doesn't display the number of remaining attempts. \$\endgroup\$ – Lowjacker Apr 23 '11 at 22:27
2
\$\begingroup\$

Clojure

This is 400 bytes gzipped, which is still quite a lot, probably because of how Clojure handles mutable state.

(def m ["will" "work" "for" "food"])
(def w (nth m (rand-int (count m))))
(def *s* (atom (replicate (count w) "_")))
(def *a* (atom 10))

(defn g [s a]
  (str (apply str (interpose " " s)) "\n" a))

(loop [n (read-line)]
  (if (some (set n) w)
    (swap! *s* (fn [s]
                 (map 
                   (fn [i]
                     (if (= n (str (nth w i)))
                       n
                       (nth s i)))
                   (range 0 (count s)))))
    (swap! *a* dec))

  (println (g (deref *s*) (deref *a*))) 

  (if (and (< 0 (deref *a*)) (some #{"_"} (deref *s*)))
    (recur (read-line))))
\$\endgroup\$
2
\$\begingroup\$

C# 370

using System;namespace h{class P{static void Main(string[]a){int c=10,d,l;char y=' ';string w=a[new Random().Next(a.Length)];l=w.Length;char[]x=new char[l];for(d=-1;++d<l;x[d]='-');while(c>0){for(d=-1;++d<l;x[d]=(y==w[d]||x[d]!='-')?w[d]:x[d]);Console.WriteLine(new string(x)+" "+c);if(w==new string(x))return;y=Console.ReadKey(true).KeyChar;if(!w.Contains(y+""))c--;}}}

wordlist as argument

\$\endgroup\$
1
\$\begingroup\$

VB.NET


I haven't tried shrinking it yet, but:
First shrinking:
Second shrinking (3759 characters):

Module Hangman
    Sub Main()
        Dim m As Int32, w = "banana|apple|pear|dog|cat|orange|monkey|programming|hangman".Split("|")(New Random().Next(9)), g = "", e = "", x, c As Char, f As Boolean, r = Sub(z) Console.Write(z), p = Sub(y, h) Console.SetCursorPosition(y, h), a = Sub() Console.Clear(), q = Function() Console.ReadKey(1), d = Sub()
                                                                                                                                                                                                                                                                                                                          r("       +--------+S       |        |S                |S                |S                |S                |S                |S                |S                |S                |S                |S                |S                |S                |S                |S                |S                |S                |S                |S                |S                |S                |S                |S   ---------------------".Replace("S", vbCrLf))
                                                                                                                                                                                                                                                                                                                          p(0, 2)
                                                                                                                                                                                                                                                                                                                          r(String.Join(vbCrLf, "    /------\S    | O   O|S    \  ... /S     ------ S        |   S        |   S        |   S        |   S -------+-------S        |   S        |   S        |   S       / \  S      /   \  S     /     \  S    /       \  ".Split("S").Take(m * 4)))
                                                                                                                                                                                                                                                                                                                      End Sub
        Console.CursorVisible = 0
        Do
            a()
            d()
            p(30, 10)
            f = 0
            For Each x In w
                If g.Contains(x) Then
                    r(x)
                Else
                    r(" ")
                    f = 1
                End If
                Console.CursorTop += 1
                Console.CursorLeft -= 1
                r("_")
                Console.CursorTop -= 1
                r(" ")
            Next
            If Not f Then
                a()
                d()
                p(30, 10)
                r("You win! Press any key to close.")
                q()
                End
            End If
            p(30, 13)
            r(e)
            Do
                c = q().KeyChar
            Loop Until Char.IsLetter(c)
            If g.Contains(c) Then
                e = "You have already guessed that letter."
            Else
                g &= c
                If w.Contains(c) Then
                    e = "There is a" & If("aehilmnorsx".Contains(c), "n", "") & " """ & c & """ in the word."
                Else
                    e = "There is no """ & c & """ in the word. Try again."
                    m += 1
                End If
            End If
        Loop Until m = 4
        a()
        d()
        p(30, 10)
        r("You lose! Press any key to close.")
        q()
    End Sub
End Module
\$\endgroup\$
  • \$\begingroup\$ Is all the indentation really required? \$\endgroup\$ – Lowjacker Apr 24 '11 at 0:19
  • \$\begingroup\$ it makes it easier to read, doesn't it? \$\endgroup\$ – Nate Koppenhaver Apr 24 '11 at 0:33
  • \$\begingroup\$ As in blocks? No, it's not required, but I don't count it as characters. \$\endgroup\$ – Ry- Apr 24 '11 at 2:32
0
\$\begingroup\$

Powershell, 125 bytes

$w=$h=$args|random|% t*y
for($n=10){$w-replace"[ $h]",'_'-join' ';$n
if(!$h+!$n){break}$n-=($c=Read-Host)-notin$h
$h=$h-ne$c}

Less golfed test script:

$f = {

$word=$hidden=$args|random|% toCharArray    # let $word and $hidden are a random word chars
#$word                                      # uncomment this to cheating
for($n=10){                                 # forever for with init section
    $word-replace"[ $hidden]",'_'-join' '   # display the word with hidden letters
    $n                                      # display $n
    if(!$hidden+!$n){break}                 # break loop if hidden array is empty or n equal to 0
    $n-=($c=Read-Host)-notin$hidden         # input $c from user, decrease $n if $c does not in $hidden array
    $hidden=$hidden-ne$c                    # recreate $hidden array with removed $c
}

}

$words = gc .\wordlist.txt
&$f $words

Output example when the guessing player has lost:

_ _ _ _ _ _ _ _
10
i
_ _ _ _ _ _ _ _
9
e
_ _ e _ _ _ _ e
9
o
o _ e _ _ o _ e
9
a
o _ e _ _ o _ e
8
q
o _ e _ _ o _ e
7
q
o _ e _ _ o _ e
6
q
o _ e _ _ o _ e
5
q
o _ e _ _ o _ e
4
q
o _ e _ _ o _ e
3
q
o _ e _ _ o _ e
2
q
o _ e _ _ o _ e
1
q
o _ e _ _ o _ e
0

Output example when the guessing player has win:

_ _ _ _ _ _ _ _ _ _
10
e
_ _ _ _ e _ _ _ _ _
10
o
_ o _ _ e _ _ _ _ _
10
i
_ o _ _ e _ _ i _ _
10
a
_ o _ _ e _ _ i a _
10
l
_ o _ _ e _ _ i a l
10
c
c o _ _ e _ c i a l
10
m
c o m m e _ c i a l
10
t
c o m m e _ c i a l
9
r
c o m m e r c i a l
9
\$\endgroup\$

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.