Second Summer 2004


The programs we wrote: Practice Quiz for Midterm Exam

The problem was this.

So I started by writing this program:

<? session_start(); 
   if ($age >= 1 && ! $reset) { 
     if ($sum == $answer) {
       $correct += 1; 
       $message = "Your answer was correct.<p>"; 
     } else {
       $message = "Your answer was NOT correct.<p>"; 
     }
     $attempts += 1; 
     $age += 1; 
     $one = rand(0, 100); 
     $two = rand(0, 100); 
     $sum = $one + $two; 
   } else {
     $correct = 0; 
     session_register("correct"); 
     $attempts = 0; 
     session_register("attempts"); 
     $age = 1; 
     session_register("age"); 
     $message = "Welcome to the game.<p>"; 
     session_register("message"); 
     $one = rand(0, 100); 
     $two = rand(0, 100); 
     $sum = $one + $two; 
     session_register("sum"); 
   } 
   $message .= "What is " . $one . " + " . $two . "?<p>Current state is:" . 
               "(" . $correct . "/" . $attempts . ":" . $age . ")<p>"; 
 ?> 
<html>
  <form>
    <?=$message?>  
    Type your answer here: <input type="text" name="answer"> <p> 
    When done please press <input type="submit" name="proceed" value="Proceed"> <p> 
    <hr> (Or press <input type="submit" name="reset" value="Reset"> to reset the game.) 
  </form> 
</html> 
Then Jesse improved on it (disregard the lack of indentation):
<? session_start(); 
   if (session_is_registered("stamp") && ! $reset) { 
    if($stampCopy == $stamp){    
 if ($sum == $answer) {
       $correct += 1; 
       $message = "Your answer was correct.<p>"; 
     } else {
       $message = "Your answer was NOT correct.<p>"; 
     }
     $attempts += 1;   
     $stamp += 1; 
     $one = rand(0, 100); 
     $two = rand(0, 100); 
     $sum = $one + $two; 
}else{
   $message ="Reloading should give you nothing!<p>";
}
   } else {
     session_destroy();
     session_start();
     $stamp = 1;
     session_register("stamp");
     
     $correct = 0; 
     session_register("correct"); 
     $attempts = 0; 
     session_register("attempts"); 
     $message = "Welcome to the game.<p>"; 
     session_register("message"); 
     $one = rand(0, 100); 
     session_register("one");
     $two = rand(0, 100); 
     session_register("two");
     $sum = $one + $two; 
session_register("sum"); 
   }
 if ($attempts == 10){
     $message.="<b>You finished with a score of " . $correct . "/" .$attempts ."</b><p>";
    $correct =0;
    $stamp = 1;
    $attempts = 0;
}
   $message .= "What is " . $one . " + " . $two . "?<p>Current state is:" 
. 
               "(" . $correct . "/" . $attempts . ":" . $stamp . ")<p>"; 
 ?> 
<html>
  <form>
    <?=$message?>  
    Type your answer here: <input type="text" name="answer"> <p>
    When done please press <input type="submit" name="proceed" 
value="Proceed"> <p>
    <hr> (Or press <input type="submit" name="reset" value="Reset"> to 
reset the game.)
<input type="hidden" name="stampCopy" value="<?=$stamp?>">
  </form>
</html> 
And he provided a client-side version of it:
<? 

   if ($stamp >= 1 && ! $reset) { 

     if ($one + $two == $answer) {
       $correct += 1; 
       $message = "Your answer was correct.<p>"; 
     } else {
       $message = "Your answer was NOT correct.<p>"; 
     }
     $attempts += 1;   
     $stamp += 1; 
     if ($attempts == 10){
       $message .= "<b>You finished with a score of " 
         . $correct . "/" .$attempts . "</b><p>";
       $correct =0;
       $stamp = 1;
       $attempts = 0;
     }
     $one = rand(0, 100); 
     $two = rand(0, 100); 
     $sum = $one + $two; 

   } else {

     $stamp = 1;
     $correct = 0; 
     $attempts = 0; 
     $message = "Welcome to the game.<p>"; 
     $one = rand(0, 100); 
     $two = rand(0, 100); 
     $sum = $one + $two; 

   }

   $message .= "What is " . $one . " + " . $two . 
     "?<p>Current state is:" . "(" . $correct . "/" . 
     $attempts . ":" . $stamp . ")<p>"; 

 ?> 

