Какова оптимальная стратегия выигрыша для этой модифицированной игры в блэкджек?

Вопросы

Есть ли лучшее значение, на котором стоит остановиться, чтобы выиграть наибольший процент игр? Если да, то какое?

Редактировать: Существует ли точная вероятность выигрыша, которую можно рассчитать для данного лимита, независимо от того, что делает соперник? (Я не занимался вероятностью и статистикой со времен колледжа). Мне было бы интересно увидеть этот ответ, чтобы сравнить его с моими смоделированными результатами.

Edit: Исправлены ошибки в моем алгоритме, обновлена таблица результатов.

Предыстория

Я играл в модифицированную игру блэкджек с некоторыми довольно раздражающими изменениями правил по сравнению со стандартными правилами. Я выделил курсивом правила, которые отличаются от стандартных правил блэкджека, а также включил правила блэкджека для тех, кто не знаком с ними.

Модифицированные правила блэкджека

  1. Ровно два игрока (дилер не имеет значения)
  2. Каждый игрок сдает по две карты лицом вниз
    • Ни один из игроков _никогда_ не знает значение _любой_ карты противника
    • Ни один из игроков не знает значение руки противника, пока _они_ не закончат игру
  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, превышает 21 (победа)
    • Ни один из игроков не знает, сколько карт использовал другой в любой момент
  6. После того, как оба игрока либо остались, либо победили, победитель определяется согласно правилу 3. выше.
  7. После каждой раздачи вся колода перетасовывается, и все 52 карты снова становятся игровыми

Что такое колода карт?

Колода карт состоит из 52 карт, по четыре каждого из следующих 13 значений:

2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, A

Никакие другие свойства карт не имеют значения.

Рубиновое представление этого:

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 колоды. 100 000 занимает около 5 минут на быстром macbook pro.

Результат (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, основываясь на "чувстве"

9
задан hobodave 21 February 2010 в 10:51
поделиться

3 ответа

Я с подозрением отношусь к вашим результатам. Например, если противник стремится к 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        --

Несколько разных замечаний:

Текущая конструкция негибкая. С помощью небольшого рефакторинга можно было бы добиться чистого разделения между работой игры (дилинг, тасовка, ведение статистики) и принятием решений игроком. Это позволило бы вам тестировать различные стратегии друг против друга. В настоящее время ваши стратегии встроены в циклы, которые запутаны в коде работы игры. Для экспериментов лучше подойдет дизайн, позволяющий создавать новых игроков и задавать их стратегию по своему усмотрению.

4
ответ дан 4 December 2019 в 23:06
поделиться

Два комментария:

  1. Похоже, что не существует ни одной доминирующей стратегии, основанной на "пределе попадания":

    • Если вы выбираете 16, ваш противник может выбрать 17
    • если вы выбираете 17, ваш противник может выбрать 18
    • если вы выбираете 18, ваш противник может выбрать 19
    • если вы выбираете 19, ваш противник может выбрать 20
    • если вы выбираете 20, ваш противник может выбрать 12
    • если вы выбираете 12, ваш противник может выбрать 16.

2. Вы не упоминаете, могут ли игроки видеть, сколько карт вытянул их противник (я бы предположил, что да). Я бы ожидал, что эта информация будет включена в "лучшую" стратегию. (ответ)


При отсутствии информации о решениях других игроков игра становится проще. Но поскольку явно не существует доминирующей "чистой" стратегии, оптимальной стратегией будет "смешанная" стратегия. То есть: набор вероятностей для каждого счета от 12 до 21 для того, следует ли вам остановиться или взять еще одну карту (EDIT: вам понадобятся разные вероятности для данного счета без тузов и счета с тузами). Выполнение стратегии требует случайного выбора (в соответствии с вероятностями), остановиться или продолжить после каждого нового розыгрыша. После этого можно найти равновесие Нэша для игры.

Конечно, если вы задаете более простой вопрос: какова оптимальная стратегия выигрыша против субоптимальных игроков (например, тех, которые всегда останавливаются на 16, 17, 18 или 19), то вы задаете совершенно другой вопрос, и вам придется точно указать, в чем именно другой игрок ограничен по сравнению с вами.

2
ответ дан 4 December 2019 в 23:06
поделиться

Вот некоторые мысли о собранных вами данных:

  • Они в меру полезны для того, чтобы сказать вам, каким должен быть ваш "предел попадания", но только если вы знаете, что ваш противник следует аналогичной стратегии "предела попадания".
  • Даже в этом случае, он будет действительно полезен, только если вы знаете, каков "лимит попаданий" вашего противника или может быть таковым. Вы можете просто выбрать лимит, который даст вам больше побед, чем им.
  • Вы можете более или менее игнорировать фактические значения в таблице. Важно, положительные они или отрицательные.

Чтобы показать данные другим способом, первое число - это лимит вашего противника, а вторая группа чисел - это лимиты, которые вы можете выбрать и выиграть. То, что отмечено звездочкой, является "самым выигрышным" выбором:

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 является самым безопасным вариантом, если противник следует случайной стратегии выбора "хит-лимита", потому что 17 и 18 побьют 7/10 "хит-лимитов" противника.

Конечно, если ваш оппонент - человек, вы не сможете ответить на его самонавязывание "хит-лимита" меньше 18 или больше 19, так что это полностью сводит на нет предыдущие расчеты. Тем не менее, я считаю эти цифры полезными:


Я согласен, что для любой отдельной руки вы можете быть достаточно уверены, что у вашего оппонента есть лимит, после которого он перестанет бить, и он останется. Если вы можете угадать этот предел, вы можете выбрать свой собственный предел, основываясь на этой оценке.

Если вы думаете, что они оптимистичны или готовы рискнуть, выберите предел 20 - вы победите их в долгосрочной перспективе, если их предел выше 17. Если вы действительно уверены в себе, выбирайте лимит 12 - вы выиграете, если их лимит выше 18, и выигрыши здесь будут гораздо чаще.

Если вы считаете, что они консервативны или не склонны к риску, выберите лимит 18. Это позволит выиграть, если они сами не превышают 18.

Для нейтральной позиции подумайте о том, каким был бы ваш предел без внешнего влияния. Обычно вы бы сделали 16? 17?

Короче говоря, вы можете только догадываться о том, каков предел вашего противника, но если вы угадаете хорошо, то сможете победить его в долгосрочной перспективе с такой статистикой.

1
ответ дан 4 December 2019 в 23:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: