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