<html>
  <form>
    <?=$message?>  
    Type your answer here: <input type="text" name="answer"> <p>
    When done please press 
      <input type="submit" name="proceed" value="Proceed"> <p>
    <hr> (Or press <input type="submit" name="reset" value="Reset"> 
      to reset the game.)
    <input type="hidden" name="stamp" value="<?=$stamp?>">
    <input type="hidden" name="correct" value="<?=$correct?>">
    <input type="hidden" name="attempts" value="<?=$attempts?>">
    <input type="hidden" name="one" value="<?=$one?>">
    <input type="hidden" name="two" value="<?=$two?>">
  </form>
</html> 
So I wrote a CGI with hidden fields version of it:
use CGI;
$q = new CGI; 
print $q->header; 
print $q->start_html;

     $stamp = $q->param('stamp');
     $correct = $q->param('correct');
     $attempts = $q->param('attempts');
     $message = $q->param('message'); 
     $one = $q->param('one');
     $two = $q->param('two'); 

$reset = $q->param('reset'); 
$proceed = $q->param('proceed'); 

   if ($stamp >= 1 && ! $reset) { 

     $answer = $q->param('answer'); 

     if ($one + $two == $answer) {
       $correct += 1; 
       $message = "Your answer was correct.<p>"; 
     } else {
       $message = "Your answer was NOT correct.<p>"; 
     }
     $attempts += 1;   
     $stamp += 1; 
     if ($attempts == 10){
       $message .= "<b>You finished with a score of " 
         . $correct . "/" .$attempts . "</b><p>";
       $correct =0;
       $stamp = 1;
       $attempts = 0;
     }

     $one = int(rand(100)); 
     $two = int(rand(100)); 

   } else {

     $stamp = 1;
     $correct = 0; 
     $attempts = 0; 
     $message = "Welcome to the game.<p>"; 
     $one = int(rand(100)); 
     $two = int(rand(100)); 
     $sum = $one + $two; 

   }

   $message .= "What is " . $one . " + " . $two . 
     "?<p>Current state is:" . "(" . $correct . "/" . 
     $attempts . ":" . $stamp . ")<p>"; 

print qq{ 
  <form>
    $message

    Type your answer here: <input type="text" name="answer"> <p>

    When done please press 
      <input type="submit" name="proceed" value="Proceed"> <p>
    <hr> (Or press <input type="submit" name="reset" value="Reset"> 
      to reset the game.)

    <input type="hidden" name="stamp" value="$stamp">
    <input type="hidden" name="correct" value="$correct">
    <input type="hidden" name="attempts" value="$attempts">
    <input type="hidden" name="one" value="$one">
    <input type="hidden" name="two" value="$two">
  </form>
}; 

print $q->end_html; 
And we converted most of that to CGI/DBI in class on Thu:
#!/usr/bin/perl

use CGI; 
use DBI;
use MD5; 

$DB = "DBI:mysql:a348"; 
$username = "a348"; 
$password = "a348AG"; 

$DB_TABLE = "dgerman_quiz_prex"; 

$SECRET = "something secret"; 
$EXPIRE = 30 * 60 * 60 * 24; # one month
$MAX_TRIES = 10; 
$ID_LENGTH = 8;

$q = new CGI; 

$DBH = DBI->connect($DB, $username, $password, { PrintError => 0 }) 
       || die "Couldn't open database: ", $DBI::errstr; 

my ($session_id) = &get_session_id();                                 

print $q->header, $q->start_html;                                     

my $state          = &get_state($session_id);                        

if ($state->{age} < 1) { $state = &initialize($state); }            

else { $state = &calculate($state); }                                 

&save_state($state, $session_id);                                    

&status($state);                                                      

&show_form();                                                         

print $q->end_html;
$DBH->disconnect; 
#--------------------------------(end of main program)------

sub show_form {

  print $q->start_form(), 

        "Type your answer here: <input type=\"text\" name=\"answer\"> " . 

        " <p> When done please press ", 

        $q->submit(-value=>'Proceed'),

        $q->end_form(); 
}

#--------------------------------(this was our basic form)---

sub get_session_id {

  &expire_old_sessions(); 

  my ($id) = $q->path_info =~ m:^/([a-h0-9]{$ID_LENGTH}):o;
  return $id if $id and &check_id($id); 

  my $session_id = &generate_id; 
  die "Couldn't make a new session_id" unless $session_id; 

  print $q->redirect($q->script_name() . "/$session_id"); 

  exit(0);   

}

#--------------------------------(needed above)--------------

sub expire_old_sessions {

  $DBH->do(<<END);
DELETE FROM $DB_TABLE
  WHERE (unix_timestamp() - unix_timestamp(modified)) > $EXPIRE
END

}

#--------------------------------(also needed above)---------

