9

質問

可能な限り多くのゲームに勝つために、とどまるのに最適な価値はありますか?もしそうなら、それは何ですか?

編集:対戦相手が何をするかに関係なく、与えられた制限に対して計算できる正確な勝率はありますか?(私は大学以来、確率と統計を行っていません)。それを私のシミュレーション結果と対比するための答えとして見たいと思います。

編集:私のアルゴリズムのバグを修正し、結果テーブルを更新しました。

バックグラウンド

私は、標準のルールからかなり厄介なルールの微調整を加えた修正ブラックジャックゲームをプレイしてきました。標準のブラックジャックのルールとは異なるルールをイタリック体で示し、慣れていない人のためにブラックジャックのルールを含めました。

変更されたブラックジャックルール

  1. 正確に2人の人間のプレーヤー(ディーラーは無関係です)
  2. 各プレイヤーには裏向きに2枚のカードが配られます
    • どちらのプレイヤーも、対戦相手のカードの_any_の値を_決して_知りません。
    • どちらのプレーヤーも、_両方_がハンドを終了するまで、対戦相手のハンドの価値を知りません。
  3. 目標は、可能な限り21のスコアに近づくことです。結果:
    • プレーヤーのAとBのスコアが同じである場合、ゲームは引き分けです
    • プレーヤーのAとBの両方のスコアが21(バスト)を超えている場合、ゲームは引き分けです
    • プレーヤーAのスコアが21未満で、プレーヤーBがバストした場合、プレーヤーAが勝ちます
    • プレーヤーAのスコアがプレーヤーBのスコアよりも大きく、どちらもバストしていない場合、プレーヤーAが勝ちます
    • それ以外の場合、プレーヤーAは負けました(Bが勝ちました)。
  4. カードの価値は次のとおりです。
    • カード2から10は、対応するポイント数の価値があります
    • カードJ、Q、Kは10ポイントの価値があります
    • カードエースは1または11ポイントの価値があります
  5. 各プレイヤーは、次の状態になるまで、一度に1枚ずつ追加のカードを要求できます。
    • プレイヤーはこれ以上欲しくない(滞在)
    • エースを1としてカウントしたプレーヤーのスコアは、21(バスト)を超えています。
    • どちらのプレイヤーも、相手がいつでも何枚のカードを使用したかを知りません。
  6. 両方のプレイヤーが滞在またはバストした後、勝者は上記のルール3に従って決定されます。
  7. 各ハンドの後、デッキ全体が再シャッフルされ、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にヒットする可能性もあります。

4

3 に答える 3

4

私はあなたの結果に疑いを持っています。たとえば、対戦相手が19を目指している場合、データによると、対戦相手を倒す最善の方法は、20に達するまで攻撃することです。これは、基本的な嗅覚テストに合格しません。バグはありませんか?対戦相手が19以上を目指して努力している場合、私の戦略は、いかなる犠牲を払ってもバストを回避することです。13以上(おそらく12以上)にとどまります。20に行くことは間違っている必要があります-そして、わずかな差だけでなく、多くの点で。

あなたのデータが悪いことをどうやって知ることができますか?あなたがプレイしているブラックジャックゲームは珍しいことではないからです。これは、ほとんどのカジノでディーラーがプレイする方法です。ディーラーは、他のプレーヤーが手に持っているものに関係なく、ターゲットにヒットしてから停止します。そのターゲットは何ですか?ハード17に立ち、ソフト17を押します。スクリプトのバグを取り除くときは、カジノがビジネスを知っていることを確認する必要があります。

私があなたのコードに次のような置き換えをするとき:

# Replace scoring method.
def score
  s = inject(0) { |sum, c| sum + c }
  return s if s < 21
  n_aces = find_all { |c| c == 11 }.size
  while s > 21 and n_aces > 0
      s -= 10
      n_aces -= 1
  end
  return s
end

# Replace section of code determining hand outcome.
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_score == opp_score
  draw += 1
elsif my_score > opp_score
  win += 1
else
  lose += 1
end

結果はカジノディーラーの行動と一致しています:17が最適なターゲットです。

n=10000
                                                       opponent_limit
my_limit |        12        13        14        15        16        17        18        19        20        21       net
-------- |  --------  --------  --------  --------  --------  --------  --------  --------  --------  --------  --------
      12 |        --      -843     -1271     -1380     -1503     -1148      -137      1234      3113      6572
      13 |       843        --      -642     -1041     -1141      -770       -93      1137      2933      6324
      14 |      1271       642        --      -498      -784      -662        93      1097      2977      5945
      15 |      1380      1041       498        --      -454      -242      -100       898      2573      5424
      16 |      1503      1141       784       454        --      -174        69       928      2146      4895
      17 |      1148       770       662       242       174        --        38       631      1920      4404
      18 |       137        93       -93       100       -69       -38        --       489      1344      3650
      19 |     -1234     -1137     -1097      -898      -928      -631      -489        --       735      2560
      20 |     -3113     -2933     -2977     -2573     -2146     -1920     -1344      -735        --      1443
      21 |     -6572     -6324     -5945     -5424     -4895     -4404     -3650     -2560     -1443        --

