commit 22d4e961b55e934d6f1b04e898cdb3c21d37e41a
parent 73d50ec8871b9cfbb95b75338ae590d78bd91db7
Author: Samir Parikh <noreply@samirparikh.com>
Date: Fri, 25 Feb 2022 21:51:08 +0000
add message authentication check to cookie
Diffstat:
A | guess_number3.cgi | | | 199 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 199 insertions(+), 0 deletions(-)
diff --git a/guess_number3.cgi b/guess_number3.cgi
@@ -0,0 +1,199 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+use CGI;
+use CGI::Cookie ();
+
+use constant TRIES => 6;
+use constant COOKIE_NAME => 'guess_number_2';
+my $game = CGI->new;
+
+# state maintenance subroutines
+sub get_state {
+ my %state = $game->cookie( COOKIE_NAME );
+ return undef unless %state;
+ return \%state;
+}
+
+sub initialize {
+ my $state = shift;
+ $state = {} unless $state;
+ $state->{NUMBER} = int( rand( 99 ) ) + 1;
+ $state->{GAMENO} += 1;
+ $state->{GUESSES_LEFT} = TRIES;
+ $state->{WON} += 0;
+ $state->{GUESSED} = '';
+ return $state;
+}
+
+sub save_state {
+ my $state = shift;
+ return CGI::Cookie->new (
+ -name => COOKIE_NAME,
+ -value => $state,
+ -expires => '+1M',
+ );
+}
+
+# game play logic
+sub process_guess {
+ my ( $guess, $state ) = @_;
+
+ # process list of previous guesses
+ my %guessed = map { $_ => 1 } split /,/, $state->{GUESSED};
+
+ # do nothing if user did not enter a guess
+ return ('', 'continue') unless $guess;
+
+ # ensure guess is a valid number between 1 and 100
+ return ("Not a valid guess.", 'error')
+ unless $guess =~ m/^[1-9][0-9]?$|^100$/;
+
+ # check if user already entered this guess previously
+ return ("You already guessed that number.", 'error')
+ if $guessed{ $guess };
+
+ # we have a valid guess. update list of guesses
+ $guessed{ $guess } = 1;
+ $state->{GUESSED} = join ',', sort keys %guessed;
+
+ $state->{GUESSES_LEFT}--;
+
+ # the order of the checks is important here. At this point, it's
+ # possible that the player has used their last guess. Therefore,
+ # we first check whether the guess is correct
+
+ # guess is correct!
+ if ( $guess == $state->{NUMBER} ) {
+ $state->{WON}++;
+ return ("Congratulations! You guessed that the correct number
+ was $state->{NUMBER}!", 'won');
+ }
+
+ # if the guess is not correct AND we are out of guesses...
+ return ("Sorry, you ran out of turns. The number was $state->{NUMBER}.",
+ 'lost') unless ($state->{GUESSES_LEFT} && $guess != $state->{NUMBER});
+
+ # otherwise...
+ # guess too low
+ return ("Your guess is too low.", 'continue')
+ if ( $guess < $state->{NUMBER} );
+
+ # ...or
+ # guess is too high
+ return ("Your guess is too high.", 'continue')
+ if ( $guess > $state->{NUMBER} );
+
+}
+
+# html generation subroutines
+sub print_header {
+ my $state = shift;
+ print $game->header(
+ -type => "text/html",
+ -charset => 'utf-8',
+ -cookie => save_state( $state ),
+ );
+ print $game->start_html(
+ -title => "Guess a Secret Number!",
+ -onLoad => 'if (document.gf) document.gf.guess.focus()'
+ );
+ print $game->h1( 'Guess a Secret Number Interactive Game' );
+}
+
+sub print_status {
+ my ( $message, $state ) = @_;
+
+ # draw table
+ print
+ $game->table( { -border=>"0", -width=>"50%" },
+ $game->Tr(
+ $game->td( $game->b("Game #: "), $state->{GAMENO} ),
+ $game->td( $game->b("Wins: "), $state->{WON} )
+ ),
+ $game->Tr(
+ $game->td( $game->b("Guesses Left: "), $state->{GUESSES_LEFT} ),
+ $game->td( $game->b("Numbers Guessed: "), $state->{GUESSED} )
+ )
+ );
+ print $game->h2( $game->font( {-color => 'red'}, $message ))
+ if $message;
+}
+
+sub show_restart_form {
+ my $state = shift;
+ print $game->start_form();
+ print $game->p(
+ #-style => 'padding-right: 25px',
+ 'Do you want to play again?',
+ );
+ print $game->submit (
+ -name => 'restart',
+ -value => 'Play again',
+ #-style => 'padding-left: 25px; padding-right: 25px',
+ );
+ print $game->checkbox (
+ -name => 'clear',
+ -label => 'Clear history',
+ );
+ print $game->end_form();
+}
+
+sub show_guess_form {
+ my $state = shift;
+ print $game->start_form( -name => 'gf' );
+ print "Your guess: ";
+ print $game->textfield (
+ -inputmode => 'numeric', # force number pad on mobile
+ -pattern => '[0-9]*', # force number pad on mobile
+ -name => 'guess',
+ -value => '',
+ -override => 1
+ );
+ print $game->submit (
+ -name => '_guess',
+ -value => 'Guess'
+ );
+ print $game->end_form();
+}
+
+sub print_footer {
+ print $game->hr;
+ print $game->p(
+ 'inspired by Hangman game from ',
+ $game->a(
+ { -href => "https://www.oreilly.com/library/view/writing-apache-modules/156592567X/"},
+ $game->i( 'Writing Apache Modules with Perl and C')
+ ),
+ ' by Lincoln Stein and Doug MacEachern.'
+ );
+ print $game->end_html();
+}
+
+# begin main program
+# retrieve current state
+my $state = get_state() unless $game->param( 'clear' );
+
+# initialize state if we are starting from the beginning (!$state)
+# or if we are playing another game ($game->param( 'restart' )).
+if ( !$state || $game->param( 'restart' ) ) {
+ $state = initialize( $state );
+}
+
+# process current guess, if any
+my ( $message, $status ) =
+ process_guess( $game->param( 'guess' ) || '', $state );
+
+# generate the HTML page
+print_header( $state );
+print_status( $message, $state );
+
+# prompt user
+if ( $status =~ /^(won|lost)$/ ) {
+ show_restart_form( $state );
+} else {
+ show_guess_form( $state );
+}
+
+print_footer();