Nesta página
Minha metodologia para análise de video poker
Uma pergunta que me fazem de vez em quando é como consegui que meu programa de vídeo pôquer avaliasse uma tabela de pagamentos em menos de um minuto. Esta página tentará responder a essa pergunta.
Meu programa original usava um método de força bruta para percorrer todas as 2.598.960 mãos iniciais e, em seguida, jogar todas as 32 maneiras possíveis de descartar, incluindo percorrer todas as 1.533.939 cartas de reposição ao descartar todas as cinco. Isso foi por volta de 1998. No meu computador da época, calculei que levaria mais de um ano para terminar. Um programa semelhante hoje levaria apenas cerca de um mês. No entanto, com dois atalhos, você pode reduzir o tempo de cerca de um mês para cerca de três segundos. Veja a seguir como fazer isso.
Para reduzir o tempo de execução para alguns dias, você pode evitar analisar mãos semelhantes na distribuição. Por exemplo, se a mão inicial fosse quatro ases e um rei, não faria diferença qual fosse o naipe do rei. Economizaria tempo atribuir um naipe arbitrário ao rei e multiplicar o resultado por quatro. Usando essa mesma lógica, o número de tipos diferentes de mãos iniciais pode ser reduzido de 2.598.960 para 134.459. As tabelas a seguir mostram as maneiras de organizar os naipes e a respectiva ponderação para cada classe de mãos por valor.
Cinco SolteirosPercorra todas as combin(13,5)=1.287 maneiras possíveis de escolher 5 valores diferentes dentre 13. Para cada combinação de valores, defina os naipes (numerados de 1 a 4) e os pesos da seguinte forma. Por exemplo, a primeira linha atribui a cada valor unitário o naipe 1. Existem quatro naipes possíveis, então, em vez de fazer isso quatro vezes, faça uma vez e multiplique os resultados pelo peso 4.
Cinco patentes únicas
| Canto 1 | Cante. 2 | Cante. 3 | Cante. 4 | Cantar.5 | Peso|
|---|---|---|---|---|---|
| 1 | 1 | 1 | 1 | 1 | 4 |
| 2 | 1 | 1 | 1 | 1 | 12 |
| 1 | 2 | 1 | 1 | 1 | 12 |
| 1 | 1 | 2 | 1 | 1 | 12 |
| 1 | 1 | 1 | 2 | 1 | 12 |
| 1 | 1 | 1 | 1 | 2 | 12 |
| 2 | 2 | 1 | 1 | 1 | 12 |
| 2 | 1 | 2 | 1 | 1 | 12 |
| 2 | 1 | 1 | 2 | 1 | 12 |
| 2 | 1 | 1 | 1 | 2 | 12 |
| 1 | 2 | 2 | 1 | 1 | 12 |
| 1 | 2 | 1 | 2 | 1 | 12 |
| 1 | 2 | 1 | 1 | 2 | 12 |
| 1 | 1 | 2 | 2 | 1 | 12 |
| 1 | 1 | 2 | 1 | 2 | 12 |
| 1 | 1 | 1 | 2 | 2 | 12 |
| 2 | 3 | 1 | 1 | 1 | 24 |
| 2 | 1 | 3 | 1 | 1 | 24 |
| 2 | 1 | 1 | 3 | 1 | 24 |
| 2 | 1 | 1 | 1 | 3 | 24 |
| 1 | 2 | 3 | 1 | 1 | 24 |
| 1 | 2 | 1 | 3 | 1 | 24 |
| 1 | 2 | 1 | 1 | 3 | 24 |
| 1 | 1 | 2 | 3 | 1 | 24 |
| 1 | 1 | 2 | 1 | 3 | 24 |
| 1 | 1 | 1 | 2 | 3 | 24 |
| 1 | 1 | 2 | 2 | 3 | 24 |
| 1 | 2 | 1 | 2 | 3 | 24 |
| 1 | 2 | 2 | 1 | 3 | 24 |
| 1 | 1 | 2 | 3 | 2 | 24 |
| 1 | 2 | 1 | 3 | 2 | 24 |
| 1 | 2 | 2 | 3 | 1 | 24 |
| 1 | 1 | 3 | 2 | 2 | 24 |
| 1 | 2 | 3 | 1 | 2 | 24 |
| 1 | 2 | 3 | 2 | 1 | 24 |
| 1 | 3 | 1 | 2 | 2 | 24 |
| 1 | 3 | 2 | 1 | 2 | 24 |
| 1 | 3 | 2 | 2 | 1 | 24 |
| 3 | 1 | 1 | 2 | 2 | 24 |
| 3 | 1 | 2 | 1 | 2 | 24 |
| 3 | 1 | 2 | 2 | 1 | 24 |
| 4 | 4 | 1 | 2 | 3 | 24 |
| 4 | 1 | 4 | 2 | 3 | 24 |
| 4 | 2 | 3 | 4 | 1 | 24 |
| 4 | 1 | 2 | 3 | 4 | 24 |
| 1 | 4 | 4 | 2 | 3 | 24 |
| 1 | 4 | 2 | 4 | 3 | 24 |
| 1 | 4 | 2 | 3 | 4 | 24 |
| 2 | 3 | 4 | 4 | 1 | 24 |
| 2 | 3 | 4 | 1 | 4 | 24 |
| 1 | 2 | 3 | 4 | 4 | 24 |
Par
Percorra todas as 13×combin(12,3)=2.860 maneiras possíveis de escolher um valor para o par e três valores dentre os 12 restantes para as três cartas isoladas. Para cada combinação de valores, defina os naipes (numerados de 1 a 4) e os pesos da seguinte forma. Por exemplo, a primeira linha define os naipes do par como 1 e 2, e os naipes das cartas isoladas como 1. Existem combin(4,2)=6 maneiras de escolher os naipes do par e 2 maneiras de escolher um naipe para as cartas isoladas igual a um dos naipes do par, resultando em um peso de 6×2=12.
Par
| Par 1 | Par 2 | Canto 1 | Cante. 2 | Cante. 3 | Peso |
|---|---|---|---|---|---|
| 1 | 2 | 1 | 1 | 1 | 12 |
| 1 | 2 | 1 | 1 | 2 | 12 |
| 1 | 2 | 1 | 2 | 1 | 12 |
| 1 | 2 | 2 | 1 | 1 | 12 |
| 1 | 2 | 1 | 1 | 3 | 24 |
| 1 | 2 | 1 | 3 | 1 | 24 |
| 1 | 2 | 3 | 1 | 1 | 24 |
| 1 | 2 | 1 | 3 | 3 | 24 |
| 1 | 2 | 3 | 1 | 3 | 24 |
| 1 | 2 | 3 | 3 | 1 | 24 |
| 1 | 2 | 3 | 3 | 3 | 12 |
| 1 | 2 | 1 | 2 | 3 | 24 |
| 1 | 2 | 1 | 3 | 2 | 24 |
| 1 | 2 | 3 | 1 | 2 | 24 |
| 1 | 2 | 3 | 4 | 4 | 12 |
| 1 | 2 | 4 | 3 | 4 | 12 |
| 1 | 2 | 4 | 4 | 3 | 12 |
| 1 | 2 | 1 | 3 | 4 | 24 |
| 1 | 2 | 3 | 1 | 4 | 24 |
| 1 | 2 | 3 | 4 | 1 | 24 |
Dois pares
Percorra todas as combinações possíveis de 13 (combin(13,2)×11=858) para escolher dois valores dentre os 13 para os dois pares e um valor dentre os 11 restantes para a carta isolada. Para cada combinação de valores, defina os naipes (numerados de 1 a 4) e os pesos da seguinte forma. Por exemplo, a primeira linha define os naipes do primeiro par como 1 e 2, os naipes do segundo par como 3 e 4 e o naipe da carta isolada como 1. Existem 6 combinações possíveis de 4 (combin(4,2)=6) para escolher os naipes do primeiro par. O segundo par possui os outros dois naipes, portanto, há apenas um 1 para escolhê-los. A carta isolada pode ter qualquer um dos naipes do primeiro par, então há duas possibilidades. Assim, o peso na primeira linha é 6×1×2=12.
Dois pares
| Par 1 Cartão 1 | Par 1 Cartão 2 | Par 2 Cartão 1 | Par 2 Cartão 2 | Canto 1 | Peso |
|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 1 | 12 |
| 1 | 2 | 3 | 4 | 3 | 12 |
| 1 | 2 | 1 | 3 | 1 | 24 |
| 1 | 2 | 1 | 3 | 2 | 24 |
| 1 | 2 | 1 | 3 | 3 | 24 |
| 1 | 2 | 1 | 3 | 4 | 24 |
| 1 | 2 | 1 | 2 | 1 | 12 |
| 1 | 2 | 1 | 2 | 3 | 12 |
Três de um mesmo tipo
Percorra todas as 13 × combin(12,2) = 858 maneiras possíveis de escolher um naipe dentre os 13 para formar uma trinca e 66 maneiras de escolher dois naipes dentre os outros 12. Para cada combinação de naipes, defina os valores (numerados de 1 a 4) e os pesos da seguinte forma. Por exemplo, a primeira linha define os naipes da trinca como 1, 2 e 3, e os naipes dos dois naipes individuais como dois dos três naipes representados na trinca. Existem combin(4,3) = 4 maneiras de escolher 3 dos 4 naipes para a trinca, 3 maneiras de escolher um naipe dentre esses três para o primeiro naipe individual e 2 maneiras de escolher um naipe para o segundo naipe individual. Assim, o peso para a primeira linha é 4 × 3 × 2 = 24.
Três de um mesmo tipo
| 3 tipos Cartão 1 | 3 tipos Cartão 2 | 3 tipos Cartão 3 | Canto 1 | Cante. 2 | Peso |
|---|---|---|---|---|---|
| 1 | 2 | 3 | 1 | 2 | 24 |
| 1 | 2 | 3 | 1 | 4 | 12 |
| 1 | 2 | 3 | 4 | 1 | 12 |
| 1 | 2 | 3 | 1 | 1 | 12 |
| 1 | 2 | 3 | 4 | 4 | 4 |
Casa cheia
Percorra todas as 13×12=156 maneiras possíveis de escolher um valor entre 13 para a trinca e 12 maneiras de escolher um valor para o par. Para cada combinação de valores, defina os naipes (numerados de 1 a 4) e os pesos da seguinte forma. Por exemplo, a primeira linha define os naipes do par como 1 e 2, e os naipes da trinca como 1, 2 e 3. Existem combin(4,2)=6 maneiras de escolher os naipes para o par. A trinca usa os dois naipes do par e um dos outros dois. Assim, o peso para a primeira linha é 6×2×2=12.
Casa cheia
| Par Cartão 1 | Par Cartão 2 | 3 tipos Cartão 1 | 3 tipos Cartão 2 | 3 tipos Cartão 3 | Peso |
|---|---|---|---|---|---|
| 1 | 2 | 1 | 2 | 3 | 12 |
| 1 | 4 | 1 | 2 | 3 | 12 |
Quatro de um mesmo tipo
Percorra todas as 13×12=156 maneiras possíveis de escolher um valor entre 13 para a quadra e 12 maneiras de escolher um valor para o singleton. Para cada combinação de valores, defina os naipes (numerados de 1 a 4) e os pesos da seguinte forma. Por exemplo, a primeira linha define os naipes da quadra como 1, 2, 3 e 4, e os naipes do singleton como 1. Há apenas uma maneira de escolher 4 naipes entre 4 para a trinca e 4 maneiras de escolher um naipe entre 4 para o singleton. Portanto, o peso para a primeira linha é 1×4×2=4.
Quatro de um mesmo tipo
| 4 tipos Cartão 1 | 4 tipos Cartão 2 | 4 tipos Cartão 3 | 4 tipos Cartão 4 | Canto 1 | Peso |
|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 1 | 4 |
A etapa acima reduzirá o tempo de computação em 95%, mas ainda levará várias horas se você percorrer as 1.533.939 combinações possíveis de cartas de substituição. O segredo para um programa de três segundos é não usar um loop na etapa de compra de cartas. Veja como fazer:
- Inicialize os seguintes arrays:
- Matriz 1: tamanho 2.598.960
- Matriz 2: tamanho 270.725 por 16
- Matriz 3: tamanho 22100 por 16
- Matriz 4: tamanho 1326 por 16
- Matriz 5: tamanho 52 por 16
- Matriz 6: tamanho 16
- Percorra todas as 2.598.960 combinações de 5 cartas dentre as 52. NÃO utilize o atalho das 134.459 mãos neste momento. Para cada mão distribuída, faça o seguinte:
- Atribua uma pontuação de acordo com seu valor no pôquer.
- Coloque a pontuação no array0. Coloque a primeira mão no elemento 0 do array e incremente em 1 para cada mão subsequente.
- Para cada uma das 5 maneiras de escolher 4 das 5 cartas na distribuição, traduza as quatro cartas em um número de índice de 0 a 270.724 (explicarei como fazer isso mais tarde) e incremente o elemento [número de índice][pontuação da mão] da matriz1 em 1.
- Para cada uma das 10 maneiras de escolher 3 das 5 cartas na distribuição, traduza as três cartas em um número de índice de 0 a 22.099 e incremente o elemento [número de índice][pontuação da mão] da matriz2 em 1.
- Para cada uma das 10 maneiras de escolher 2 entre 5 cartas na distribuição, traduza as duas cartas em um número de índice de 0 a 1.325 e incremente o elemento [número de índice][pontuação da mão] da matriz3 em 1.
- Para cada uma das 5 maneiras de escolher 1 entre 5 cartas na distribuição, traduza a carta em um número de índice de 0 a 51 e incremente o elemento [número de índice][pontuação da mão] da matriz4 em 1.
- Incrementa o elemento [pontuação da mão] do array5 em 1.
- Em seguida, percorra as 134.459 classes de mãos explicadas acima.
- Para determinar o valor de possuir todas as cinco cartas, traduza as cinco cartas para um número de índice e procure o valor de pôquer no array lin array0.
- Para determinar o valor de ter quatro cartas quaisquer, traduza as quatro cartas para um número de índice e consulte os resultados possíveis no descarte no elemento correspondente da matriz1. Isso, no entanto, incluirá receber a carta descartada na distribuição. Portanto, você deve subtrair um do elemento na matriz associado ao valor de pôquer de ter todas as cinco cartas. Por exemplo, se você tiver J♣, Q♣, K♣, A♣ e descartar 2♥, haverá 1 maneira de obter um royal flush, 8 maneiras de obter um flush, 3 maneiras de obter uma sequência, 12 maneiras de obter um par de valetes ou melhor e 23 maneiras de obter uma mão perdedora. No entanto, a matriz1 indicará que existem 24 maneiras de obter uma mão perdedora, incluindo receber o 2♥ no descarte. Portanto, você precisa subtrair o resultado de ter todas as cartas dos resultados possíveis das 5 maneiras de ter 4 cartas.
- Certifique-se de compreender a lógica da etapa anterior, pois a levaremos a um nível superior nesta etapa. Para as 10 maneiras de segurar quaisquer 3 cartas, você consultará os resultados possíveis na matriz 2. Em seguida, subtraia os resultados possíveis da matriz 1 para as 3 cartas que você está segurando e para cada uma das cartas que você está descartando. Por exemplo, para o valor de manter 2♣ 2♥ 2♠ e descartar 4♥ e J♠, comece com os valores na matriz 2 para 2♣ 2♥ 2♠ e, em seguida, subtraia os valores para 2♣ 2♥ 2♠ 4♥ e 2♣ 2♥ 2♠ J♠. No entanto, isso subtrairá duas vezes se você segurar todas as cinco cartas. Portanto, você precisa adicionar de volta o resultado obtido ao segurar todas as 5 cartas.
- Seguindo a mesma lógica, para armazenar quaisquer 2 cartas, comece com os valores em array3, subtraia os conjuntos apropriados de cartas de array2, adicione novamente os conjuntos apropriados de cartas de array1 e subtraia de volta o elemento que contém tudo de array0.
- Para armazenar 1 carta, comece com os valores correspondentes do array4, subtraia os valores apropriados do array3, adicione novamente os valores apropriados do array2, subtraia os valores apropriados do array1 e adicione o valor apropriado do array0.
- Para descartar tudo, comece com os valores em array5, depois subtraia os valores apropriados de array4, adicione novamente os valores apropriados de array3, subtraia novamente os valores apropriados de array2, adicione novamente os valores apropriados de array1 e subtraia o valor apropriado de array0.
- Agora você deve ter o número de combinações de todos os resultados possíveis para as 32 maneiras de jogar a mão. Determine o valor esperado de cada uma. Para a jogada que resulta no maior valor esperado, adicione a uma matriz que representa os resultados possíveis do jogo como um todo os resultados possíveis dessa jogada. Lembre-se de multiplicar pelo peso associado à mão na distribuição das cartas.
- Após percorrer todas as 134.459 mãos iniciais possíveis, você deverá ter o número de maneiras de obter cada mão no descarte. Use esse array para determinar o retorno total do jogo.
A seguir, apresentamos quatro sub-rotinas para traduzir de 2 a 5 cartas (numeradas de 0 a 51) e retornar um valor de índice.
int HandIndex2(int c1, int c2){ int r; r=combin_array[52][2]-combin_array[52-c1][2]; r+=combin_array[51-c1][1]-combin_array[52-c2][1]; return r;}int HandIndex3(int c1, int c2, int c3){ int r; r=combin_array[52][3]-combin_array[52-c1][3]; r+=combin_array[51-c1][2]-combin_array[52-c2][2]; r+=combin_array[51-c2][1]-combin_array[52-c3][1]; return r;}int HandIndex4(int c1, int c2, int c3, int c4){ int r; r=combin_array[52][4]-combin_array[52-c1][4]; r+=combin_array[51-c1][3]-combin_array[52-c2][3]; r+=combin_array[51-c2][2]-combin_array[52-c3][2]; r+=combin_array[51-c3][1]-combin_array[52-c4][1]; return r;}int HandIndex5(int CardIndex[]){ int r; r=combin_array[52][5]-combin_array[52-CardIndex[0]][5]; r+=combin_array[51-CardIndex[0]][4]-combin_array[52-CardIndex[1]][4]; r+=combin_array[51-CardIndex[1]][3]-combin_array[52-CardIndex[2]][3]; r+=combin_array[51-CardIndex[2]][2]-combin_array[52-CardIndex[3]][2]; r+=combin_array[51-CardIndex[3]][1]-combin_array[52-CardIndex[4]][1]; return r;}Links
Este site explica como o autor acelerou seu analisador de vídeo poker de um ano para sete segundos.
O VP Genius tem uma página excelente sobre programação de video poker.