Determinando as mãos de poker

19

Eu tenho feito um jogo de Texas Hold'Em como parte de uma avaliação e tenho pensado em como examinar as 7 cartas disponíveis e determinar se existem mãos.

O único método possível em que consigo pensar é classificar as cartas numericamente, depois examinar cada grupo possível de 5 cartas e verificar se elas correspondem a uma lista de todas as mãos possíveis. Isso levaria muito tempo e só seria possível para determinar pares, pois o processo é irrelevante.

As cartas são cada sequência, composta de um número / a / j / q / k e um naipe (char)3(que faz um pequeno símbolo de espadas).

Alguém tem alguma sugestão, fórmula ou link que eu possa usar para ajudar a criar um sistema de análise manual?

Não se preocupe em posicionar as mãos uma contra a outra ainda, isso é uma chaleira diferente de peixe.

Magicaxis
fonte
1
Apenas apontando, mas a tag do vetor em seu contexto é sobre álgebra linear. Não é o contêiner.
Sidar
@Sidar Sinta-se livre para editar as tags no futuro, se você acredita que elas não se encaixam. Se você passar o mouse sobre as tags e a opção "Editar tags" aparecer (pelo menos para mim?), Poderá editar apenas as tags sem editar a pergunta.
MichaelHouse
@ Byte56 Eu tenho esse hábito estranho de pensar que não tenho certeza se estou certo, então faço isso passivamente através de um comentário ... É por isso que não editei o post. Talvez eu tenha perdido algo no post, então estava esperando por sua resposta.
Sidar
Há também um belo artigo aqui que apareceu na Game Developer há alguns anos: cowboyprogramming.com/2007/01/04/programming-poker-ai
celion
1
Eu fiz uma implementação em java por diversão há algum tempo. Você pode encontrá-lo aqui: codereview.stackexchange.com/questions/10973/… . Se você procurar no PokerHand.java, encontrará métodos para testar cada tipo de mão (por exemplo, isFullHouse). Eles só funcionam se os cartões forem classificados primeiro.
bughi

Respostas:

20

Eu acho que você pode encontrar a maioria das mãos de poker simplesmente fazendo algumas mesas com quantas cartas na mão há de cada nível e naipe.

Em outras palavras, crie um ranking de cartas de mapeamento de matriz (números e A / J / Q / K) para a contagem de cartas dessa classificação na sua mão. Se o jogador tiver um par ou três do mesmo tipo, haverá um elemento nesse array igual a 2 ou 3, etc. Eles terão um full house se houver um elemento que é 2 e outro que é 3, e um straight se houver cinco elementos consecutivos iguais a 1 nessa matriz.

Da mesma forma, você pode fazer uma matriz semelhante da contagem de cartas de cada naipe e usá-la para detectar descargas.

Depois de detectar a presença de uma mão específica, é muito fácil voltar e encontrar os cartões específicos na mão, destacando-os na interface do usuário ou o que você precisar fazer.

No pseudocódigo:

int countByRank[13] = { 0 };        // Initialize counter to zero for each rank
for (cards in hand)
    countByRank[card.rank] += 1;    // Increment counter for this card's rank
if (countByRank.find(2))
    // There's a pair
else if (countByRank.find(3))
    // There's a three-of-a-kind
// etc...
Nathan Reed
fonte
17

É um pouco complicado, porque há muitas combinações. Felizmente, você tem um processador que pode verificar um grande número de combinações em um tempo muito curto.

Você precisará de algumas estratégias diferentes para detectar diferentes tipos de mãos. Felizmente, alguns dos diferentes tipos podem se sobrepor a estratégias. Eu procuraria, por ordem de posição da mão.

  1. Straight flush
  2. Quatro de um tipo
  3. Casa cheia
  4. Rubor
  5. Direto
  6. Três de um tipo
  7. Dois pares
  8. Um par
  9. Cartão alto

