Here is my solution. I originally tried to run through all solutions
but
then adopted the simulation strategy already mentioned by others
before.

If sample size is 55000 (default is 10000), this script runs about as
long (with ruby 1.8) as Denis Hennessy's solution (which uses much
less
memory though). Also, I get constantly less bust counts for an Ace as
upcard which makes me wonder if I did something wrong here. The other
figures appear about the same.

Sample results:

$ ruby quiz151b.rb 55000
    bust    natural 17      18      19      20      21
 A:  11.62%  31.09%  12.65%  13.09%  12.88%  13.33%   5.35%
 2:  35.43%   0.00%  13.84%  13.45%  12.88%  12.35%  12.05%
 3:  37.39%   0.00%  13.56%  12.85%  12.73%  12.03%  11.43%
 4:  40.08%   0.00%  12.85%  11.97%  12.11%  11.72%  11.28%
 5:  42.21%   0.00%  12.25%  12.19%  12.02%  10.85%  10.48%
 6:  41.83%   0.00%  16.71%  10.58%  10.74%  10.45%   9.68%
 7:  26.29%   0.00%  36.96%  13.76%   7.76%   7.90%   7.32%
 8:  24.48%   0.00%  13.05%  35.85%  12.92%   6.63%   7.06%
 9:  23.39%   0.00%  11.97%  11.10%  35.50%  11.94%   6.11%
10:  21.21%   7.85%  11.01%  11.12%  11.52%  33.77%   3.52%
 B:  21.15%   7.71%  11.26%  11.11%  11.50%  33.77%   3.49%
 D:  21.36%   7.73%  11.25%  11.08%  11.32%  33.74%   3.52%
 K:  21.65%   7.77%  11.47%  11.28%  11.17%  33.16%   3.51%


Regards,
Thomas.



#!/usr/bin/env ruby
# Author::      Thomas Link (micathom AT gmail com)
# Created::     2008-01-05.

class Quiz151b
    LABELS = ['bust', 'natural', *(17..21).to_a]
    NAMES  = ['A', *(2..10).to_a] << 'B' << 'D' << 'K'
    CARDS  = (1..10).to_a + [10] * 3

    class << self
        def run(sample=10000, decks=2)
            puts '    ' + LABELS.map {|k| '%-7s' % k}.join(' ')
            13.times do |upcard|
                puts Quiz151b.new(upcard, decks).run(sample)
            end
        end
    end

    def initialize(upcard, decks)
        @upcard = upcard
        @cards  = CARDS * (4 * decks)
        @hands  = []
    end

    def run(sample)
        sample.times {@hands << deal(@upcard)}
        self
    end

    def to_s
        total = @hands.size
        acc   = Hash.new(0)
        @hands.each do |sum, hand|
            label = sum > 21 ? 'bust' :
                sum == 21 && hand.size == 2 ? 'natural' :
                sum
            acc[label] += 1
        end
        '%02s: %s' % [
            NAMES[@upcard],
            LABELS.map {|k| '%6.2f%%' % (100.0 * acc[k] /
total)}.join(' ')
        ]
    end

    def deal(idx)
        cards = @cards.dup
        hand  = []
        sum   = 0
        loop do
            hand << cards.delete_at(idx)
            sum = count(hand)
            return [sum, hand] if sum >= 17
            idx = rand(cards.size)
        end
    end

    def count(hand)
        sum  = 0
        tidx = 21 - hand.size - 10
        hand.dup.sort.reverse.each_with_index do |c, i|
            sum += c == 1 && sum <= tidx + i ? 11 : c
        end
        return sum
    end

end


if __FILE__ == $0
    case ARGV[0]
    when '-h', '--help'
        puts "#$0 [DEALS=10000] [DECKS=2]"
    else
        Quiz151b.run(*ARGV.map {|e| e.to_i})
    end
end