#!/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; } }