http://www.perlmonks.org?node_id=42055
Category: Fun Stuff
Author/Contact Info japhy
Description: This is a hangman game. It's not too frilly, but it's a nice efficient layout. Version 2 will be out soon, with a better data structure for the word list...
#!/usr/bin/perl -ws

use strict;
use vars qw( $h $words $g );

my @words;

usage() if $h;
build_word_list($words);
$g ||= 10;


while (1) {
  my $len = length(my $word = randword());
  my $freq = dissect($word);
  my @state = ("_") x $len;
  my ($left, %guessed) = 10;

  print "The word is $len letters long.\n";

  while (%$freq and $left) {
    printf "%d wrong guess%s left.\n", $left, $left > 1 && "es";
    print @state, "\n";
    print "Guessed: @{[ sort keys %guessed ]}\n";

    print "Next Letter: ";
    my $c = substr <STDIN>, 0, 1;
    print("You already guessed '$c'"), next if $guessed{$c}++;

    if (my $pos = delete $freq->{$c}) { @state[@$pos] = ($c) x @$pos }
    else { $left--, print "'$c' not found" }
  }
  continue { print "\n\n" }

  if (%$freq) { print "Too late -- the word was '$word'." }
  else { printf "You got it in %d guesses!", scalar keys %guessed }

  print "\n\n";
  print "Play again ([y]/n): ";
  last if lc substr(<STDIN>, 0, 1) !~ /^y?$/;
  print "\n\n";
}


sub usage {
  print << "USAGE";
$0 [-h] [-words=file] [-g=num]

  -h           display this usage note
  -words=file  the file to use as a word list (def: .hm-words)
  -g=num       the maximum number of wrong guesses (def: 10)

USAGE
  exit;
}


sub build_word_list {
  for my $file (@_, ".hm-words") {
    next if not $file;
    my $i = 0;
    open WORDLIST, $file or warn("file $file not readable: $!\n"), nex
+t;
    chomp, push(@words,$_) while <WORDLIST>;
    close WORDLIST;
    return;
  }
  die "no wordlist found!  make an .hm-words file!\n";
}


# XXX:  slow to splice towards the beginning of a LARGE list
#       to be replaced by a hash/linked-list implementation
sub randword () { splice @words, rand @words, 1 }


sub dissect {
  my ($i, %freq);
  push @{ $freq{$_} }, $i++ for split //, shift;
  return \%freq;
}