Hangman wordgame golf

14

4

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.

drspod

Posted 2011-04-23T12:54:32.793

Reputation: 323

Can I let each word in the list be the same number of characters? – Peter Olson – 2011-04-23T15:04:41.133

Do the letters in the output have to be separated by spaces? – Lowjacker – 2011-04-23T15:44:09.343

@Peter Of The Corn: you should assume that the word list is arbitrary – drspod – 2011-04-23T16:28:22.993

@Lowjacker: the spaces improve the legibility of consecutive underscores, otherwise it's hard to count how many letters they represent. – drspod – 2011-04-23T16:29:46.817

Answers

6

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}

Lowjacker

Posted 2011-04-23T12:54:32.793

Reputation: 4 466

8

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

badp

Posted 2011-04-23T12:54:32.793

Reputation: 184

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)) – badp – 2011-04-28T10:28:50.330

8

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))))

Michael Dickens

Posted 2011-04-23T12:54:32.793

Reputation: 181

1I'm upvoting you because I'm pretty sure you're being intentionally humorous :-) – Dr. Pain – 2011-04-29T19:25:59.937

4

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

user324234sdf

Posted 2011-04-23T12:54:32.793

Reputation: 41

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. – drspod – 2011-04-23T16:54:21.873

@drspod You should edit the question to reflect that, then. – Lowjacker – 2011-04-23T19:24:58.700

edited to clarify – drspod – 2011-04-23T22:49:48.007

2

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.

Sergey G

Posted 2011-04-23T12:54:32.793

Reputation: 121

@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.

– dmckee --- ex-moderator kitten – 2011-04-23T14:36:50.297

I think that's cause I was using tabs and they were converted to spaces here. wc says it's 1034 with tabs. – Sergey G – 2011-04-23T14:43:35.520

@user: Yes. A well known difficulty with python submissions.

– dmckee --- ex-moderator kitten – 2011-04-23T14:47:41.540

2

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

user1379

Posted 2011-04-23T12:54:32.793

Reputation:

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--} – None – 2011-04-23T21:54:04.460

3You could just edit your original answer. – Lowjacker – 2011-04-23T22:03:26.630

1This doesn't display the number of remaining attempts. – Lowjacker – 2011-04-23T22:27:05.370

2

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))))

jhuni

Posted 2011-04-23T12:54:32.793

Reputation: 121

2

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

matt

Posted 2011-04-23T12:54:32.793

Reputation: 21

1

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

Ry-

Posted 2011-04-23T12:54:32.793

Reputation: 5 283

Is all the indentation really required? – Lowjacker – 2011-04-24T00:19:49.083

it makes it easier to read, doesn't it? – Nate Koppenhaver – 2011-04-24T00:33:45.617

As in blocks? No, it's not required, but I don't count it as characters. – Ry- – 2011-04-24T02:32:18.450

0

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

mazzy

Posted 2011-04-23T12:54:32.793

Reputation: 4 832