2, 3, 6, 7, 8São todos contagem simples. Usando uma lista de cartas Ás a Rei, basta colocar o número de cada valor na lista, aumentando para cada carta adicional encontrada. Em seguida, verifique a lista em busca de 4s, se não houver 4s, você não possui um 4 do tipo. Verifique se há 3s, se não houver 3s, você não possui um 3 do mesmo tipo. Se você tem um 3, verifique um 2 (indicando casa cheia). E assim por diante...

Para 1, 5você pode usar a mesma lista e procurar seqüências em que todos os cartões tenham uma ou mais entradas na lista para uma sequência de 5 cartões. Se eles também têm o mesmo naipe, é um straight flush.

4pode ter a mesma configuração de lista, mas desta vez você está contando o processo. Procure números de 5 ou mais.

Finalmente, 9você tem a carta mais alta, o que deve ser uma simples questão de olhar para o último valor mais alto de uma de suas listas acima.

Você pode sair assim que encontrar uma correspondência, se pesquisar em ordem. Embora seja trivial continuar a pesquisa e encontrar todas as correspondências, se você deseja fornecer todas essas informações ao usuário.


Essencialmente, você está enchendo baldes. Em seguida, verifique se há combinações nos baldes. Ilustrar:

insira a descrição da imagem aqui

Começando com uma matriz, com um balde para cada cartão, percorra os cartões e conte a instância de cada cartão. Em seguida, você pode facilmente iterar a matriz e verificar determinadas combinações. Neste exemplo, é claro que existe um tipo 4, porque um dos baldes possui 4 itens.

MichaelHouse
fonte
6

Eu me deparei com esse algoritmo uma vez. Multiplica números primos para determinar as mãos e é uma leitura muito interessante. Avaliador de mãos de pôquer de Cactus Kev

petervaz
fonte
1
Essa é interessante, mas não sei se você leu tudo. Esse algoritmo é para 5 cartões . Você pode ter encontrado em uma pesquisa no Google relacionada a 7 cartões, pois o autor afirma que eles têm um algoritmo para 7 cartões, mas não o compartilham. Veja o texto cinza na parte superior da página. Portanto, não tenho certeza de quão útil esse algoritmo é para um conjunto de 7 cartas.
MichaelHouse
1
Como existem apenas 21 mãos diferentes de 5 cartas que podem ser feitas a partir de uma mão de 7 cartas, uma opção é usar um avaliador de 5 cartas em cada uma delas e escolher a melhor.
Adam
@ Byte56 Você está certo, lembro-me de que o usei no cartão 7, mas tive que determinar o melhor cartão 5 de antemão, o que meio que minou a eficácia.
petervaz
O link desta resposta apodreceu e o domínio foi capturado por um remetente de spam. Redirecionei o link para um arquivo da página original, mas para tornar essa resposta mais robusta, seria ideal editar a resposta para incluir um resumo do algoritmo proposto no corpo da resposta.
DMGregory
2

Para complementar as excelentes respostas que essa pergunta já obteve, pensei que seria útil oferecer uma das maneiras mais diretas de comparar mãos uma vez que a técnica básica de classificação foi implementada. Antes de tudo, convém marcar as mãos com a classe , como sugeriram várias respostas - a maioria de suas comparações de 'mão X é melhor que mão Y?' Isso pode ser feito apenas comparando as classes das duas mãos e vendo qual classe é melhor. Quanto ao resto, você realmente precisará comparar cartão por cartão, e acontece que um pouco mais de trabalho na classificação tornará isso mais fácil.

Como o caso de linha de base, considere a situação em que ambas as mãos são 'high card'; nesse caso, você compararia primeiro as duas cartas mais altas e depois (se corresponderem) as próximas duas cartas, etc. esta:

int CompareHandsOfSameClass(Hand h1, Hand h2) {
  for ( int i = 0; i < 5; i++ ) {
    if ( h1[i].rank > h2[i].rank ) {
      return -1;
    } else if ( h1[i].rank < h2[i].rank ) {
      return 1;
    }
  }
  return 0;
}

