cgi-dev

Repository that serves as my CGI "scratchpad" to try things out.
git clone git://git.samirparikh.com/cgi-dev
Log | Files | Refs | README

hangman2.cgi (6098B) - raw


      1 #!/usr/bin/perl
      2 
      3 # file: hangman2.cgi
      4 # hangman game using simple cookie maintain state
      5 
      6 use IO::File();
      7 use CGI qw( :standard );
      8 use CGI::Cookie ();
      9 use warnings;
     10 use strict;
     11 
     12 use constant WORDS => '/usr/share/dict/hangman_words';
     13 use constant TRIES => 6;
     14 use constant COOKIE_NAME => 'Hangman';
     15 
     16 # retrieve the state
     17 #my $state = get_state(); # $state is a hash reference
     18 my $state = get_state() unless param('clear');
     19 
     20 # reinitialize if we need to
     21 $state    = initialize($state) if !$state or param('restart');
     22 
     23 # process the current guess, if any
     24 my ($message, $status) = process_guess(param('guess') || '', $state);
     25 
     26 # generate the HTML page
     27 print   header(-charset    => 'utf-8',
     28                -cookie => save_state($state)
     29               ),
     30         start_html( -Title   => 'Hangman 1',
     31                     -bgcolor => 'white',
     32                     -onLoad  => 'if (document.gf) document.gf.guess.focus()'),
     33         h1( 'Hangman 1 Interactive Game' );
     34 
     35 # draw the statistics
     36 status( $message, $state );
     37 
     38 # prompt the user to restart or to enter the next guess
     39 if ( $status =~ /^(won|lost)$/ ) {
     40     show_restart_form($state);
     41 } else {
     42     show_guess_form($state);
     43 }
     44 print   hr,
     45         a( {-href => '/'}, "Home"),
     46         p( cite( {-style => "fontsize: 10pt"},
     47                  'graphics courtesy of Andy Wardley')),
     48         end_html();
     49 
     50 # This is called to initialize a whole new state object
     51 # or to create a new game.
     52 sub initialize {
     53     my $state = shift;
     54     $state = {} unless $state;
     55     $state->{WORD}              = pick_random_word();
     56     $state->{GUESSES_LEFT}      = TRIES;
     57     $state->{GUESSED}           = ''; # empty hash reference
     58     $state->{GAMENO}            += 1;
     59     $state->{WON}               += 0;
     60     $state->{TOTAL}             += 0;
     61     return $state;
     62 }
     63 
     64 #sub save_state {
     65 #    my $state = shift;
     66 #    foreach (qw(WORD GAMENO GUESSES_LEFT WON TOTAL GUESSED)) {
     67 #        print hidden(
     68 #                -name => $_, -value=> $state->{$_}, -override => 1
     69 #        );
     70 #    }
     71 #}
     72 
     73 sub save_state {
     74     my $state = shift;
     75     return CGI::Cookie->new (
     76         -name   => COOKIE_NAME,
     77         -value  => $state,
     78         -expires => '+1M'
     79     );
     80 }
     81 
     82 #sub get_state {
     83 #    return undef unless param();
     84 #    my $state = {};
     85 #    foreach (qw(WORD GAMENO GUESSES_LEFT WON TOTAL GUESSED)) {
     86 #        $state->{$_} = param($_);
     87 #    }
     88 #    return $state;
     89 #}
     90 
     91 sub get_state {
     92     my %cookie = cookie( COOKIE_NAME );
     93     return undef unless %cookie;
     94     return \%cookie;
     95 }
     96 
     97 sub show_guess_form {
     98    my $state = shift;
     99    print start_form(-name => 'gf'),
    100          "Your guess: ",
    101          textfield(-name => 'guess', -value => '', -override => 1),
    102          submit(-value => 'Guess');
    103    #save_state($state);
    104    print end_form;
    105 }
    106 
    107 sub show_restart_form {
    108    my $state = shift;
    109    print start_form,
    110          "Do you want to play again?",
    111          submit(-name => 'restart',
    112                 -value => 'Another game'),
    113          checkbox(-name => 'clear', -label => 'Clear history');
    114    delete $state->{WORD};
    115    #save_state($state);
    116    print end_form;
    117 }
    118 
    119 sub process_guess { # returns message and status
    120     my ( $guess, $state ) = @_;
    121 
    122     # lose immediately if user has no more guesses left
    123     #return ('', 'lost') unless $state->{GUESSES_LEFT} > 0;
    124     #return ('', 'lost') unless $state->{GUESSES_LEFT};
    125 
    126     my %guessed = map { $_ => 1 } $state->{GUESSED} =~ /(.)/g;
    127     my %letters = map { $_ => 1 } $state->{WORD}    =~ /(.)/g;
    128 
    129     # return immediately  if user has already guessed the word
    130     return ('', 'won') unless grep(!$guessed{$_}, keys %letters);
    131 
    132     # do nothing more if no guess
    133     return ('', 'continue') unless $guess;
    134 
    135     # this section processes individual letter guesses
    136     $guess = lc $guess;
    137     return ("Not a valid letter or word", 'error')
    138         unless $guess =~ /^[a-z]+$/;
    139     return ("You already guessed that letter", 'error')
    140         if $guessed{$guess};
    141 
    142     # this section is called when the user guesses the whole word
    143     if (length($guess) > 1 and $guess ne $state->{WORD}) {
    144         $state->{TOTAL} += $state->{GUESSES_LEFT};
    145         return (qq(You lose. The word was "$state->{WORD}".), 'lost');
    146     }
    147 
    148     # update the list of guesses
    149     $guessed{$_}++ foreach ($guess =~ /(.)/g);
    150     $state->{GUESSED} = join '', sort keys %guessed;
    151 
    152     # correct guess -- word completely filled in
    153     unless (grep(!$guessed{$_}, keys %letters)) {
    154         $state->{WON}++;
    155         return (qq(You won! The word was "$state->{WORD}".), 'won');
    156     }
    157 
    158     # incorrect guess
    159     if (!$letters{$guess}) {
    160         $state->{TOTAL}++;
    161         $state->{GUESSES_LEFT}--;
    162         # user out of turns
    163         return (qq(Sorry, you're out of turns. The word was "$state->{WORD}".),
    164             'lost') if $state->{GUESSES_LEFT} <= 0;
    165         # user still has some turns
    166         return ("Wrong guess", 'continue');
    167     }
    168 
    169     # correct guess but word still incomplete
    170     return ("Good guess.", 'continue');
    171 }
    172 
    173 sub status {
    174     my ( $message, $state ) = @_;
    175 
    176     # print the word with underscores replacing unguessed letters
    177     print table( { -width => '50%' },
    178         TR(
    179             td(b('Word #:'), $state->{GAMENO}),
    180             td(b('Guessed:'), $state->{GUESSED})
    181           ),
    182         TR(
    183             td(b('Won:'), $state->{WON}),
    184             td(b('Current average:'),
    185                 sprintf("%2.3f", $state->{TOTAL}/$state->{GAMENO})),
    186             td(b('Overall average:'),
    187                 $state->{GAMENO} > 1 ?
    188                     sprintf("%2.3f",
    189                         ($state->{TOTAL}-(TRIES-$state->{GUESSES_LEFT}))/
    190                             ($state->{GAMENO}-1))
    191                     : '0.000')
    192            )
    193     );
    194 
    195     my %guessed = map { $_ => 1 } $state->{GUESSED} =~ /(.)/g;
    196     print h2("Word:",
    197         map {$guessed{$_} ? $_ : '_'} $state->{WORD} =~ /(.)/g);
    198     print h2(font({-color => 'red'}, $message)) if $message;
    199 }
    200 
    201 # pick a word, any word
    202 sub pick_random_word {
    203    my $list = IO::File->new(WORDS)
    204       || die "Couldn't open ${\WORDS}: $!\n";
    205    my $word;
    206    rand($.) < 1 && ($word = $_) while <$list>;
    207    chomp $word;
    208    $word;
    209 }