Here's my solution. I'm particularly interested in comments on my
initialize method in the Dealer class. Is there a better way to make
the deck object available to the methods in this class ?

$ ruby quiz_151.rb
Upcard  Bust    17      18      19      20      21      Natural
c2      34.90%  14.20%  13.38%  13.30%  12.16%  12.06%   0.00%
c3      37.30%  13.68%  12.68%  13.34%  11.56%  11.44%   0.00%
c4      39.88%  14.22%  11.74%  11.82%  11.08%  11.26%   0.00%
c5      41.88%  11.46%  11.94%  12.08%  11.86%  10.78%   0.00%
c6      42.30%  16.54%  11.06%  10.64%  10.00%   9.46%   0.00%
c7      26.82%  35.92%  13.12%   8.70%   7.94%   7.50%   0.00%
c8      23.84%  13.30%  35.72%  13.14%   6.60%   7.40%   0.00%
c9      22.94%  11.68%  12.46%  34.48%  12.24%   6.20%   0.00%
ct      21.32%  11.12%  11.56%  11.18%  34.06%   3.48%   7.28%
cj      20.98%  11.18%  11.28%  10.60%  34.14%   3.82%   8.00%
cq      21.40%  11.24%  11.34%  10.72%  34.36%   3.48%   7.46%
ck      20.54%  10.36%  10.38%  11.32%  35.72%   3.64%   8.04%
ca      13.08%  13.04%  12.96%  13.56%  11.92%   5.48%  29.96%

$ cat quiz_151.rb
#!/usr/bin/env ruby -w

class Deck
  def initialize(number_of_decks)
    @cards = []

    suits = ["h","c","d","s"]
    values = [2,3,4,5,6,7,8,9,"t","j","q","k","a"]

    number_of_decks.times do
      suits.each do |suit|
        values.each do |value|
          @cards << suit + value.to_s
        end
      end
    end
    shuffle
  end

  def shuffle
    @cards = @cards.sort_by {rand}
  end

  def deal
    @cards.pop
  end

  def deal_a(card)
    # Deal a named card from the deck
    @cards.delete_at(@cards.index(card))
  end
end

class Dealer

  def initialize(deck,upcard)
    @hand = []
    @score = 0
    @hand << deck.deal_a(upcard)
    @hand << deck.deal
    @deck = deck
  end

  def bust?
    current_score > 21
  end

  def natural?
    current_score == 21 && @hand.length == 2
  end

  def current_score

    # To deal with multiple aces, sort the current hand so that the
    # aces appear as the last elements in the array.
    values = []
    @hand.each {|card| values << card[1].chr}
    not_aces = values.find_all {|v| /[^a]/=~v}
    aces = values.find_all {|v| /[a]/=~v}

    values = not_aces + aces

    # Calculate the score for this hand
    score = 0
    values.each do |value|
      if /\d/ =~ value then score += value.to_i end
      if /[t,k,j,q]/ =~ value then score += 10 end
      if /[a]/ =~ value then
        if score + 11 > 21
          score += 1
        elsif
          score += 11
        end
      end
    end
    score
  end

  def play
    until self.bust? || current_score >= 17
      card = @deck.deal
      @hand << card
    end

    if self.bust?
      "bust"
    elsif self.natural?
      "natural"
    else
      current_score
    end
  end
end

if __FILE__ == $0
  upcards =
["c2","c3","c4","c5","c6","c7","c8","c9","ct","cj","cq","ck","ca"]
  outcomes = ["bust",17,18,19,20,21,"natural"]

  no_of_games = 5000
  printf("Upcard\tBust\t17\t18\t19\t20\t21\tNatural\n")
  upcards.each do |upcard|
    results = []
    no_of_games.times {results << Dealer.new(Deck.new(8),upcard).play}

    p = []
    outcomes.each do |outcome|
      number = results.find_all {|r| r==outcome}
      p << (number.length.to_f/no_of_games)*100
    end

    printf("%s\t%5.2f%%\t%5.2f%%\t%5.2f%%\t%5.2f%%\t%5.2f%%\t%5.2f%%\t
%5.2f%%\n",
           upcard,p[0],p[1],p[2],p[3],p[4],p[5],p[6])
  end
end