Определение покерных рук

19

Я делал игру в Техасский Холдем как часть оценки, и я размышлял над тем, как проверить 7 доступных карт и определить, существуют ли руки.

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

Каждая из карточек - это струны, состоящие из числа / a / j / q / k и масти (char)3(которая делает небольшой символ пики).

Есть ли у кого-нибудь какие-либо предложения, формулы или ссылки, которые я мог бы использовать для создания системы анализа рук?

Пока не беспокойтесь о том, чтобы расставить руки друг против друга, это другой котелок рыбы.

Magicaxis
источник
1
Просто указываю на это, но векторный тег в его контексте относится к линейной алгебре. Не контейнер.
Сидар
@Sidar Не стесняйтесь редактировать теги в будущем, если вы считаете, что они не подходят. Если вы наводите курсор на теги, и появляется опция «Редактировать теги» (по крайней мере, для меня?), И вы можете редактировать только теги, не редактируя вопрос.
MichaelHouse
@ Byte56 У меня странная привычка думать, что я не уверен, прав ли я, поэтому я делаю это пассивно через комментарий ... Вот почему я не редактировал сообщение. Возможно, я что-то пропустил в посте, поэтому я ждал его ответа.
Сидар
Есть также хорошая статья, которая появилась в Game Developer несколько лет назад: cowboyprogramming.com/2007/01/04/programming-poker-ai
celion
1
Некоторое время назад я сделал реализацию в Java для забавы, вы можете найти ее здесь: codereview.stackexchange.com/questions/10973/… . Если вы заглянете в PokerHand.java, вы найдете методы для тестирования каждого типа рук (например, isFullHouse). Они работают, только если карты отсортированы первыми.
Bughi

Ответы:

20

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

Другими словами, создайте карты карт массива (числа и A / J / Q / K) для подсчета карт этого ранга в вашей руке. Если у игрока есть пара или тройка в своем роде, в этом массиве будет элемент, равный 2 или 3, и т. Д. У него есть фулл-хаус, если есть один элемент, равный 2, и другой, равный 3, и прямой если в этом массиве пять последовательных элементов, равных 1

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

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

В псевдокоде:

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...
Натан Рид
источник
17

Это немного сложно, потому что есть так много комбинаций. К счастью, у вас есть процессор, который может проверить множество комбинаций за очень короткое время.

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

  1. Стрит флеш
  2. Четверка
  3. Аншлаг
  4. Румянец
  5. Прямо
  6. Три в своем роде
  7. Две пары
  8. Одна пара
  9. Старшая карта

2, 3, 6, 7,8 Все простой подсчет. Используя список карт от туза до короля, просто поместите номер каждого значения в списке, увеличивая для каждой дополнительной найденной карты. Затем проверьте список на 4, если нет 4, у вас нет 4 в своем роде. Проверьте это на 3 с, если нет 3 с, у вас нет 3 в своем роде. Если у вас 3, проверьте на 2 (указав фулл хаус). И так далее...

Для 1, 5вы можете использовать тот же список и искать последовательности, где все карты имеют одну или несколько записей в списке для последовательности из 5 карт. Если у них тоже одинаковый костюм, это стрит-флеш.

4может иметь ту же настройку списка, но на этот раз вы рассчитываете костюм. Ищите числа 5 или выше.

Наконец, у 9вас есть самая высокая карта, и вам просто нужно посмотреть на последнее самое высокое значение из одного из ваших списков выше.

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


По сути, вы наполняете ведра. Затем проверяем ведра на комбинации. Проиллюстрировать:

введите описание изображения здесь

Начиная с массива, с ведром для каждой карты, перебирайте карты и подсчитывайте количество экземпляров каждой карты. Затем вы можете легко перебрать массив и проверить наличие определенных комбинаций. В этом примере ясно, что существует 4 вида, потому что в одном из блоков есть 4 элемента.

MichaelHouse
источник
6

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

