Monty Hall in Ruby

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).

No comments:

Post a Comment