So I can't sleep this morning, and I've got a big interview with
$PRESTIGOUS_COMPANY that I need to get my mind off of, so I thought
I'd share a little thing I did in Ruby a few months ago with the list.

If you've never heard of the "Monty Hall" problem, suffice it to say
that it's a big famous problem in probability
(http://en.wikipedia.org/wiki/Monty_Hall_problem) that is sometimes
used to see if people really, really know their probability.  Anyway,
I've written a ruby class that allows one to investigate probabilities
such as the monty hall problem.  For example:

puts('===Monty Hall, classic version===')
ProbabilityTree.runreport(1.to_r) { |u|
  treasuredoor = u.choose(1,2,3)
  guessdoor = u.choose(1,2,3)
  remaining_doors = [1,2,3].select{ |x|
     x != treasuredoor and x != guessdoor }
  showdoor = u.choose(*remaining_doors)
  if (treasuredoor == guessdoor)
    u.report "You should stay"
  else
    u.report "You should switch"
  end
}.display

Produces:

===Monty Hall, classic version===
You should switch
==>     2/3
You should stay
==>     1/3

Which is in fact the known probabilities.  While playing with this, I
also have discovered subtleties to the problem, such as this
variation:

# variation where the host doesn't know where the treasure is either,
# and sometimes accidentally reveals the treasure.  In that case,
# the producers declare a do-over, and the segment is retaped.
puts('===Monty Hall with ignorant host and do-overs===')
ProbabilityTree.runreport(1.to_r) { |u|
  treasuredoor = u.choose(1,2,3)
  guessdoor = u.choose(1,2,3)
  remaining_doors = [1,2,3].select{|x| x != guessdoor}
  showdoor = u.choose(*remaining_doors)
  u.assert(treasuredoor != showdoor)  # i.e. we're not a do-over
  if (treasuredoor == guessdoor)
    u.report "You should stay"
  else
    u.report "You should switch"
  end
}.display

Produces:

===Monty Hall with ignorant host and do-overs===
You should switch
==>     1/2
You should stay
==>     1/2

This is in fact also known to be the correct result.

I can tackle other probability puzzles/games, such as:

# A family has two children.  One of them is a boy.
# What is a chance that the other is a boy?
puts "===Boy-Girl problem==="
ProbabilityTree.runreport(1.to_r) { |u|
  child1 = u.choose(:boy, :girl)
  child2 = u.choose(:boy, :girl)
  u.assert(child1 == :boy || child2 == :boy)
  if child1 == :boy and child2 == :boy then
    u.report "Both children are boys"
  else
    u.report "Other child is a girl"
  end
}.display

===Boy-Girl problem===
Other child is a girl
==>     2/3
Both children are boys
==>     1/3

Anyway, I've found this fun to play around with, and useful in
clarifying many subtleties in probability problems and how to work
them.  It takes a bit of practice to translate properly from problem
statement to code, and along the way gets you to think about exactly
what's going on.

For example, the boy-girl problem could be incorrectly coded as:

ProbabilityTree.runreport(1.to_r) { |u|
  children = [u.choose(:boy, :girl), u.choose(:boy, :girl)]
  choose_index = u.choose(0,1)
  u.assert(children[choose_index] == :boy)
  u.report "Other child is a #{children[1-choose_index]}"
}.display

Which is a different problem.  (This problem is: "A couple has two
children, and you pick one at random.  That child is a boy.  What is
the chance that the other child is also a boy?")

You can also choose things with unequal probability, so you can
simulate loaded dice if you want to.  I tried to simulate the
probabilities of blood types for my offspring given everything I know
about blood type distributions in the US and among my relatives, but
that never got anywhere in ruby; it's just a bit too slow.  I do have
this same idea implemented in Haskell, however, and that was able to
produce an answer to the blood-type question in a reasonable amount of
time.

Anyway, here's the code:

____________probworlds.rb_________________
# whatever
class Amb
  class ExhaustedError < RuntimeError; end

  def initialize
    @fail = [proc { fail ExhaustedError, "amb tree exhausted" }]
  end

  def choose(*choices)
    enum_choose(choices)
  end
  
  def enum_choose(choices)
    choices.each { |choice|
      callcc { |fk|
        @fail << fk
        if choice.respond_to? :call
          return choice.call
        else
          return choice
        end
      }
    }
    @fail.pop.call
  end

  def failure
    choose
  end

  def assert(cond)
    failure unless cond
  end
end

class ProbabilityTree
  private_class_method :new

  private

  def initialize(amb,one=nil)
    @amb = amb
    @reporthash = Hash.new(0)
    @currentreport = nil
    @currentprob = (one || 1)
    @one = one || 1.0
    @probtotal = 0
  end
    
  def save
    [ @currentreport, @currentprob ]
  end
    
  def restore(savedstate)
    @currentreport, @currentprob = savedstate
  end
    
  def normalize
    @reporthash.each_key { |k| @reporthash[k] /= @probtotal }
    @probtotal = 1 unless @reporthash.empty?
    @reporthash
  end
    
  def succeed
    @reporthash[@currentreport] += @currentprob
    @probtotal += @currentprob
    @amb.failure
  end
    
  public
    
  def failure
    @amb.failure
  end
    
  def choose_h(h)
    choose_p(*(h.to_a))
  end
    
  def choose(*rest)
    choose_p(*(rest.map {|x| [x, @one/rest.size]}))
  end
    
  def choose_p(*args)
    savedstate = save()
    x, prob = @amb.enum_choose(args)
    restore(savedstate)
    @currentprob *= prob
    return x
  end
    
  def report(string)
    if (@currentreport and @currentreport != '')
      @currentreport = @currentreport.to_s
      @currentreport += "\n#{string.to_s}"
    else
      @currentreport = string
    end
  end
    
  def assert(cond)
    failure unless cond
  end
    
  def ProbabilityTree.universe(one=nil)
    ae = Amb.new
    ptree = new(ae,one)
    if (ae.choose(true,false))
      yield ptree
      ptree.send(:succeed)
    else
      return ptree.send(:normalize)
    end
  end

  def ProbabilityTree.report(h)
    rval = ""
    h.keys.sort {|x,y| h[y] <=> h[x]}.each { |k|
      rval += "#{k}\n==>\t#{h[k]}\n"
    }
    rval
  end

  def ProbabilityTree.runreport(one=nil)
    report(universe(one) {|u| yield u})
  end
end

# reference http://en.wikipedia.org/wiki/Three_cards_problem
puts 'Three-card problem:'
ProbabilityTree.runreport { |u|
  card = u.choose('bw', 'ww', 'bb')
  side = u.choose(0,1)
  u.assert(card[side] == ?b);
  u.report "Other side is #{card[1-side,1]}"
}.display

puts 'Three-card problem with someone who always shows you the black
side if possible:'
ProbabilityTree.runreport { |u|
  card = u.choose('bw', 'ww', 'bb')
  side = u.choose(0,1)
  if card[side] != ?b and card[1-side] == ?b then
    side = 1-side
  end
  u.assert(card[side] == ?b);
  u.report "Other side is #{card[1-side,1]}"
}.display

# Monty Hall
require "rational"

# standard version
puts('===Monty Hall, classic version===')
ProbabilityTree.runreport(1.to_r) { |u|
  treasuredoor = u.choose(1,2,3)
  guessdoor = u.choose(1,2,3)
  remaining_doors = [1,2,3].select{|x|
    x != treasuredoor and x != guessdoor }
  showdoor = u.choose(*remaining_doors)
  if (treasuredoor == guessdoor)
    u.report "You should stay"
  else
    u.report "You should switch"
  end
}.display

# variation where the host doesn't know where the treasure is either,
# and sometimes accidentally reveals the treasure.  In that case,
# the producers declare a do-over, and the segment is retaped.
puts('===Monty Hall with ignorant host and do-overs===')
ProbabilityTree.runreport(1.to_r) { |u|
  treasuredoor = u.choose(1,2,3)
  guessdoor = u.choose(1,2,3)
  remaining_doors = [1,2,3].select{|x| x != guessdoor}
  showdoor = u.choose(*remaining_doors)
  u.assert(treasuredoor != showdoor)  # i.e. we're not a do-over
  if (treasuredoor == guessdoor)
    u.report "You should stay"
  else
    u.report "You should switch"
  end
}.display

# The lesson?
# use assert for stuff that "just happened".  Pass a
# different list to choose for stuff that was deliberate.

# You and Bob both choose doors.  Monty Hall tells one of you
# to go home, and opens the losers door, because they chose poorly.
# Should the remaining person switch doors or stick with their
# original guess?
puts('===Monty Hall with Bob===')
ProbabilityTree.runreport(1.to_r) { |u|
  treasuredoor = u.choose(1,2,3)
  myguessdoor = u.choose(1,2,3)
  bobguessdoor = u.choose(*([1,2,3].select{|i| i != myguessdoor}))
  elimchoices = [[:me,myguessdoor],[:bob,bobguessdoor]]
  elimchoices = elimchoices.select{|i| i[1] != treasuredoor}
  elimchoice = u.choose(*elimchoices)
  u.assert(elimchoice[0] == :bob)
  if (treasuredoor == myguessdoor)
    u.report "You should stay"
  else
    u.report "You should switch"
  end
}.display

# Same as above, but the host hates Bob and will eliminate
# him early whenever possible.  Now, if Bob is eliminated
# what should you do?
puts('===Monty Hall hates Bob===')
ProbabilityTree.runreport(1.to_r) { |u|
  treasuredoor = u.choose(1,2,3)
  myguessdoor = u.choose(1,2,3)
  bobguessdoor = u.choose(*([1,2,3].select{|i| i != myguessdoor}))
  elimchoices = [[:me,myguessdoor],[:bob,bobguessdoor]]
  elimchoices = elimchoices.select{|i| i[1] != treasuredoor}
  elimchoice = nil
  if elimchoices.size == 2 then
    # If we're both still in the running to be eliminated, choose Bob
    elimchoice = elimchoices[1]
  else
    # Choose whoever is left
    elimchoice = elimchoices[0]
  end
  u.assert(elimchoice[0] == :bob)
  if (treasuredoor == myguessdoor)
    u.report "You should stay"
  else
    u.report "You should switch"
  end
}.display

# This doesn't work
puts "Sum - naive"
ProbabilityTree.runreport(1.to_r) { |u|
  sum = 0
  3.times {
    i = u.choose(0,1)
    sum += i
  }
  u.report sum
}.display

# This does work
puts "Sum - store state"
ProbabilityTree.runreport(1.to_r) { |u|
  sum = 0
  3.times {
    psum = sum
    i = u.choose(0,1)
    sum = psum + i
  }
  u.report sum
}.display

# This works too
puts "Sum - hidden store state"
ProbabilityTree.runreport(1.to_r) { |u|
  sum = 0
  3.times {
    sum += u.choose(1,0)
  }
  u.report sum
}.display

# and this
puts "Sum - choose state"
ProbabilityTree.runreport(1.to_r) { |u|
  sum = 0
  3.times {
    i,sum = u.choose([0,sum],[1,sum])
    sum += i
  }
  u.report sum
}.display

# and this
puts "Sum - inject"
ProbabilityTree.runreport(1.to_r) { |u|
  sum = (0 ... 3).inject(0) { |s,|
    s + u.choose(0,1)
  }
  u.report sum
}.display

# A family has two children.  One of them is a boy.
# What is a chance that the other is a boy?
puts "===Boy-Girl problem==="
ProbabilityTree.runreport(1.to_r) { |u|
  child1 = u.choose(:boy, :girl)
  child2 = u.choose(:boy, :girl)
  u.assert(child1 == :boy || child2 == :boy)
  if child1 == :boy and child2 == :boy then
    u.report "Both children are boys"
  else
    u.report "Other child is a girl"
  end
}.display