#!/usr/bin/perl
#
# Syntax: get_cards [-s] [-v] -i <repeat_time>
#   -s = Switch: Your policy is to switch after Montys disclosure
#   -v = Verbose. Print 1 line for each iteration
#   -i = Number of iterations
#
# Program to demonstrate the 'Monty Hall' brainteaser.
# See http://www.deanesmay.com/archives/000601.html &
# http://www.deanesmay.com/archives/000656.html#000656  for more info.
# C 2003 David Yule <david@yule.org>
########################################################################

use strict;
use Getopt::Std;

my %opts;
getopts('svi:', \%opts);   # options as above. Values in %opts
my ($iterations, $switchpolicy, $verbose) = ($opts{i}, $opts{s}, $opts{v});

die "Syntax: $0 -i <iterations> [-s] [-v]\n" unless ($iterations > 0);

my $wins = 0;

foreach my $count (1 .. $iterations)
  {
    my ($win_door, $sel_door, $monty_door, $open_door);
    $win_door = &pick_door;   # Randomly select the winning door
    $sel_door = &pick_door;   # Randomly select your initial choice

    # Monty selects his door: He can't select $windoor or $seldoor
    # But we must check that these doors aren't the same
    if ($sel_door == $win_door)
      { $monty_door = &pick_door($win_door); }
    else
      { $monty_door = &pick_door($win_door, $sel_door); }

    # Now select your final door to be opened. Depends on your policy
    if ($switchpolicy) # Switch (pick the other door)
      { $open_door = &pick_door($sel_door, $monty_door); }
    else
      { $open_door = $sel_door}

    # Add to win count if the open door is the winning door.
    $wins += 1 if ($open_door == $win_door);

    print "Prize door: $win_door, Picked door: $sel_door, Opened door: $monty_door, Final door: $open_door: " . ($open_door == $win_door ? "WIN" : "LOSE") . "\n" if ($verbose);

  }

printf "TOTAL WINS: $wins/$iterations (%.2f%%)\n", 100*$wins/$iterations;

# Subroutine which randomly picks a door. It is passed a list of doors which
# can't be picked.
# The concept is simple but the code looks a bit weird ...
# If any door can be selected: it's a simple random number.
# If 1 door can't be selected: then add a random number (1 or 2) to the
# unselectable door number (modulo 3)
# If 2 doors can't be selected: Use the fact that adding the numbers of all
# the doors comes to 6 (1+2+3) to select the unselected one.
sub pick_door
  {
    my @doors = @_;
    my $selected;
    my $options = 2-$#doors;  # Number of doors to choose from (1,2 or 3)

    if    ($options == 3) # Pick a random number 1,2,3
      { $selected = int(rand(3)) + 1; }
    elsif ($options == 2) # Pick a random number 1,2 then add that to invalid
      { $selected = (int(rand(2)) + $doors[0]) % 3 + 1; }
    elsif ($options == 1) # No choice: pick unselected door
      { $selected = 6 - $doors[0] - $doors[1];}
    else { die "Illegal list of doors sent to pick_door: @doors"; }
    $selected;
  }