petervaz
источник
1
Это интересно, но я не уверен, что вы все прочитали. Этот алгоритм для 5 карт . Вы, возможно, обнаружили в поиске Google, связанном с 7 картами, поскольку автор заявляет, что у них есть алгоритм для 7 карт, но не делитесь им. Смотрите серый текст в верхней части страницы. Поэтому я не уверен, насколько полезен этот алгоритм для набора из 7 карт.
MichaelHouse
1
Поскольку из 7 карточных комбинаций можно сделать только 21 разную комбинацию из 5 карт, одним из вариантов является использование 5-карточного оценщика на каждой и выбор наилучшего.
Адам
@ Byte56 Вы правы, я помню, я использовал его для 7 карт, но мне пришлось заранее определить лучшие 5 карт, что подорвало эффективность.
petervaz
Ссылка на этот ответ с тех пор сгнила, и домен поднял спамер. Я перенаправил ссылку на архив исходной страницы, но чтобы сделать этот ответ более надежным, было бы идеально отредактировать ответ, включив в него краткое изложение предлагаемого алгоритма в теле самого ответа.
DMGregory
2

В дополнение к отличным ответам на этот вопрос уже получен, я подумал, что было бы полезно предложить один из самых простых способов сравнения рук после того, как базовый метод классификации на месте. Прежде всего, вы захотите пометить руки своим классом , как подсказывают многочисленные ответы - большинство ваших сравнений «рука X лучше руки Y?» тогда можно сделать это, просто сравнив классы двух рук и увидев, какой класс лучше. В остальном вам действительно нужно сравнивать по карточкам, и оказывается, что немного больше работы в классификации упростит эту задачу.

В качестве базового случая рассмотрим ситуацию, когда обе руки являются руками «старшей карты»; в этом случае вы сначала сравниваете две старшие карты, затем (если они совпадают) следующие две карты и т. д. Если вы предполагаете, что каждая входная рука отсортирована от самой верхней до самой низкой карты, такой подход приводит к коду, который выглядит следующим образом: это:

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

Теперь, хорошая новость: оказывается, что этот лексикографический порядок , соответствующим образом подправленный, работает для сравнения двух рук в любомклассов, пока их класс одинаков. Например, поскольку способ сравнения пар состоит в том, чтобы сначала сравнить пары, а затем три другие карты, вы можете отсортировать свою руку, чтобы поставить пару первой (или даже одну карту пары первой!) И выполнить это же сравнение. (Так, например, такая рука, как A9772, будет сохранена как 77A92 или, что еще лучше, 7A927; рука A9972 будет сохранена как 9A729, и, сравнивая с приведенным выше кодом, вы начнете с расстановки 7 против 9 и обнаружите, что A9972 выиграл). Рука из двух пар будет храниться с наивысшей из этих двух пар, затем с более низкой, затем с кикером (например, A9977 будет хранить как 97A97); три вида будут храниться сначала с одной картой из трех, затем с кикерами, затем с другими картами (например, A7772 будет 7A277); полный дом будет храниться с одним из трех, а затем с одним из двух (например, 99777 будет храниться как 79779); и стриты и флеши могут храниться в «прямом лексикографическом» порядке, поскольку они сравниваются точно так же, как руки с высокими картами. Это приводит к простой внешней функции сравнения, которая работает для всех классов рук с уже заданной функцией:

// 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);
  }
}

Надеюсь, это поможет!

Стивен Стадницки
источник
1

Возможны некоторые распараллеливания с использованием подходящих представлений карт и переворота битов. Например, этот Java-код оценивает 7-карточные харды, возвращая целое число, которое можно использовать для сравнения двух рук. Он может быть адаптирован для сообщения о типе руки более удобным для пользователя способом. Основные идеи взяты из страницы Кактуса Кева, на которую есть ссылка в предыдущем ответе.

Если вас интересуют только возможные реализации для именования рук на разных языках, а не эффективность и ясность кода, вы также можете взглянуть на вызов Name в покере на codegolf.SE.

Питер Тейлор
источник
1

Во-первых, вам нужно знать ранг и масть всех карт; тривиально, но необходимо.

