Pergunta

Perguntas

Tem alguma melhor valor para permanecer para ganhar a maior porcentagem de jogos possíveis? Se assim for, o que é?

Editar: Existe uma probabilidade exata de vencer que possa ser calculada para um determinado limite, independente de qualquer que seja o oponente? (Não faço probabilidade e estatística desde a faculdade). Eu estaria interessado em ver isso como uma resposta para contrastá -la com meus resultados simulados.

Editar: Corrigido erros no meu algoritmo, tabela de resultados atualizados.

Fundo

Eu tenho jogado um jogo de blackjack modificado com alguns ajustes de regra bastante irritantes das regras padrão. Eu em itálico as regras diferentes das regras padrão do Blackjack, bem como incluíram as regras do Blackjack para aqueles que não estão familiarizados.

Regras de blackjack modificadas

  1. Exatamente dois jogadores humanos (revendedor é irrelevante)
  2. Cada jogador recebe duas cartas de bruços
    • Nenhum dos jogadores _ever_ conhece o valor de _any_ das cartas do oponente
    • Nenhum dos jogadores sabe o valor da mão do oponente até que você tenha terminado a mão
  3. O objetivo é chegar o mais próximo possível de 21. Resultados:
    • Se o jogador de A&B tem uma pontuação idêntica, o jogo é um empate
    • Se o jogador de A&B tem uma pontuação acima de 21 (um busto), o jogo é um empate
    • Se a pontuação do jogador A for <= 21 e o jogador B quebrou, o jogador A vence
    • Se a pontuação do jogador A é maior que o jogador B, e nenhum dos dois, o jogador A vence
    • Caso contrário, o jogador A perdeu (B venceu).
  4. Cartões valem a pena:
    • Os cartões 2 a 10 valem a quantidade correspondente de pontos
    • Cartões j, q, k valem 10 pontos
    • Card Ace vale 1 ou 11 pontos
  5. Cada jogador pode solicitar cartas adicionais uma de cada vez até:
    • O jogador não quer mais (ficar)
    • A pontuação do jogador, com qualquer ases contado como 1, excede 21 (busto)
    • Nenhum jogador sabe quantas cartas o outro usou a qualquer momento
  6. Depois que os dois jogadores permaneceram ou prejudicaram, o vencedor é determinado por regra 3 acima.
  7. Depois de cada mão, todo o baralho é reorganizado e todas as 52 cartas estão em jogo novamente

O que é um baralho de cartas?

Um baralho de cartas consiste em 52 cartas, quatro cada um dos 13 valores a seguir:

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

Nenhuma outra propriedade dos cartões é relevante.

Uma representação rubi disso é:

CARDS = ((2..11).to_a+[10]*3)*4

Algoritmo

Eu tenho me aproximado disso da seguinte maneira:

  • Eu sempre vou querer acertar se minha pontuação for de 2 a 11, pois é impossível quebrar
  • Para cada uma das pontuações 12 a 21, simularei as mãos contra um oponente
    • Para essas n mãos, a pontuação será meu "limite". Quando eu atingir o limite ou mais, eu irei fique.
    • Meu oponente seguirá exatamente a mesma estratégia
    • Vou simular n mãos para cada permutação dos conjuntos (12..21), (12..21)
  • Imprima a diferença de vitórias e perdas para cada permutação, bem como a diferença de perda de vitória líquida

Aqui está o algoritmo implementado em 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

Uso

ruby blackjack.rb [num_iterations] [num_decks]

O script padrão é de 100.000 iterações e 4 decks. 100.000 leva cerca de 5 minutos em um MacBook Pro rápido.

