------ art_3010_26146912.1199835152196
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
Hi, here goes my solution to this weeks Ruby Quiz, I was interested in
exact values so I explored the whole game tree of the dealer.
I also allowed to remove visible cards from the deck, here are some numbers:
2 decks, all cards
608/109 > ruby sol_r_dober-1.0.rb 2
Card 17 18 19 20 21 NATURAL BUST
2 13.9367% 13.3348% 13.0743% 12.3997% 11.9254% 0.0000% 35.3291%
3 13.2764% 13.0676% 12.4575% 12.1811% 11.5380% 0.0000% 37.4794%
4 13.0704% 12.0208% 12.1038% 11.6366% 11.3145% 0.0000% 39.8540%
5 12.1005% 12.2831% 11.7318% 10.9022% 10.7320% 0.0000% 42.2505%
6 16.6224% 10.6170% 10.6749% 10.1217% 9.7508% 0.0000% 42.2132%
7 37.0478% 13.8195% 7.8013% 7.8781% 7.3390% 0.0000% 26.1143%
8 12.9697% 36.1182% 12.9025% 6.8857% 6.9610% 0.0000% 24.1630%
9 12.0940% 11.2016% 35.4051% 12.1118% 6.0977% 0.0000% 23.0898%
H 11.2904% 11.2156% 11.3008% 33.5608% 3.5463% 7.7670% 21.3191%
A 12.8467% 13.0890% 13.0179% 13.1159% 5.2754% 31.0680% 11.5872%
3 decks no Aces left, (thus no naturals)
Card 17 18 19 20 21 NATURAL BUST
2 13.0138% 13.3157% 12.8600% 12.0227% 11.3617% 0.0000% 37.4260%
3 12.5628% 13.0186% 12.2907% 11.8168% 11.0145% 0.0000% 39.2965%
4 12.0887% 12.1174% 12.1185% 11.3440% 10.8491% 0.0000% 41.4825%
5 12.1908% 12.1116% 11.5061% 10.6511% 10.4057% 0.0000% 43.1347%
6 6.8368% 12.2179% 11.5637% 10.9612% 10.4570% 0.0000% 47.9633%
7 39.2553% 6.8531% 8.9188% 8.2753% 7.6780% 0.0000% 29.0194%
8 13.2365% 39.2553% 5.7605% 7.8262% 7.1811% 0.0000% 26.7404%
9 12.2132% 12.5372% 38.3526% 4.7974% 6.8658% 0.0000% 25.2339%
H 11.3942% 12.2132% 12.2841% 36.6997% 3.9018% 0.0000% 23.5070%
A is not left in deck
1 deck, lots of high cards visible, 3 Aces, 8 honors and 3 Nines
Card 17 18 19 20 21 NATURAL BUST
2 16.0973% 15.5326% 15.0611% 13.9028% 9.9494% 0.0000% 29.4568%
3 15.3853% 15.8948% 14.1015% 13.5737% 12.4078% 0.0000% 28.6369%
4 15.7013% 14.1293% 14.5066% 12.7401% 12.0064% 0.0000% 30.9162%
5 14.5124% 15.1168% 13.9803% 12.1107% 11.3529% 0.0000% 32.9269%
6 13.5209% 14.4244% 13.5992% 12.1245% 11.6677% 0.0000% 34.6633%
7 31.4337% 12.8325% 11.0155% 10.3669% 8.9369% 0.0000% 25.4145%
8 11.6807% 31.1870% 11.7455% 9.9479% 9.2536% 0.0000% 26.1854%
9 17.7379% 8.6150% 29.9423% 10.1906% 8.3671% 0.0000% 25.1472%
H 16.4181% 17.2912% 9.5652% 25.4766% 6.0171% 2.7027% 22.5291%
A 16.0890% 18.4076% 18.0607% 10.4038% 7.0168% 21.6216% 8.4006%
and here is the code
--------------------------- >8 -----------------------
require 'mathn'
require "enumerator"
class String
def each_char &blk
return enum_for(:each_byte).map{ |b| b.chr } unless blk
enum_for(:each_byte).each do |b| blk.call b.chr end
end
end
class Object
def tap
yield self
self
end
def ivars_set ivar_hash
ivar_hash.each do | ivar_name, ivar_value |
instance_variable_set "@#{ivar_name}", ivar_value
end
self
end
end
module Probability
def to_p digits
"%#{digits+3}.#{digits}f%%" % ( 100 * to_f )
end
end
[ Fixnum, Rational ].each do | number_class |
number_class.send :include, Probability
end
NATURAL
BUST
BANK_STANDS 7
BLACKJACK 1
DataError lass::new RuntimeError
Cards w{ 2 3 4 5 6 7 8 9 h a }
class String
def ace
downcase "a" ? 1 : 0
end
def value
case self
when "A", "a"
11
when "H", "h"
10
when "2".."9"
to_i
else
nil
end
end
end
class Fixnum
def add_value face_or_value
face_or_value ace_or_value.value if String face_or_value
self + face_or_value
end
end
# unmutable objects representing cards that still can be drawn
class Pack
def initialize *args
@data ash[ *args ] # a count of faces as a hash
@total data.inject(0){ |sum, (k,v)| sum + v } # total count of cards
end
def each_with_p
@data.each do |face, count|
next if count.zero?
yield face, probability( face )
end
end
def probability face
Rational @data[face], @total
end
def - face
data, total data, @total
self.class.allocate.instance_eval { | new_pack |
@data ata.dup
@data[ face ] -
raise DataError, "Cannot remove #{face}" if @data[ face ] < 0
@total otal - 1
new_pack
}
end
end # class Pack
# represents the hand of the dealer, immutable
class Hand
attr_reader :probability
def initialize pack, card
@pack ack
@cards card ]
@count ard.value
@aces ard.ace
@probability
end
def adjust_prob p
@probability *
end
def result
return BUST if @count > BLACKJACK
return NATURAL if @count BLACKJACK && @cards.size 2
return nil if @count < BANK_STANDS
@count - BANK_STANDS
end
def + face
count count + face.value
aces aces + face.ace
loop do
break if count < LACKJACK || aces.zero?
count - 0
aces -
end
self.class.allocate.tap{ | new_hand |
new_hand.ivars_set :cards ( @cards.dup << face ), :count count,
:aces aces, :pack @pack - face,
:probability @probability
}
end
# the workerbee, recursive traversal of the game tree of the
# dealers hand.
def compute results
@pack.each_with_p do | face, p |
new_hand elf + face
new_hand.adjust_prob p
r ew_hand.result
if r then
results[ r ] + ew_hand.probability
else
new_hand.compute results
end
end
end
end
def output card, results
puts " #{card.upcase} #{results.map{ |r| r.to_p(4) }.join(" ")}"
end
def usage
puts %<usage:
ruby #{$0} <number of decks> [visible cards]
visible cards: One or more strings with single characters indicating
face values as follows, 23456789[hH][aA]
>
exit -1
end
usage if ARGV.empty? || /^-h|^--help/ ARGV.first
number_of_decks RGV.shift.to_i
visible_cards RGV.join.each_char.to_a
pack ack.new(
*Cards.map{ |c| [c.downcase,
( c.downcase "h" ? 4 : 1 ) * 4 * number_of_decks ]
}.flatten )
visible_cards.each do |vc|
pack ack - vc.downcase
end
puts "Card 17 18 19 20 21
NATURAL BUST"
Cards.each do
| card |
begin
hand and.new pack - card, card
results rray.new( 7 ){ 0 }
hand.compute results
output card, results
rescue DataError
puts " #{card.upcase} is not left in deck"
end
end
------------------------------------------- 8<
--------------------------------------
------ art_3010_26146912.1199835152196
Content-Type: application/x-ruby; name=sol_r_dober-1.0.rb
Content-Transfer-Encoding: base64
X-Attachment-Id: f_fb72t0qo0
Content-Disposition: attachment; filename=sol_r_dober-1.0.rb
IyEvdXNyL2Jpbi9ydWJ5CiMgdmltOiBzdz0yIHRzPTIgZnQ9cnVieSBleHBhbmR0YWIgdHc9MCBu
dSBzeW46CiMKcmVxdWlyZSAnbWF0aG4nCnJlcXVpcmUgImVudW1lcmF0b3IiCgpjbGFzcyBTdHJp
bmcKICBkZWYgZWFjaF9jaGFyICZibGsKICAgIHJldHVybiBlbnVtX2Zvcig6ZWFjaF9ieXRlKS5t
YXB7IHxifCBiLmNociB9IHVubGVzcyBibGsKICAgIGVudW1fZm9yKDplYWNoX2J5dGUpLmVhY2gg
ZG8gfGJ8IGJsay5jYWxsIGIuY2hyIGVuZAogIGVuZCAKZW5kCgpjbGFzcyBPYmplY3QKICBkZWYg
dGFwCiAgICB5aWVsZCBzZWxmCiAgICBzZWxmCiAgZW5kCgogIGRlZiBpdmFyc19zZXQgaXZhcl9o
YXNoCiAgICBpdmFyX2hhc2guZWFjaCBkbyB8IGl2YXJfbmFtZSwgaXZhcl92YWx1ZSB8CiAgICAg
IGluc3RhbmNlX3ZhcmlhYmxlX3NldCAiQCN7aXZhcl9uYW1lfSIsIGl2YXJfdmFsdWUKICAgIGVu
ZAogICAgc2VsZgogIGVuZAplbmQKCm1vZHVsZSBQcm9iYWJpbGl0eQogIGRlZiB0b19wIGRpZ2l0
cwogICAgIiUje2RpZ2l0cyszfS4je2RpZ2l0c31mJSUiICUgKCAxMDAgKiB0b19mICkKICBlbmQK
ZW5kClsgRml4bnVtLCBSYXRpb25hbCBdLmVhY2ggZG8gfCBudW1iZXJfY2xhc3MgfAogIG51bWJl
cl9jbGFzcy5zZW5kIDppbmNsdWRlLCBQcm9iYWJpbGl0eQplbmQKCk5BVFVSQUwgPSA1CkJVU1Qg
ICAgPSA2CkJBTktfU1RBTkRTID0gMTcKQkxBQ0tKQUNLICAgPSAyMQoKRGF0YUVycm9yID0gQ2xh
c3M6Om5ldyBSdW50aW1lRXJyb3IKQ2FyZHMgPSAld3sgMiAzIDQgNSA2IDcgOCA5IGggYSB9Cgpj
bGFzcyBTdHJpbmcKICBkZWYgYWNlIAogICAgZG93bmNhc2UgPT0gImEiID8gMSA6IDAKICBlbmQK
CiAgZGVmIHZhbHVlCiAgICBjYXNlIHNlbGYKICAgIHdoZW4gIkEiLCAiYSIKICAgICAgMTEKICAg
IHdoZW4gIkgiLCAiaCIKICAgICAgMTAKICAgIHdoZW4gIjIiLi4iOSIKICAgICAgdG9faQogICAg
ZWxzZQogICAgICBuaWwKICAgIGVuZAogIGVuZAplbmQKCmNsYXNzIEZpeG51bQogIGRlZiBhZGRf
dmFsdWUgZmFjZV9vcl92YWx1ZQogICAgZmFjZV9vcl92YWx1ZSA9IGZhY2Vfb3JfdmFsdWUudmFs
dWUgaWYgU3RyaW5nID09PSBmYWNlX29yX3ZhbHVlCiAgICBzZWxmICsgZmFjZV9vcl92YWx1ZQog
IGVuZAplbmQKCiMgdW5tdXRhYmxlIG9iamVjdHMgcmVwcmVzZW50aW5nIGNhcmRzIHRoYXQgc3Rp
bGwgY2FuIGJlIGRyYXduCmNsYXNzIFBhY2sKCiAgZGVmIGluaXRpYWxpemUgKmFyZ3MKICAgIEBk
YXRhID0gSGFzaFsgKmFyZ3MgXSAjIGEgY291bnQgb2YgZmFjZXMgYXMgYSBoYXNoCiAgICBAdG90
YWwgPSBAZGF0YS5pbmplY3QoMCl7IHxzdW0sIChrLHYpfCBzdW0gKyB2IH0gIyB0b3RhbCBjb3Vu
dCBvZiBjYXJkcwogIGVuZAoKICBkZWYgZWFjaF93aXRoX3AKICAgIEBkYXRhLmVhY2ggZG8gfGZh
Y2UsIGNvdW50fAogICAgICBuZXh0IGlmIGNvdW50Lnplcm8/CiAgICAgIHlpZWxkIGZhY2UsIHBy
b2JhYmlsaXR5KCBmYWNlICkKICAgIGVuZAogIGVuZAoKICBkZWYgcHJvYmFiaWxpdHkgZmFjZQog
ICAgUmF0aW9uYWwgQGRhdGFbZmFjZV0sIEB0b3RhbAogIGVuZAoKICBkZWYgLSBmYWNlCiAgICBk
YXRhLCB0b3RhbCA9IEBkYXRhLCBAdG90YWwKICAgIHNlbGYuY2xhc3MuYWxsb2NhdGUuaW5zdGFu
Y2VfZXZhbCB7IHwgbmV3X3BhY2sgfAogICAgICBAZGF0YSA9IGRhdGEuZHVwCiAgICAgIEBkYXRh
WyBmYWNlIF0gLT0gMQogICAgICByYWlzZSBEYXRhRXJyb3IsICJDYW5ub3QgcmVtb3ZlICN7ZmFj
ZX0iIGlmIEBkYXRhWyBmYWNlIF0gPCAwCiAgICAgIEB0b3RhbCA9IHRvdGFsIC0gMQogICAgICBu
ZXdfcGFjawogICAgfQogIGVuZAplbmQgIyBjbGFzcyBQYWNrCgojIHJlcHJlc2VudHMgdGhlIGhh
bmQgb2YgdGhlIGRlYWxlciwgaW1tdXRhYmxlCmNsYXNzIEhhbmQKICBhdHRyX3JlYWRlciA6cHJv
YmFiaWxpdHkKICBkZWYgaW5pdGlhbGl6ZSBwYWNrLCBjYXJkCiAgICBAcGFjayA9IHBhY2sKICAg
IEBjYXJkcyA9IFsgY2FyZCBdCiAgICBAY291bnQgPSBjYXJkLnZhbHVlCiAgICBAYWNlcyA9IGNh
cmQuYWNlCiAgICBAcHJvYmFiaWxpdHkgPSAxCiAgZW5kCgogIGRlZiBhZGp1c3RfcHJvYiBwCiAg
ICBAcHJvYmFiaWxpdHkgKj0gcAogIGVuZAoKICBkZWYgcmVzdWx0CiAgICByZXR1cm4gQlVTVCBp
ZiBAY291bnQgPiBCTEFDS0pBQ0sKICAgIHJldHVybiBOQVRVUkFMIGlmIEBjb3VudCA9PSBCTEFD
S0pBQ0sgJiYgQGNhcmRzLnNpemUgPT0gMgogICAgcmV0dXJuIG5pbCBpZiBAY291bnQgPCBCQU5L
X1NUQU5EUwogICAgQGNvdW50IC0gQkFOS19TVEFORFMKICBlbmQKCiAgZGVmICsgZmFjZQogICAg
Y291bnQgPSBAY291bnQgKyBmYWNlLnZhbHVlCiAgICBhY2VzID0gQGFjZXMgKyBmYWNlLmFjZQog
ICAgbG9vcCBkbwogICAgICBicmVhayBpZiBjb3VudCA8PSBCTEFDS0pBQ0sgfHwgYWNlcy56ZXJv
PwogICAgICBjb3VudCAtPSAxMAogICAgICBhY2VzIC09IDEKICAgIGVuZAogICAgc2VsZi5jbGFz
cy5hbGxvY2F0ZS50YXB7IHwgbmV3X2hhbmQgfAogICAgICBuZXdfaGFuZC5pdmFyc19zZXQgOmNh
cmRzID0+ICggQGNhcmRzLmR1cCA8PCBmYWNlICksIDpjb3VudCA9PiBjb3VudCwKICAgICAgICAg
ICAgICAgICAgICAgICAgIDphY2VzID0+IGFjZXMsIDpwYWNrID0+IEBwYWNrIC0gZmFjZSwgOnBy
b2JhYmlsaXR5ID0+IEBwcm9iYWJpbGl0eQogICAgfQogIGVuZAoKICAjIHRoZSB3b3JrZXJiZWUs
IHJlY3Vyc2l2ZSB0cmF2ZXJzYWwgb2YgdGhlIGdhbWUgdHJlZSBvZiB0aGUKICAjIGRlYWxlcnMg
aGFuZC4KICBkZWYgY29tcHV0ZSByZXN1bHRzCiAgICBAcGFjay5lYWNoX3dpdGhfcCBkbyB8IGZh
Y2UsIHAgfAogICAgICBuZXdfaGFuZCA9IHNlbGYgKyBmYWNlCiAgICAgIG5ld19oYW5kLmFkanVz
dF9wcm9iIHAKICAgICAgciA9IG5ld19oYW5kLnJlc3VsdAogICAgICBpZiByIHRoZW4KICAgICAg
ICByZXN1bHRzWyByIF0gKz0gbmV3X2hhbmQucHJvYmFiaWxpdHkKICAgICAgZWxzZQogICAgICAg
IG5ld19oYW5kLmNvbXB1dGUgcmVzdWx0cwogICAgICBlbmQKICAgIGVuZAogIGVuZAoKZW5kCgoK
ZGVmIG91dHB1dCBjYXJkLCByZXN1bHRzCiAgcHV0cyAiICAje2NhcmQudXBjYXNlfSAgI3tyZXN1
bHRzLm1hcHsgfHJ8IHIudG9fcCg0KSB9LmpvaW4oIiAgICIpfSIKZW5kCgpkZWYgdXNhZ2UKICBw
dXRzICU8dXNhZ2U6CiAgcnVieSAjeyQwfSA8bnVtYmVyIG9mIGRlY2tzPiBbdmlzaWJsZSBjYXJk
c10KICB2aXNpYmxlIGNhcmRzOiBPbmUgb3IgbW9yZSBzdHJpbmdzIHdpdGggc2luZ2xlIGNoYXJh
Y3RlcnMgaW5kaWNhdGluZwogICAgICAgZmFjZSB2YWx1ZXMgYXMgZm9sbG93cywgMjM0NTY3ODlb
aEhdW2FBXQogID4KICBleGl0IC0xCmVuZAp1c2FnZSBpZiBBUkdWLmVtcHR5PyB8fCAvXi1ofF4t
LWhlbHAvID09PSBBUkdWLmZpcnN0CgpudW1iZXJfb2ZfZGVja3MgPSBBUkdWLnNoaWZ0LnRvX2kK
dmlzaWJsZV9jYXJkcyA9IEFSR1Yuam9pbi5lYWNoX2NoYXIudG9fYQpwYWNrID0gUGFjay5uZXco
IAogICAgICAqQ2FyZHMubWFweyB8Y3wgW2MuZG93bmNhc2UsIAogICAgICAgICggYy5kb3duY2Fz
ZSA9PSAiaCIgPyA0IDogMSApICogNCAqIG51bWJlcl9vZl9kZWNrcyBdCiAgICAgfS5mbGF0dGVu
ICkKdmlzaWJsZV9jYXJkcy5lYWNoIGRvIHx2Y3wKIHBhY2sgPSBwYWNrIC0gdmMuZG93bmNhc2UK
ZW5kCgpwdXRzICJDYXJkICAgIDE3ICAgICAgICAgMTggICAgICAgICAxOSAgICAgICAgIDIwICAg
ICAgICAgMjEgICAgICAgTkFUVVJBTCAgICAgQlVTVCIKQ2FyZHMuZWFjaCBkbwogIHwgY2FyZCB8
CiAgYmVnaW4KICAgIGhhbmQgPSBIYW5kLm5ldyBwYWNrIC0gY2FyZCwgY2FyZAogICAgcmVzdWx0
cyA9IEFycmF5Lm5ldyggNyApeyAwIH0KICAgIGhhbmQuY29tcHV0ZSByZXN1bHRzCiAgICBvdXRw
dXQgIGNhcmQsIHJlc3VsdHMKICByZXNjdWUgRGF0YUVycm9yCiAgICBwdXRzICIgICN7Y2FyZC51
cGNhc2V9ICBpcyBub3QgbGVmdCBpbiBkZWNrIgogIGVuZAplbmQK
------ art_3010_26146912.1199835152196--