Затем прокрутите эти 7 карт и создайте две гистограммы; один по рангу (используя массив с 13 индексами, все инициализируются в ноль и увеличиваются на 1, если и когда найдена карта в руке с таким рангом) и один по масти (используя массив из четырех элементов, построенных так же, как и для ранга) , Это линейные операции, и вы можете выполнять обе операции для каждой карты только для одного набора обходов.

Затем вы можете определить, существует ли какая-либо из следующих раздач, просто проверив каждую гистограмму на наличие сегментов, соответствующих критериям, и / или простого дополнительного теста:

  • Пара: Имеет ли ровно один сегмент ранга значение ровно 2, при этом нет другого сегмента со значением выше 1 и нет сброса?
  • Две пары: имеют ли два или более ранговых сегмента значение ровно 2, при этом ни один сегмент не имеет более 2 и нет флеша? (очевидно, что три пары - это не рука, но есть возможность получить семь карт; самая сильная пара - рука игрока)
  • TOAK: Имеет ли ровно 3 сегмента значение только для одного сегмента, при этом нет другого сегмента, значения которого превышают 1, и нет флеша?
  • Straight: пять последовательных ранговых групп имеют значение 1 или более без флеша? (Не забывайте, что тузы бывают как высокие, так и низкие; вы можете использовать гистограмму ранга из 14 элементов и считать тузы в двух ведрах, если хотите)
  • Flush: Есть ли какой - нибудь костюм иметь ведро 5 или больше карт? (если это так, отсканируйте руку на наличие карт этой масти и выберите топ 5)
  • Фулл-хаус: имеет ли одно ведро одного ранга значение 3, а любое другое ведро имеет значение 2 (при 7 картах флеш с фулл-хаусом невозможен, если вы не играете с колодой Pinochle)
  • Четверка рода: имеет ли какой-либо ранг значение 4? (никакая другая комбинация не должна быть возможной)
  • Стрит-флеш: есть ли на гистограммах стрит и флеш? Если да, есть ли хотя бы одна карта в указанном флеш-масти, ранг которой соответствует каждому рангу указанной прямой? (это, вероятно, самый дорогой в вычислительном отношении, но подсчитать, сколько существует последовательных рангов, должно быть просто, и вам нужно только отсканировать руку один раз для 5 последовательных рангов, дважды для 6 и три раза для 7)

Вы можете, естественно, объединить несколько из этих проверок:

  • Есть ли флеш?
  • Есть ли прямой?
    • Если есть и то и другое, это стрит-флеш?
    • Если это стрит-флеш, есть ли у него туз и король?
  • Есть четыре в своем роде?
  • Сколько есть три в своем роде? (Два, это фулл-хаус. Один, проверьте пары)
  • Сколько там пар? (С двумя или более, это две пары. С одним, если есть также три, это фулл-хаус, в противном случае это пара)
  • Ничего из вышеперечисленного (старшая карта).

По сути, если ответы на них дают какую-либо руку, установите полученное значение «сила руки» равным силе найденной руки, если значение еще не выше. Например, если у вас есть фулл-хаус, сила 7 из 9, то у вас также есть три типа, сила 4 и пара, сила 2.

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

Keiths
источник
0

Вы можете относительно легко делать покерные комбинации с простым итеративным подходом.

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

Полные дома похожи. Или, если вы найдете пару и тройку не одинакового лица, пометьте аншлаг как найденный.

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

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

Флеш-рояль и стрит-флеш можно найти так же, как стриты. У флеш-роялей есть некоторые дополнительные условия, в которых карты с более низкой стоимостью можно игнорировать.

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

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

Шон Миддледич
источник
0

простой способ найти пары, двойные пары, жетоны, фулл-хаусы, покер и т. д. заключается в следующем:

сравните каждую карту друг с другом во вложенном цикле, как этот:

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

Матчи пройдут следующим образом:
2 для пары
4 для двух пар
6 для тоака
8 для фуллхауса
12 для покера

чтобы оптимизировать скорость: нет необходимости запускать j-цикл до 5, его можно запустить до i-1. сравнение "i! = j" может быть удалено, тогда значения совпадения делятся пополам (1 = пара, 2 = 2 пары и т. д.)

Томас Шулер
источник