Agora, a boa notícia: acontece que essa ordem lexicográfica , adequadamente ajustada, funciona para comparar duas mãos em qualquerdas classes, desde que a classe seja a mesma. Por exemplo, como a maneira de comparar pares é comparar os pares primeiro, depois as outras três cartas, você pode classificar sua mão para colocar o par primeiro (ou mesmo uma carta do par primeiro!) E executar essa mesma comparação. (Por exemplo, uma mão como A9772 seria armazenada como 77A92 ou, melhor ainda, 7A927; a mão A9972 seria armazenada como 9A729 e, comparando com o código acima, você começaria colocando 7 contra 9 e descobrisse que A9972 ganhou). Uma mão de dois pares seria armazenada com o mais alto dos dois pares primeiro, depois o mais baixo e depois o 'kicker' (assim, por exemplo, A9977 seria armazenado como 97A97); três desse tipo seriam armazenados com uma carta das três primeiro, depois os kickers e depois as outras cartas (por exemplo, A7772 seria 7A277); uma casa cheia seria armazenada com um de seus três e, em seguida, um de seus dois (por exemplo, 99777 seria armazenado como 79779); e straights e flushes podem ser armazenados na ordem 'lexicográfica direta', pois são comparados da mesma forma que as mãos com cartas altas. Isso leva a uma função comparadora externa direta que funciona para todas as classes de mãos com a função já fornecida:

// Compare two hands, returning -1/0/+1 as hand 1 is less than, equal to,
// or greater than hand 2. Note that this function assumes the hands have
// already been classified and sorted!
int CompareHands(Hand h1, Hand h2) {
  if ( h1.handClass > h2.handClass ) {
    return -1;
  } else if ( h1.handClass < h2.handClass ) {
    return 1;
  } else {
    return CompareHandsOfSameClass(h1, h2);
  }
}

Espero que isso ajude!

Steven Stadnicki
fonte
1

Existem algumas paralelizações possíveis usando representações de cartão adequadas e ajustes de bits. Por exemplo, este código Java avalia hards de 7 cartas retornando um número inteiro que pode ser usado para comparar duas mãos. Pode ser adaptado para relatar o tipo de mão de uma maneira mais amigável. As idéias centrais vêm da página de Cactus Kev mencionada em uma resposta anterior.

Se você está interessado apenas em possíveis implementações para nomear a mão em vários idiomas, em vez de eficiência e clareza de código, você também pode olhar para o desafio Nome da mão de pôquer no codegolf.SE.

Peter Taylor
fonte
1

Primeiro, você precisa conhecer o ranking e o naipe de todas as cartas; trivial, mas necessário.

Em seguida, gire essas 7 cartas e crie dois histogramas; um por classificação (usando uma matriz com 13 índices, todos inicializados em zero e incrementado em 1 se e quando uma carta na mão com essa classificação for encontrada) e uma por naipe (usando uma matriz de quatro elementos construídos de maneira semelhante à classificação) . Essas são operações lineares e você pode realizar as duas operações para cada cartão para apenas um conjunto de travessias.

Você pode determinar se alguma das seguintes mãos existe simplesmente examinando cada histograma em busca de intervalos correspondentes aos critérios e / ou um simples teste de acompanhamento:

  • Par: exatamente um intervalo de classificação tem um valor de exatamente 2, nenhum outro intervalo com um valor acima de 1 e nenhum fluxo?
  • Dois pares: dois ou mais buckets de classificação têm um valor de exatamente 2, sem nenhum bucket com mais de 2 e nenhum flush? (obviamente três pares não são uma mão, mas é uma possibilidade dada sete cartas; o dois pares mais fortes é a mão do jogador)
  • TOAK: Exatamente um balde tem um valor de exatamente 3, com nenhum outro balde com um valor maior que 1 e sem descarga?
  • Straight: cinco buckets de classificação consecutivos têm um valor de 1 ou mais sem flush? (Não se esqueça que os ases são altos e baixos; você pode usar um histograma de classificação de 14 elementos e contar os ases em dois intervalos, se desejar)
  • Flush: Algum balde de naipe tem 5 ou mais cartas? (se sim, procure na mão as cartas desse naipe e escolha as 5 principais)
  • Full House: qualquer um dos rankings tem um valor de 3 e qualquer outro bucket tem um valor de 2 (com 7 cartas, um flush é impossível com um full house, a menos que você esteja jogando com um baralho de Pinochle)
  • Four of a Kind: Alguém tem um valor de 4? (nenhuma outra combinação deve ser possível)
  • Straight Flush: Existe um straight e um flush indicados pelos histogramas? Em caso afirmativo, há pelo menos uma carta no naipe indicado com um valor correspondente a cada valor da sequência indicada? (este é provavelmente o mais caro em termos computacionais, mas deve ser simples contar quantas classificações consecutivas existem e você deve escanear a mão apenas uma vez por cinco classificações consecutivas, duas vezes por seis e três vezes por sete)

