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.
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 ": Monty opens all but 1 door and you decide to switch" puts ": Monty opens 1 door and you switch to a different closed door" puts ": 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)