いくつかの雑多なコメント

現在の設計は柔軟性がありません。ほんの少しのリファクタリングで、ゲームの操作(取引、シャッフル、実行中の統計の維持)とプレーヤーの意思決定を明確に分離することができます。これにより、さまざまな戦略を相互にテストできます。現在、戦略はすべてゲーム操作コードに絡み合っているループに埋め込まれています。あなたの実験は、あなたが新しいプレーヤーを作成し、彼らの戦略を自由に設定することを可能にするデザインによってよりよく役立つでしょう。

于 2010-02-20T15:37:07.193 に答える
2

2つのコメント:

  1. 「ヒットリミット」に基づく単一の支配的な戦略はないようです。

    • 16を選択した場合、対戦相手は17を選択できます
    • 17を選択した場合、対戦相手は18を選択できます
    • 18を選択した場合、対戦相手は19を選択できます
    • 19を選択した場合、対戦相手は20を選択できます
    • 20を選択した場合、対戦相手は12を選択できます
    • 12を選択した場合、対戦相手は16を選択できます。

2.対戦相手が何枚のカードを引いたかをプレイヤーが見ることができるかどうかについては言及していません(私はそう思います)。この情報が「最良の」戦略に組み込まれることを期待しています。(回答済み)


他のプレイヤーの決定に関する情報がないため、ゲームはよりシンプルになります。しかし、明らかに支配的な「純粋な」戦略は存在しないため、最適な戦略は「混合」戦略になります。つまり、別のカードを停止するか引くかについて、12から21までの各スコアの確率のセット(編集:エースのない特定のスコアとエースのあるスコアでは異なる確率が必要になります)。戦略を実行するには、次のようになります。新しい抽選のたびに停止するか続行するかを(確率に応じて)ランダムに選択します。次に、ゲームのナッシュ均衡を見つけることができます。

もちろん、より単純な質問だけをしている場合:次善のプレーヤー(たとえば、常に16、17、18、または19で停止するプレーヤー)に対する最適な勝利戦略は何ですか?完全に異なる質問をしているので、他のプレイヤーがあなたと比較してどのように制限されているかを正確に指定します。

于 2010-02-20T09:10:58.277 に答える
1

収集したデータについての考えは次のとおりです。

  • これは、「ヒットリミット」がどうあるべきかを伝えるのに少し役立ちますが、対戦相手が同様の「ヒットリミット」戦略に従っていることがわかっている場合に限ります。
  • それでも、対戦相手の「ヒットリミット」が何であるか、またはそうなる可能性が高いかを知っている場合にのみ、これは本当に役立ちます。あなたはあなたに彼らより多くの勝利を与える限界を選ぶことができます。
  • 表の実際の値は多かれ少なかれ無視できます。重要なのは、それらがポジティブかネガティブかです。

別の方法でデータを表示するために、最初の数字は対戦相手の制限であり、2番目の数字のグループはあなたが選択して勝つことができる制限です。アスタリスクが付いているものが「最も勝者」の選択肢です。

12:   13, 14, 15, 16*, 17, 18
13:   14, 15, 16*, 17, 18, 19
14:   15, 16, 17*, 18, 19
15:   16, 17*, 18, 19
16:   17, 18*, 19
17:   18*, 19
18:   19*, 20
19:   12, 20*
20:   12*, 13, 14, 15, 16, 17
21:   12*, 13, 14, 15, 16, 17, 18, 19, 20

このことから、17と18は7/10の対戦相手の「ヒット制限」を打ち負かすため、対戦相手がランダムな「ヒット制限」選択戦略に従っている場合、17または18のヒット制限が最も安全なオプションであることがわかります。

もちろん、対戦相手が人間である場合、18未満または19を超える「ヒット制限」を自主的に課すことで相手に返信することはできません。そのため、以前の計算は完全に無効になります。ただし、これらの数値はまだ役立つと思います。


私は、個々のハンドについて、対戦相手に制限があり、その後はヒットを停止し、そのままでいることを合理的に確信できることに同意します。その制限を推測できる場合は、その見積もりに基づいて独自の制限を選択できます。

彼らが楽観的であるか、リスクを冒して喜んでいると思う場合は、20の制限を選択してください。制限が17を超えていれば、長期的にはそれらを打ち負かすことができます。本当に自信がある場合は、制限を選択してください。 12-制限が18を超えていて、ここでより頻繁に勝つことがある場合、それが勝ちます。

彼らが保守的またはリスク回避的であると思う場合は、18の制限を選択してください。彼らが18未満のどこかに滞在している場合は、それが勝ちます。

中立的な立場の場合、外部からの影響がなければ限界がどうなるかを考えてみてください。あなたは通常16を打つでしょうか?17?

要するに、あなたは対戦相手の限界が何であるかを推測することしかできませんが、あなたがうまく推測すれば、それらの統計で長期的に彼らを打ち負かすことができます。

于 2010-02-20T08:52:42.060 に答える