Naturalmente, você pode combinar várias dessas verificações:

  • Existe um flush?
  • Existe uma reta?
    • Se houver os dois, é um straight flush?
    • Se for um straight flush, ele tem um ás e um rei?
  • Existe um quatro-de-um-tipo?
  • Quantos tipos de três existem? (Dois, é uma casa cheia. Um, verifique pares)
  • Quantos pares existem? (Com dois ou mais, são dois pares. Com um, se houver três, é um full house, caso contrário, é um par)
  • Nenhuma das opções acima (cartão alto).

Basicamente, se as respostas a essas perguntas renderem alguma mão, defina o valor resultante da "força da mão" com a força da mão encontrada, se o valor ainda não estiver maior. Por exemplo, se você tem uma casa cheia, a força 7 de 9, também possui uma trinca, força 4, e um par, força 2.

Existem alguns atalhos e saídas rápidas, mas no geral não é realmente que caro apenas para executar todas as verificações.

KeithS
fonte
0

Você pode fazer mãos de pôquer de maneira relativamente fácil com uma abordagem iterativa simples.

Para cada carta, verifique se há uma ou duas ou três outras com a mesma face para verificar o par ou três / quatro do mesmo tipo.

Casas cheias são semelhantes. Ou se você encontrar um par e três do mesmo tipo que não sejam da mesma face, marque uma casa cheia como encontrada.

Para flushs, verifique cada naipe para ver se há cinco da mesma naipe.

Verificar diretamente é fácil, mesmo que não classificado. Para cada cartão, verifique se existe um mais alto e repita até que cinco cartões consecutivos sejam encontrados ou não.

Royal flushes e straight flushes podem ser encontrados da mesma forma que os straights. Royal flushes têm algumas condições extras, pois cartões de menor valor podem ser ignorados.

Sim, essa abordagem é ineficiente, mas para a maioria dos jogos de poker isso é irrelevante. Você está checando um punhado de jogadores a cada meio minuto, não checando milhares de mãos por segundo.

Existem métodos melhores, mas podem levar mais tempo para codificar, e você provavelmente tem coisas mais importantes para gastar tempo / dinheiro, o que contribuirá para um jogo melhor da perspectiva dos jogadores, além da esperteza e eficiência de um algoritmo.

Sean Middleditch
fonte
0

Uma maneira simples de encontrar pares, pares duplos, toaks, casas completas, jogadores de pôquer etc. é a seguinte:

compare cada cartão um com o outro em um loop aninhado como este:

int matches=0;
for (int i=0;i<5;i++)
   for (int j=0;j<5;j++)
      if (i!=j && cardvalue(card[j])==cardvalue(card[i])) matches++;

As partidas terão o seguinte:
2 para um par
4 para dois pares
6 para toak
8 para uma casa cheia
12 para um pôquer

para otimizar rapidamente isso: não é necessário executar o loop j até 5, ele pode ser executado no i-1. a comparação "i! = j" pode ser removida e os valores da correspondência são divididos pela metade (1 = par, 2 = 2 pares etc.)

Thomas Schüler
fonte