Skip to content

Instantly share code, notes, and snippets.

@bjjb
Last active December 11, 2015 12:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bjjb/4600289 to your computer and use it in GitHub Desktop.
Save bjjb/4600289 to your computer and use it in GitHub Desktop.
A demonstration of the Monty Hall Problem (http://en.wikipedia.org/wiki/Monty_Hall_problem).
#!/usr/bin/env ruby
# encoding: utf-8
#
# A silly script to demonstrate the Monty Hall Problem.
# http://en.wikipedia.org/wiki/Monty_Hall_problem
#
# Usage
# ruby [-v] monty.rb <n>
# where n is the number of times each of the 3 players plays the game.
#
# From [Wikipedia](http://en.wikipedia.org/wiki/Monty_Hall_problem):
# > Suppose you're on a game show, and you're given the choice of three doors:
# > Behind one door is a car; behind the others, goats.
# > You pick a door, say No. 1, and the host, who knows what's behind the doors,
# > opens another door, say No. 3, which has a goat.
# > He then says to you, "Do you want to pick door No. 2?"
# > Is it to your advantage to switch your choice?
#
# Oddly enough, the answer is that you have a 66% chance of winning if you switch to
# the other closed door, 33% if you stay put, and roughly 50% if you randomly
# change (as expected).
#
# The script demonstrates that with Alice (who always switches), Bob (who never switches),
# and Cecil, who does what he likes.
class Game
class Door
attr_accessor :open, :prize, :label
def initialize(game, prize)
@game = game
@prize = prize
@chosen = @open = false
end
end
class Player
@@players = []
attr_accessor :name, :plays, :wins
def initialize(name, switch = nil)
@name = name
@plays = 0.0
@wins = 0.0
@switch = switch
@@players << self unless @@players.include?(self)
end
def policy
case @switch
when nil then "sometimes switches"
when true then "ALWAYS switches"
when false then "NEVER switches"
end
end
def to_s
w = @@players.map { |p| p.name.length }.max
x = @@players.map { |p| p.policy.length }.max + 2
y = ($n || 1000000).to_s.length
success = ((wins / plays) * 100).round(2)
"%#{w}s %-#{x}s - %#{y}d/%-#{y}d %3.2f%% " % [name, policy, wins, plays, success]
end
def switch?
if @switch.nil?
[true, false].shuffle.first
else
@switch
end
end
def play
Game.play(self)
end
end
def initialize
@doors = [
Door.new(self, 'Goat'),
Door.new(self, 'Goat'),
Door.new(self, 'Car')
].shuffle
@doors[0].label = 'A'
@doors[1].label = 'B'
@doors[2].label = 'C'
end
def puts(*args)
STDOUT.puts(*args) if $VERBOSE
end
def play(player)
puts
puts "Three doors, two goats, one car..."
@choice = @doors.shuffle.first
puts "#{player.name} chooses door #{@choice.label}"
door = @doors.reject { |d| d == @choice || d.prize == 'Car' }.shuffle.first
puts "Monty opens door #{door.label} to reveal a #{door.prize}"
door.open = true
if player.switch?
@choice = @doors.reject { |d| d == @choice }.reject { |d| d.open }.shuffle.first
puts "#{player.name} changes mind, and chooses door #{@choice.label}"
else
puts "#{player.name} stays on door #{@choice.label}"
end
puts "Monty opens door #{@choice.label}, to reveal a #{@choice.prize}"
if @choice.prize == 'Car'
puts "Congratulations!!"
player.wins += 1
else
puts "Sorry..."
end
player.plays += 1
end
def self.play(player)
new.play(player)
end
end
$n = Integer(ARGV[0] || 100)
alice = Game::Player.new(ARGV[1] || "Alice", true)
bob = Game::Player.new(ARGV[2] || "Bob", false)
cecil = Game::Player.new(ARGV[3] || "Cecil", nil)
$n.times { alice.play; print "\r#{alice}" }
puts
$n.times { bob.play; print "\r#{bob}" }
puts
$n.times { cecil.play; print "\r#{cecil}" }
puts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment