John Cleary (@TheRealBifter) is doing a nice project -
The 12 TDD's of Xmas.
Day 4 was the Monty Hall problem. I did it in Ruby. Here's the code from traffic-light 111.
Tests first:
require './monty_hall'
require 'test/unit'
class TestMontyHall < Test::Unit::TestCase
def test_either_goat_door_is_opened_when_you_choose_the_car_door
check_either_goat([:car, :goat, :goat],
{ :chosen_door => 0,
:goat_doors => [1, 2]
})
check_either_goat([:goat, :car, :goat],
{ :chosen_door => 1,
:goat_doors => [0, 2]
})
check_either_goat([:goat, :goat, :car],
{ :chosen_door => 2,
:goat_doors => [0, 1]
})
end
def test_other_goat_door_is_opened_when_you_choose_a_goat_door
prizes = [:car, :goat, :goat]
check_other_goat(prizes,
{ :chosen_door => 1,
:opened_door => 2,
:offered_door => 0
})
check_other_goat(prizes,
{ :chosen_door => 2,
:opened_door => 1,
:offered_door => 0
})
prizes = [:goat, :car, :goat]
check_other_goat(prizes,
{ :chosen_door => 0,
:opened_door => 2,
:offered_door => 1
})
check_other_goat(prizes,
{ :chosen_door => 2,
:opened_door => 0,
:offered_door => 1
})
prizes = [:goat, :goat, :car]
check_other_goat(prizes,
{ :chosen_door => 0,
:opened_door => 1,
:offered_door => 2
})
check_other_goat(prizes,
{ :chosen_door => 1,
:opened_door => 0,
:offered_door => 2
})
end
def test_strategy_of_sticking_with_chosen_door
wins = Array.new(big) { MontyHall.new() }
.count { |game| game.chosen_door == game.car_door }
puts "Win car(sticking with chosen door):#{wins}/#{big}"
end
def test_strategy_of_switching_to_offered_door
wins = Array.new(big) { MontyHall.new() }
.count { |game| game.offered_door == game.car_door }
puts "Win car(switching to offered door):#{wins}/#{big}"
end
#- - - - - - - - - - - - - - - - - - - - - - - -
def check_either_goat(prizes, expected)
chosen_door = expected[:chosen_door]
goat_doors = expected[:goat_doors]
check_params(prizes, chosen_door, goat_doors[0], goat_doors[1])
goat_counts = [0,0]
100.times do |n|
game = MontyHall.new(prizes,chosen_door)
opened_door = game.opened_door
offered_door = game.offered_door
assert_equal chosen_door, game.chosen_door
assert_equal goat_doors.sort, [opened_door,offered_door].sort
assert_equal doors, [chosen_door,opened_door,offered_door].sort
assert_equal :car , prizes[chosen_door]
assert_equal :goat, prizes[opened_door]
assert_equal :goat, prizes[offered_door]
[0,1].each do |n|
goat_counts[n] += (offered_door == goat_doors[n] ? 1 : 0)
end
end
[0,1].each { |n| assert goat_counts[n] > 25 }
end
def check_other_goat(prizes, expected)
chosen_door = expected[:chosen_door]
opened_door = expected[:opened_door]
offered_door = expected[:offered_door]
check_params(prizes, chosen_door, opened_door, offered_door)
game = MontyHall.new(prizes, chosen_door)
assert_equal chosen_door, game.chosen_door
assert_equal opened_door, game.opened_door
assert_equal offered_door, game.offered_door
assert_equal :goat, prizes[ chosen_door]
assert_equal :goat, prizes[ opened_door]
assert_equal :car , prizes[offered_door]
end
def check_params(prizes, door1, door2, door3)
assert_equal 3, prizes.length
prizes.each { |prize| assert [:goat,:car].include? prize }
assert_equal doors, [door1,door2,door3].sort
end
def doors
[0,1,2]
end
def big
1000
end
end
Code second:
class MontyHall
def initialize(prizes = [:goat,:goat,:car].shuffle,
chosen_door = doors.shuffle[0])
@prizes = prizes
@chosen_door = chosen_door
@car_door = prizes.find_index { |prize| prize == :car }
if prizes[chosen_door] == :car
@opened_door = goat_doors.shuffle[0]
end
if prizes[chosen_door] == :goat
@opened_door = (goat_doors - [chosen_door])[0]
end
@offered_door = (doors - [chosen_door, opened_door])[0]
end
def chosen_door
@chosen_door
end
def car_door
@car_door
end
def opened_door
@opened_door
end
def offered_door
@offered_door
end
private
def doors
[0,1,2]
end
def goat_doors
doors.select { |door| @prizes[door] == :goat }
end
end
You can
replay my entire progression (warts and all) on Cyber-Dojo (naturally).