質問
可能な限り多くのゲームに勝つために、とどまるのに最適な価値はありますか?もしそうなら、それは何ですか?
編集:対戦相手が何をするかに関係なく、与えられた制限に対して計算できる正確な勝率はありますか?(私は大学以来、確率と統計を行っていません)。それを私のシミュレーション結果と対比するための答えとして見たいと思います。
編集:私のアルゴリズムのバグを修正し、結果テーブルを更新しました。
バックグラウンド
私は、標準のルールからかなり厄介なルールの微調整を加えた修正ブラックジャックゲームをプレイしてきました。標準のブラックジャックのルールとは異なるルールをイタリック体で示し、慣れていない人のためにブラックジャックのルールを含めました。
変更されたブラックジャックルール
- 正確に2人の人間のプレーヤー(ディーラーは無関係です)
- 各プレイヤーには裏向きに2枚のカードが配られます
- どちらのプレイヤーも、対戦相手のカードの_any_の値を_決して_知りません。
- どちらのプレーヤーも、_両方_がハンドを終了するまで、対戦相手のハンドの価値を知りません。
- 目標は、可能な限り21のスコアに近づくことです。結果:
- プレーヤーのAとBのスコアが同じである場合、ゲームは引き分けです
- プレーヤーのAとBの両方のスコアが21(バスト)を超えている場合、ゲームは引き分けです
- プレーヤーAのスコアが21未満で、プレーヤーBがバストした場合、プレーヤーAが勝ちます
- プレーヤーAのスコアがプレーヤーBのスコアよりも大きく、どちらもバストしていない場合、プレーヤーAが勝ちます
- それ以外の場合、プレーヤーAは負けました(Bが勝ちました)。
- カードの価値は次のとおりです。
- カード2から10は、対応するポイント数の価値があります
- カードJ、Q、Kは10ポイントの価値があります
- カードエースは1または11ポイントの価値があります
- 各プレイヤーは、次の状態になるまで、一度に1枚ずつ追加のカードを要求できます。
- プレイヤーはこれ以上欲しくない(滞在)
- エースを1としてカウントしたプレーヤーのスコアは、21(バスト)を超えています。
- どちらのプレイヤーも、相手がいつでも何枚のカードを使用したかを知りません。
- 両方のプレイヤーが滞在またはバストした後、勝者は上記のルール3に従って決定されます。
- 各ハンドの後、デッキ全体が再シャッフルされ、52枚のカードすべてが再び場に出ます。
トランプとは何ですか?
カードのデッキは52枚のカードで構成され、次の13個の値がそれぞれ4枚ずつあります。
2、3、4、5、6、7、8、9、10、J、Q、K、A
カードの他のプロパティは関係ありません。
これのRuby表現は次のとおりです。
CARDS = ((2..11).to_a+[10]*3)*4
アルゴリズム
私はこれに次のように取り組んできました:
- スコアが2から11の場合、バストすることは不可能なので、常にヒットしたいと思います。
- スコア12から21のそれぞれについて、対戦相手に対してNハンドをシミュレートします
- これらのNハンドの場合、スコアは私の「限界」になります。限界以上になったら留まります。
- 私の対戦相手はまったく同じ戦略に従います
- セットの順列ごとにN個の手をシミュレートします(12..21)、(12..21)
- 各順列の勝ち負けの差と正味の勝ち負けの差を印刷します
Rubyに実装されているアルゴリズムは次のとおりです。
#!/usr/bin/env ruby
class Array
def shuffle
sort_by { rand }
end
def shuffle!
self.replace shuffle
end
def score
sort.each_with_index.inject(0){|s,(c,i)|
s+c > 21 - (size - (i + 1)) && c==11 ? s+1 : s+c
}
end
end
N=(ARGV[0]||100_000).to_i
NDECKS = (ARGV[1]||1).to_i
CARDS = ((2..11).to_a+[10]*3)*4*NDECKS
CARDS.shuffle
my_limits = (12..21).to_a
opp_limits = my_limits.dup
puts " " * 55 + "opponent_limit"
printf "my_limit |"
opp_limits.each do |result|
printf "%10s", result.to_s
end
printf "%10s", "net"
puts
printf "-" * 8 + " |"
print " " + "-" * 8
opp_limits.each do |result|
print " " + "-" * 8
end
puts
win_totals = Array.new(10)
win_totals.map! { Array.new(10) }
my_limits.each do |my_limit|
printf "%8s |", my_limit
$stdout.flush
opp_limits.each do |opp_limit|
if my_limit == opp_limit # will be a tie, skip
win_totals[my_limit-12][opp_limit-12] = 0
print " --"
$stdout.flush
next
elsif win_totals[my_limit-12][opp_limit-12] # if previously calculated, print
printf "%10d", win_totals[my_limit-12][opp_limit-12]
$stdout.flush
next
end
win = 0
lose = 0
draw = 0
N.times {
cards = CARDS.dup.shuffle
my_hand = [cards.pop, cards.pop]
opp_hand = [cards.pop, cards.pop]
# hit until I hit limit
while my_hand.score < my_limit
my_hand << cards.pop
end
# hit until opponent hits limit
while opp_hand.score < opp_limit
opp_hand << cards.pop
end
my_score = my_hand.score
opp_score = opp_hand.score
my_score = 0 if my_score > 21
opp_score = 0 if opp_score > 21
if my_hand.score == opp_hand.score
draw += 1
elsif my_score > opp_score
win += 1
else
lose += 1
end
}
win_totals[my_limit-12][opp_limit-12] = win-lose
win_totals[opp_limit-12][my_limit-12] = lose-win # shortcut for the inverse
printf "%10d", win-lose
$stdout.flush
end
printf "%10d", win_totals[my_limit-12].inject(:+)
puts
end
使用法
ruby blackjack.rb [num_iterations] [num_decks]
スクリプトのデフォルトは、100,000回の反復と4つのデッキです。高速なMacBookProでは100,000分で約5分かかります。
出力(N = 100 000)
opponent_limit
my_limit | 12 13 14 15 16 17 18 19 20 21 net
-------- | -------- -------- -------- -------- -------- -------- -------- -------- -------- -------- --------
12 | -- -7666 -13315 -15799 -15586 -10445 -2299 12176 30365 65631 43062
13 | 7666 -- -6962 -11015 -11350 -8925 -975 10111 27924 60037 66511
14 | 13315 6962 -- -6505 -9210 -7364 -2541 8862 23909 54596 82024
15 | 15799 11015 6505 -- -5666 -6849 -4281 4899 17798 45773 84993
16 | 15586 11350 9210 5666 -- -6149 -5207 546 11294 35196 77492
17 | 10445 8925 7364 6849 6149 -- -7790 -5317 2576 23443 52644
18 | 2299 975 2541 4281 5207 7790 -- -11848 -7123 8238 12360
19 | -12176 -10111 -8862 -4899 -546 5317 11848 -- -18848 -8413 -46690
20 | -30365 -27924 -23909 -17798 -11294 -2576 7123 18848 -- -28631 -116526
21 | -65631 -60037 -54596 -45773 -35196 -23443 -8238 8413 28631 -- -255870
解釈
これは私が苦労しているところです。このデータの解釈方法がよくわかりません。一見、常に16か17にとどまるのが道のように思えますが、それが簡単かどうかはわかりません。実際の人間の対戦相手が12、13、場合によっては14に留まる可能性は低いと思うので、それらのopponent_limit値を破棄する必要がありますか?また、実際の人間の対戦相手の変動を考慮に入れるためにこれをどのように変更できますか?たとえば、実際の人間は、「感情」に基づいて15に留まる可能性が高く、「感情」に基づいて18にヒットする可能性もあります。