#!/usr/bin/perl
#
#Monty Hall's Hall Of Doors v2.0 19991026
#by Leonard Richardson leonardr@ucla.edu
#
#"And you may ask yourself, where is that large automobile?"
# - Talking Heads, 'Once in a Lifetime'
#
use CGI qw/:standard/;
$max_games = 10000;
$disable_logging_at = 100;
#preliminaries prints out the usual suspects and gets the input.
preliminaries();
#Initialize data structures.
foreach $a ("switch","stick")
{
$wins{$a} = 0;
$losses{$a} = 0;
}
#Play a bunch of games.
$switch_counter = 1;
for ($counter = 1; $counter <= $times; $counter++)
{
play_game();
}
#Let's tabulate those scores!
print_statistics();
###########################################
sub play_game()
#Plays one round of the Monty Hall game of fun.
{
$mega_door = int(rand(3)) + 1;
@door_stats[$position]++;
if ($same_door_always)
{
$first_choice = 1;
} else {
$first_choice = int(rand(3))+1;
}
#Figure out which door Monty opens. The door that neither the
#player nor Monty picked is our choice if we want to switch.
$opened = monty_decision($first_choice, $mega_door);
$switch_choice = other_door($first_choice,$opened);
#Now we need to figure out if we're going to switch or not.
#
#We can switch randomly rather than with a counter, to exchange
#experimental error (eg., 40% switching on 30 games) for random
#error.
if ($random)
{
if (int(rand(100))+1 <= $percent_switch)
{
$action = "switch";
} else {
$action = "stick";
}
} else {
#Or, we can switch with a counter, to exchange random error for
#experimental error. This is the default.
if ($switch_counter <= $percent_switch)
{
$action = "switch";
} else {
$action = "stick";
}
$switch_counter++;
if ($switch_counter > 100) { $switch_counter = 1; }
}
if ($action eq "switch")
{
$new_choice = $switch_choice;
} else {
$new_choice = $first_choice;
}
#Now we see whether or not we won, and increment the appropriate variables.
if ($mega_door == $new_choice)
{
$wins{$action}++;
} else {
$losses{$action}++;
}
if ($log)
{
print "**Log for round $counter:**

\n";
print "I chose door $first_choice.

\n";
print "Monty opened door $opened.

\n";
print "I decided ";
if ($action eq "stick") {print "not ";}
print "to switch";
if ($action eq "switch") {print " to door $new_choice"};
print ".

\n";
if ($mega_door == $new_choice)
{
print "Woohoo!";
} else {
print "Damn!";
}
print " The mega prize was behind door $mega_door";
if ($action eq "switch" && $mega_door ne $new_choice) { print " after all";}
print "!

\n

\n";
if ($counter == $disable_logging_at)
{
print "$counter rounds played, disabling log.

\n";
$log = 0;
}
}
}
sub print_statistics #See also print_lies and print_damn_lies
{
%past = ("switch" => "switched",
"stick" => "stuck",);
#First, using only the starting conditions, we will predict the total
#percentage of rounds that should be won.
%theoretical_win_chance = ("switch" => 2/3,
"stick" => 1/3,);
$theoretical_win_chance = ($theoretical_win_chance{switch} * $percent_switch) +
($theoretical_win_chance{stick} * (100-$percent_switch));
#Now we print them out.
print "# Monty Hall's Hall Of Doors

";
print "Stats on $times games, switching $percent_switch% of the time:

";
foreach $a ("switch","stick") {
#Need to do this crap to avoid division by zero errors
$percentage{$a} = ($losses{$a} + $wins{$a} == 0 ? "0":
$wins{$a}/(($losses{$a} +
$wins{$a})) * 100);
$total_percentage{$a} = ($wins{$a} / $times) * 100;
$total = $wins{$a}+$losses{$a};
print "**".ucfirst($a)."ing:** ";
print "You $past{$a} $total times (" . $total/$times * 100 ."% of the time)

".
"Wins on $a: $wins{$a}

".
"Losses on $a: $losses{$a}

".
"You won $percentage{$a}% of the time you $past{$a}.

";
}
print "

**The Bottom Line:**

";
print $total_percentage{stick}+$total_percentage{switch} .
"% of all rounds were won.

";
print "Theory predicts $theoretical_win_chance%.\n

";
}
sub monty_decision
{
($player_chosen_door, $mega_door) = @_;
#Monty can't open the door you picked, and he can't open the mega
#door. If these doors are the same door, we pick
#randomly. Otherwise, his hand is forced.
#This function isn't very understandable.
if ($first_choice == $mega_door)
{
#There are two unopened doors. We pick one at random and then
#see which actual doors they correspond to.
$chosen_unopened_door = int(rand(2))+1;
if ($mega_door == 1 ||
($mega_door == 2 && $chosen_unopened_door == 2))
{
return $chosen_unopened_door+1;
} else {
return $chosen_unopened_door;
}
} else {
#We pick the door that's neither the chosen door nor the mega
#door.
return other_door($first_choice,$mega_door);
}
}
sub other_door
{
#Give this puppy two doors and it returns the door that's not the
#one you gave it.
@doors = (1..3);
@invalid_doors = @_;
foreach $door (@invalid_doors)
{
for ($i = 0; $i < scalar(@doors); $i++)
{
if ($doors[$i] == $door)
{
splice(@doors,$i,1);
last;
}
}
}
return $doors[0];
}
sub preliminaries
{
print header;
$times = param("times");
$percent_switch = param("percent");
$random = param("random");
$same_door_always = param("same_door_always");
$log = param("log");
if ($times > $max_games)
{
print "Can't play $times games, playing $max_games games instead.

";
$times = $max_games;
}
}