Second Summer 2004

The programs we wrote: Practice Quiz for Midterm Exam

So I started by writing this program:

```<? session_start();
if (\$age >= 1 && ! \$reset) {
if (\$sum == \$answer) {
\$correct += 1;
} 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?>
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;
} else {
\$message = "Your answer was NOT correct.<p>";
}
\$attempts += 1;
\$stamp += 1;
\$one = rand(0, 100);
\$two = rand(0, 100);
\$sum = \$one + \$two;
}else{
}
} 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?>
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;
} 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?>
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->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) {

if (\$one + \$two == \$answer) {
\$correct += 1;
} 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

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";

\$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();

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(),

" <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;

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

\$state->{attempts} += 1;
\$state->{correct} += 1;
} else {
\$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";
\$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]
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;
if (\$answer == \$state->{three}) {
} else {
}
\$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