sub generate_id {

  my $tries = 0; 

  my $id = &hash($SECRET . rand()); 

  while ($tries++ < $MAX_TRIES) {
    last if 
      $DBH->do("INSERT INTO $DB_TABLE (session_id) VALUES ('$id')"); 
    $id = &hash($SECRET . rand()); 
  }

  return undef if $tries >= $MAX_TRIES;

  return $id; 

}

sub hash {
  my $value = shift; 
  return substr(MD5->hexhash($value), 0, $ID_LENGTH);   
}

#--------------------------------(last one needed)-----------

sub check_id {
  my $id = shift; 
  return $id 
    if $DBH->do("SELECT 1 FROM $DB_TABLE WHERE session_id = '$id'") > 0; 
  return $id 
    if $DBH->do("INSERT INTO $DB_TABLE (session_id) VALUES ('$id')"); 
  return ''; 
}

#--------------------------------(retrieve acc)--------------

sub get_state {

  my $id = shift; 

  my $query = "SELECT  *  FROM  $DB_TABLE  WHERE session_id = '$id'"; 

  my $sth = $DBH->prepare($query) || die "Prepare: ", $DBH->errstr; 
  $sth->execute || die "Execute: ", $sth->errstr;
  my $state = $sth->fetchrow_hashref;
  $sth->finish; 
  return $state; 
}

#--------------------------------(calculate new acc)---------
sub calculate { 

  my $state = shift; 

  $answer = $q->param('answer'); 

  if ($answer == $state->{one} + $state->{two}) {

    print "Good answer.<p>"; 
    $state->{attempts} += 1; 
    $state->{correct} += 1; 
  } else {
    print "Bad answer.<p>"; 
    $state->{attempts} += 1; 
  }
  $state->{age} += 1; 
  $state->{message} = "<p>Transaction no." . $state->{age} . 
    " (" . $state->{correct} . "/" . $state->{attempts} . ")"; 
  $state->{one} = int(rand(100));
  $state->{two} = int(rand(100)); 

  return $state; 
}

#--------------------------------(store new acc)-------------

sub save_state {
  my ($state, $id) = @_; 
  my $sth = $DBH->prepare(<<END) || die "Prepare: ", $DBH->errstr; 
UPDATE $DB_TABLE
  SET attempts = ?, correct = ?, age = ?, message = ?, one = ?, two = ?  
  WHERE session_id = '$id'
END
  $sth->execute(@{$state}{qw(attempts correct age message one two)}) 
  || die "Execute: ", $DBH->errstr; 
  $sth->finish; 
}

#--------------------------------(print current acc)---------

sub status {
  my ($state) = @_; 

  print  qq{

          $state->{message} <p> 

          What is: $state->{one} + $state->{two}?  <p> 

        };

}

sub initialize { 
  my $state = shift; 
  $state = {} unless $state; 
  $state->{correct} = 0; 
  $state->{attempts} = 0; 
  $state->{age} = 1;  
  $state->{message} = "Welcome"; 
  $state->{one} = int(rand(100)); 
  $state->{two} = int(rand(100)); 
  return $state; 
}
But we tweaked Fibonacci into an almost quiz program on Wed:
#!/usr/bin/perl

use CGI; 
use DBI;
use MD5; 

$DB = "DBI:mysql:a348"; 
$username = "a348"; 
$password = "a348AG"; 
$DB_TABLE = "dgerman_mar4"; 
$SECRET = "something secret"; 
$EXPIRE = 30 * 60 * 60 * 24; # one month
$MAX_TRIES = 10; 
$ID_LENGTH = 8;

$q = new CGI; 

$DBH = DBI->connect($DB, $username, $password, { PrintError => 0 }) 
       || die "Couldn't open database: ", $DBI::errstr; 

my ($session_id) = &get_session_id();                                 # [1] 
print $q->header, $q->start_html;                                     
my $state          = &get_state($session_id);                         # [2] 
if (! $state->{one}) { $state = &initialize($state); }                # [3] 
else { $state = &calculate($state); }                                 # [4] 
&save_state($state, $session_id);                                     # [5] 
&status($state);                                                      # [6] 
&show_form();                                                         # [7] 

print $q->end_html;
$DBH->disconnect; 
#--------------------------------(end of main program)------

sub show_form {

  print $q->start_form(), 

        "Answer: <input type=\"text\" name=\"answer\"> <p> When done please press ", 

        $q->submit(-value=>'Proceed'),

        $q->end_form(); 
}

#--------------------------------(this was our basic form)---