Saída (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

Interpretação

É aqui que eu luto. Não tenho muita certeza de como interpretar esses dados. À primeira vista, parece que sempre Ficar aos 16 ou 17 anos é o caminho a seguir, mas não tenho certeza se é tão fácil. eu acho que é improvável Que um oponente humano real permaneceria em 12, 13 e possivelmente 14, então devo jogar fora esses valores do oponente_limit? Além disso, como posso modificar isso para levar em consideração a variabilidade de um oponente humano real? por exemplo, um humano real provavelmente permanecerá em 15 apenas com base em um "sentimento" e também pode atingir 18 com base em um "sentimento"

Foi útil?

Solução

Eu suspeito dos seus resultados. Por exemplo, se o oponente pretende 19, seus dados dizem que a melhor maneira de vencê -lo é acertar até você atingir 20. Isso não passa em um teste básico de cheiro. Tem certeza de que não tem um bug? Se meu oponente estiver se esforçando por 19 ou melhor, minha estratégia seria evitar o fracasso a todo custo: permaneça em qualquer coisa 13 ou superior (talvez até 12?). Ir para 20 tem que errado - e não apenas por uma pequena margem, mas por muita coisa.

Como sei que seus dados são ruins? Porque O jogo de blackjack que você está jogando não é incomum. É a maneira como um revendedor toca na maioria dos cassinos: o revendedor atinge um alvo e depois para, independentemente do que os outros jogadores mantêm em suas mãos. O que é esse alvo? Fique no Hard 17 e acerte o Soft 17. Quando você se livrar dos insetos do seu script, ele deve confirmar que os cassinos conhecem seus negócios.

Quando eu faço as seguintes substituições ao seu código:

# 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

Os resultados concordam com o comportamento dos revendedores de cassinos: 17 é o alvo ideal.

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        --

Alguns comentários diversos:

O design atual é inflexível. Com apenas uma pequena refatoração, você pode obter uma separação limpa entre a operação do jogo (negociando, embaralhando, mantendo estatísticas em execução) e a tomada de decisão do jogador. Isso permitiria que você testasse várias estratégias uma contra a outra. Atualmente, suas estratégias estão incorporadas em loops que estão todos emaranhados no código de operação do jogo. Sua experimentação seria melhor servida por um design que lhe permitia criar novos jogadores e definir sua estratégia à vontade.

Outras dicas

Dois comentários:

  1. Parece que não há uma única estratégia dominante baseada em um "limite de sucesso":

    • Se você escolher 16, seu oponente pode escolher 17
    • Se você escolher 17, seu oponente pode escolher 18
    • Se você escolher 18, seu oponente pode escolher 19
    • Se você escolher 19, seu oponente pode escolher 20
    • Se você escolher 20, seu oponente pode escolher 12
    • Se você escolher 12, seu oponente poderá escolher 16.

2. Você não menciona se os jogadores podem ver quantas cartas seu oponente desenhou (eu acho que sim). Eu esperaria que essas informações fossem incorporadas à estratégia "melhor". (respondidas)


Sem informações sobre as decisões de outros jogadores, o jogo fica mais simples. Mas já que claramente não há não dominante "puro" estratégia, a estratégia ideal será uma "misturado"Estratégia. isto é: um conjunto de probabilidades para cada pontuação de 12 a 21 para se você deve parar ou desenhar outra carta (editar: você precisará de probabilidades diferentes para uma determinada pontuação sem ACEs versus a pontuação com ases.) Executando o A estratégia então exige que você escolha aleatoriamente (de acordo com as probabilidades) se deve parar ou continuar após cada novo empate. Você pode encontrar o equilíbrio de Nash para o jogo.

Obviamente, se você está apenas fazendo a pergunta mais simples: qual é a estratégia de vitória ideal contra jogadores abaixo do ideal (por exemplo, que sempre param em 16, 17, 18 ou 19), você está fazendo uma pergunta totalmente diiferente, e você terá que ter que ter que Especifique exatamente de que maneira o outro jogador é limitado em comparação com você.

Aqui estão algumas reflexões sobre os dados que você coletou:

  • É um pouco útil para dizer o que seu "limite de hit" deve ser, mas apenas se você souber que seu oponente está seguindo uma estratégia semelhante "Hit Limit".
  • Mesmo assim, é apenas verdade Útil se você souber qual é o "limite de hit" do seu oponente ou provavelmente será. Você pode simplesmente escolher um limite que lhe dê mais vitórias do que elas.
  • Você pode ignorar mais ou menos os valores reais na tabela. É se eles são positivos ou negativos que importa.

Para mostrar seus dados de outra maneira, o primeiro número é o limite do seu oponente e o segundo grupo de números são os limites que você pode escolher e vencer. Aquele com um asterisco é a escolha "mais vencedora":

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

A partir disso, você pode ver que um limite de hit de 17 ou 18 é a opção mais segura se o oponente estiver seguindo uma estratégia de seleção aleatória "Hit Limit", porque 17 e 18 vencerão 7/10 "Limites de sucesso".

Obviamente, se o seu oponente é humano, você não pode responder a eles auto-impondo um "limite de acerto" com menos de 18 anos ou mais de 19, de modo que nega completamente os cálculos anteriores. Eu ainda acho que esses números são úteis no entanto:


Concordo que, por qualquer mão individual, você pode estar razoavelmente confiante de que seu oponente terá um limite, após o qual eles pararão de bater, e eles ficarão. Se você conseguir adivinhar esse limite, poderá escolher seu próprio limite com base nessa estimativa.

Se você acha que eles estão sendo otimistas ou felizes em arriscar, escolha um limite de 20 - você os vencerá a longo prazo, desde que seu limite seja acima de 17. Se você estiver realmente confiante, escolha um limite de 12 - Isso vencerá se o limite deles estiver acima de 18 e há ganhos muito mais frequentes a serem adquiridos aqui.

Se você acha que eles estão sendo conservadores ou avessos ao risco, escolha um limite de 18. Isso vencerá se eles ficarem em qualquer lugar abaixo dos 18.

Para terreno neutro, talvez pense em qual seria seu limite sem nenhuma influência externa. Você normalmente acertaria em 16? A 17?

Em suma, você só pode adivinhar qual é o limite do seu oponente, mas se você adivinhar bem, poderá vencê -los a longo prazo com essas estatísticas.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top