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