From: Chris Lowis Date: 2008-01-06T22:54:57+09:00 Subject: Re: Studying Blackjack (#151) 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