> by Kenneth Kin Lum
>
> You just did some probability calculations, and don't know if the answers are
> correct.  So you write a program to verify the results.  If you have eight dice,
> and throw them all at once, what is the probability that there are AT LEAST
> three fives?  Try to write a program that find out the "number of desirable
> outcomes" / "number of possible outcomes" by iterating through all the possible
> outcomes of the dice throw.

Hi,

I wanted to look a little into parsing command line options and
arguments, so I took the opportunity to try Ara's "main" gem (gem
install main). I have a question about the usage of main at the end of
the post, if somebody can help me out, that'd be great.
In addition to the requirements I also added the possibility to pass
another option (--type or -t) with the type of comparison you want to
perform: at_least, exactly, no_more_than, less_than or more_than. It
defaults to at_least. Here it is:

# quiz141.rb
# 29 September 2007
#

require 'main'

$compare_types = {"at_least" => :>=, "exactly" => :==, "no_more_than" => :<=,
                  "more_than" => :>, "less_than" => :<}

main {
  argument('dice') {
    cast :int
    description "number of dice to throw"
  }
  argument('num') {
    cast :int
    description "number of fives to count in each throw"
  }
  option('verbose', 'v') {
    description "verbose mode: will show all combinations"
  }
  option('samples', 's') {
    description "sample mode: will show 1 combination every 50000"
  }
  option('type', 't') {
    argument :required
    defaults "at_least"
    validate {|type| $compare_types.keys.include?(type)}
    description "Type of comparison. Possible values: " +
$compare_types.keys.join(",")
  }

def run
  verbose = params[:verbose].given?
  samples = params[:samples].given?
  dice = params[:dice].value
  num = params[:num].value
  type = params[:type].value
  method = $compare_types[type]
  puts "Checking #{type} #{num} fives throwing #{dice} dice"
  puts "Verbose mode" if verbose
  puts "Samples mode" if samples
  current = Array.new(dice) {1}
  current[0] = 0 # to start the loop incrementing the first element
  total_matches = 0
  total_iter = 0
  begin
    total_iter += 1
    (0...dice).each do |i|
      if (current[i] < 6)
        current[i] += 1
        break
      else
        current[i] = 1
      end
    end
    match = current.select {|x| x == 5}.size.send(method, num)
    total_matches += 1 if match
    if (verbose || (samples && total_iter%50000 == 1))
      print total_iter, " ", current.inspect
        puts "#{match ?'<==':''}"
    end
  end while current.any?{|x|x != 6}
  puts "Number of desirable outcomes: #{total_matches}"
  puts "Number of possible outcomes: #{total_iter}"
  puts "Probabilty is #{total_matches.to_f/total_iter.to_f}"
end
}

Here's the output with 2 1 for each type of comparison.

$ ruby -rubygems quiz141.rb --type less_than 2 1
Checking less_than 1 fives throwing 2 dice
Number of desirable outcomes: 25
Number of possible outcomes: 36
Probabilty is 0.694444444444444

$ ruby -rubygems quiz141.rb --type at_least 2 1
Checking at_least 1 fives throwing 2 dice
Number of desirable outcomes: 11
Number of possible outcomes: 36
Probabilty is 0.305555555555556

$ ruby -rubygems quiz141.rb --type exactly 2 1
Checking exactly 1 fives throwing 2 dice
Number of desirable outcomes: 10
Number of possible outcomes: 36
Probabilty is 0.277777777777778

$ ruby -rubygems quiz141.rb --type no_more_than 2 1
Checking no_more_than 1 fives throwing 2 dice
Number of desirable outcomes: 35
Number of possible outcomes: 36
Probabilty is 0.972222222222222

$ ruby -rubygems quiz141.rb --type more_than 2 1
Checking more_than 1 fives throwing 2 dice
Number of desirable outcomes: 1
Number of possible outcomes: 36
Probabilty is 0.0277777777777778

My question about main is the following: if you check my code, I have
a variable ($compare_types) I need to use both in the validate block
for an option and in the run method. Is there a better way to achieve
this than a global variable?

Regards,

Jesus.