sub get_session_id {

  &expire_old_sessions(); 

  my ($id) = $q->path_info =~ m:^/([a-h0-9]{$ID_LENGTH}):o;
  return $id if $id and &check_id($id); 

  my $session_id = &generate_id; 
  die "Couldn't make a new session_id" unless $session_id; 

  print $q->redirect($q->script_name() . "/$session_id"); 

  exit(0);   

}

#--------------------------------(needed above)--------------

sub expire_old_sessions {

  $DBH->do(<<END);
DELETE FROM $DB_TABLE
  WHERE (unix_timestamp() - unix_timestamp(modified)) > $EXPIRE
END

}

#--------------------------------(also needed above)---------

sub generate_id {

  my $tries = 0; 

  my $id = &hash($SECRET . rand()); 

  while ($tries++ < $MAX_TRIES) {
    last if 
      $DBH->do("INSERT INTO $DB_TABLE (session_id) VALUES ('$id')"); 
    $id = &hash($SECRET . rand()); 
  }

  return undef if $tries >= $MAX_TRIES;

  return $id; 

}

sub hash {
  my $value = shift; 
  return substr(MD5->hexhash($value), 0, $ID_LENGTH);   
}

#--------------------------------(last one needed)-----------

sub check_id {
  my $id = shift; 
  return $id 
    if $DBH->do("SELECT 1 FROM $DB_TABLE WHERE session_id = '$id'") > 0; 
  return $id 
    if $DBH->do("INSERT INTO $DB_TABLE (session_id) VALUES ('$id')"); 
  return ''; 
}

#--------------------------------(retrieve acc)--------------

sub get_state {

  my $id = shift; 

  my $query = "SELECT * FROM $DB_TABLE WHERE session_id = '$id'"; 

  my $sth = $DBH->prepare($query) || die "Prepare: ", $DBH->errstr; 

  $sth->execute || die "Execute: ", $sth->errstr;

  my $state = $sth->fetchrow_hashref;

  $sth->finish; 

  return $state; 
}

#--------------------------------(calculate new acc)---------
sub calculate { 
  my $state = shift; 
  $answer = $q->param('answer'); 
  if ($answer == $state->{three}) {
    print "Good answer.<p>"; 
  } else {
    print "Bad answer.<p>"; 
  }
  $state->{one} = int(rand(100));
  $state->{two} = int(rand(100)); 
  $state->{three} = $state->{one} + $state->{two};

  return $state; 
}

#--------------------------------(store new acc)-------------

sub save_state {
  my ($state, $id) = @_; 
  my $sth = $DBH->prepare(<<END) || die "Prepare: ", $DBH->errstr; 
UPDATE $DB_TABLE
  SET one = ?, two = ?, three = ?  
  WHERE session_id = '$id'
END
  $sth->execute(@{$state}{qw(one two three)}) 
  || die "Execute: ", $DBH->errstr; 
  $sth->finish; 
}

#--------------------------------(print current acc)---------

sub status {
  my ($state) = @_; 

  print  qq{
          One: $state->{one} <p> 
          Two: $state->{two} <p> 
          Three: $state->{three} <p> 
        };

}

sub initialize { 
  my $state = shift; 
  $state = {} unless $state; 
  $state->{one} = int(rand(100)); 
  $state->{two} = int(rand(100)); 
  $state->{three} = $state->{one} + $state->{two}; 
  return $state; 
}
And the final version required the creation of a new table:
mysql> describe dgerman_quiz_prex;
+------------+---------------+------+-----+---------+-------+
| Field      | Type          | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| session_id | varchar(8)    |      | PRI |         |       |
| correct    | int(11)       | YES  |     | NULL    |       |
| attempts   | int(11)       | YES  |     | NULL    |       |
| one        | int(11)       | YES  |     | NULL    |       |
| two        | int(11)       | YES  |     | NULL    |       |
| age        | int(11)       | YES  |     | NULL    |       |
| message    | varchar(120)  | YES  |     | NULL    |       |
| modified   | timestamp(14) | YES  |     | NULL    |       |
+------------+---------------+------+-----+---------+-------+
8 rows in set (0.00 sec)
While the Fibonacci program relies on this table:
mysql> describe dgerman_mar4;
+------------+---------------+------+-----+---------+-------+
| Field      | Type          | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| session_id | char(8)       |      | PRI |         |       |
| one        | int(11)       | YES  |     | NULL    |       |
| two        | int(11)       | YES  |     | NULL    |       |
| three      | int(11)       | YES  |     | NULL    |       |
| modified   | timestamp(14) | YES  |     | NULL    |       |
+------------+---------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

Last updated: Jul 22, 2004 by Adrian German for A348/A548