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 }