Based on this question on math.SE regarding probabilities in variations on the Monty Hall problem, I cobbled up a simulator in Ruby to give myself an introduction to the language.
Since this is my first Ruby program, I'm especially looking for feedback regarding Ruby style, but I would love any general code improvement suggestions as well (all the results seem correct, so I think the math is good). I'm more comfortable with Python and shell scripting, so I'm sure my preferences show through here.
Current functionality:
The program allows simulations of the following scenarios:
- The host (Monty Hall) opens all but 1 door and the player switches to the door Monty left closed (the classic version)
- Monty opens 1 extra door, leaving the rest closed, and you decide to switch to a different closed door (the premise of the math.SE question)
- You pick a door and don't switch no matter what Monty does
Currently, it's hard-coded to simulate scenario #2 with 5 doors 1000 times, but that can be changed by switching the comment on the second-to-last line.
puts "Hello Monty!"
$wins = 0.0
$goats = 0.0
def choose_doors(num_doors)
door_range = (1..num_doors)
winning_door = rand(door_range)
player_choice = rand(door_range)
losing_doors = door_range.to_a - [winning_door] - [player_choice]
monty_open = losing_doors.sample # used when Monty opens 1 non-winning door
return player_choice, monty_open, winning_door
end
def switch_door_multiple(choice, revealed, num_doors)
# Monty revealed only 1 other door
door_range = (1..num_doors)
door_choices = door_range.to_a - [choice] - [revealed]
new_choice = door_choices.sample
end
def count_result(final_choice, winning_door)
if final_choice == winning_door
$wins += 1
else
$goats += 1
end
end
def simulate(num_doors, runs, strategy)
for i in 1..runs
player_door, open_door, winning_door = choose_doors(num_doors)
if strategy == 1 # switch, 1 choice
if player_door != winning_door
player_door = winning_door
else
player_door = -1
end
elsif strategy == 2 # switch, multiple choice
player_door = switch_door_multiple(player_door, open_door, num_doors)
else
# keep initial door
end
count_result(player_door, winning_door)
end
puts "wins: " + $wins.to_s
puts "goats: " + $goats.to_s
puts "win percentage: " + (100 * ($wins / ($wins + $goats))).to_s + "%"
end
def prompt_user
print "Number of doors: "
input_num_doors = gets.chomp.to_i
while input_num_doors < 3
puts "This only works with 3 or more doors"
print "Number of doors: "
input_num_doors = gets.chomp.to_i
end
print "Number of runs: "
input_runs = gets.chomp.to_i
while input_runs < 1
puts "Need 1 or more runs"
print "Number of runs: "
input_runs = gets.chomp.to_i
end
puts "Simulation: "
puts "[1]: Monty opens all but 1 door and you decide to switch"
puts "[2]: Monty opens 1 door and you switch to a different closed door"
puts "[3]: Keep your door - you don't switch no matter what Monty does"
print "Choose a game type (1/2/3): "
input_strategy = gets.chomp.to_i
while ![1, 2, 3].include? input_strategy
print "Choose a game type (1/2/3): "
input_strategy = gets.chomp.to_i
end
return input_num_doors, input_runs, input_strategy
end
# num_doors, num_runs, strategy = prompt_user
num_doors, num_runs, strategy = 5, 1000, 2
simulate(num_doors, num_runs, strategy)