Алгоритм присвоения 5-карточной покерной руки

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

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

//HAND TYPES:
ROYAL_FLUSH = 900000
STRAIGHT_FLUSH = 800000
...
TWO_PAIR = 200000
ONE_PAR = 100000

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

Так, например, следующая формула может быть использована для оценки руки:

HAND_TYPE + (each card value in the hand)^(the number of occurences of that value)

Таким образом, для фулл-хауса из трех Куинс и двух 7-х, счет будет:

600000 + 12^3 + 7^2

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

hand1 = 4C, 6C, 6H, JS, KC
hand2 = 3H, 4H, 7C, 7D, 8H

Эти две руки имеют одну пару, поэтому их соответствующие оценки:

100000 + 4^1 + 6^2 + 11^1 + 13^1 = 100064
100000 + 3^1 + 4^1 + 7^2 + 8^1 = 100064

Это приводит к ничьей, когда ясно, что пара 7s превосходит пару 6s.

Как я могу улучшить эту формулу, или даже, какую формулу лучше использовать?

Кстати, в моем коде руки хранятся в массиве значений каждой карты в порядке возрастания, например:

[2H, 6D, 10C, KS, AS]

РЕДАКТИРОВАТЬ:

Вот мое окончательное решение благодаря ответам ниже:

    /**
     * Sorts cards by putting the "most important" cards first, and the rest in decreasing order.
     * e.g. High Hand:  KS, 9S, 8C, 4D, 2H
     *      One Pair:   3S, 3D, AH, 7S, 2C
     *      Full House: 6D, 6C, 6S, JC, JH
     *      Flush:      KH, 9H, 7H, 6H, 3H
     */
    private void sort() {
        Arrays.sort(hand, Collections.reverseOrder());      // Initially sorts cards in descending order of game value
        if (isFourOfAKind()) {                              // Then adjusts for hands where the "most important" cards
            sortFourOfAKind();                              // must come first
        } else if (isFullHouse()) {
            sortFullHouse();
        } else if (isThreeOfAKind()) {
            sortThreeOfAKind();
        } else if (isTwoPair()) {
            sortTwoPair();
        } else if (isOnePair()){
            sortOnePair();
        }
    }

    private void sortFourOfAKind() {
        if (hand[0].getGameValue() != hand[HAND_SIZE - 4].getGameValue()) {     // If the four of a kind are the last four cards
            swapCardsByIndex(0, HAND_SIZE - 1);                                 // swap the first and last cards
        }                                                                       // e.g. AS, 9D, 9H, 9S, 9C => 9C, 9D, 9H, 9S, AS
    }

    private void sortFullHouse() {
        if (hand[0].getGameValue() != hand[HAND_SIZE - 3].getGameValue()) {     // If the 3 of a kind cards are the last three
            swapCardsByIndex(0, HAND_SIZE - 2);                                 // swap cards 1 and 4, 2 and 5
            swapCardsByIndex(HAND_SIZE - 4, HAND_SIZE - 1);                     // e.g. 10D, 10C, 6H, 6S, 6D => 6S, 6D, 6H, 10D, 10C
        }
    }

    private void sortThreeOfAKind() {                                                                                                                               // If the 3 of a kind cards are the middle 3 cards
        if (hand[0].getGameValue() != hand[HAND_SIZE - 3].getGameValue() && hand[HAND_SIZE - 1].getGameValue() != hand[HAND_SIZE - 3].getGameValue()) {             // swap cards 1 and 4
            swapCardsByIndex(0, HAND_SIZE - 2);                                                                                                                     // e.g. AH, 8D, 8S, 8C, 7D => 8C, 8D, 8S, AH, 7D
        } else if (hand[0].getGameValue() != hand[HAND_SIZE - 3].getGameValue() && hand[HAND_SIZE - 4].getGameValue() != hand[HAND_SIZE - 3].getGameValue()) {
            Arrays.sort(hand);                                                                                                                                      // If the 3 of a kind cards are the last 3,
            swapCardsByIndex(HAND_SIZE - 1, HAND_SIZE - 2);                                                                                                         // reverse the order (smallest game value to largest)
        }                                                                                                                                                           // then swap the last two cards (maintain the large to small ordering)
    }                                                                                                                                                               // e.g. KS, 9D, 3C, 3S, 3H => 3H, 3S, 3C, 9D, KS => 3H, 3S, 3C, KS, 9D

    private void sortTwoPair() {                                                                                                                                    
        if (hand[0].getGameValue() != hand[HAND_SIZE - 4].getGameValue()) {                                                                                         // If the two pairs are the last 4 cards
            for (int i = 0; i < HAND_SIZE - 1; i++) {                                                                                                               // "bubble" the first card to the end
                swapCardsByIndex(i, i + 1);                                                                                                                         // e.g. AH, 7D, 7S, 6H, 6C => 7D, 7S, 6H, 6C, AH
            }
        } else if (hand[0].getGameValue() == hand[HAND_SIZE - 4].getGameValue() && hand[HAND_SIZE - 2].getGameValue() == hand[HAND_SIZE - 1].getGameValue()) {      // If the two pairs are the first and last two cards
            swapCardsByIndex(HAND_SIZE - 3, HAND_SIZE - 1);                                                                                                         // swap the middle and last card
        }                                                                                                                                                           // e.g. JS, JC, 8D, 4H, 4S => JS, JC, 4S, 4H, 8D
    }

    private void sortOnePair() {                                                                    // If the pair are cards 2 and 3, swap cards 1 and 3
        if (hand[HAND_SIZE - 4].getGameValue() == hand[HAND_SIZE - 3].getGameValue()) {             // e.g QD, 8H, 8C, 6S, 4J => 8C, 8H, QD, 6S, 4J
            swapCardsByIndex(0, HAND_SIZE - 3);
        } else if (hand[HAND_SIZE - 3].getGameValue() == hand[HAND_SIZE - 2].getGameValue()) {      // If the pair are cards 3 and 4, swap 1 and 3, 2 and 4 
            swapCardsByIndex(0, HAND_SIZE - 3);                                                     // e.g. 10S, 8D, 4C, 4H, 2H => 4C, 4H, 10S, 8D, 2H
            swapCardsByIndex(HAND_SIZE - 4, HAND_SIZE - 2);
        } else if (hand[HAND_SIZE - 2].getGameValue() == hand[HAND_SIZE - 1].getGameValue()) {      // If the pair are the last 2 cards, reverse the order
            Arrays.sort(hand);                                                                      // and then swap cards 3 and 5
            swapCardsByIndex(HAND_SIZE - 3, HAND_SIZE - 1);                                         // e.g. 9H, 7D, 6C, 3D, 3S => 3S, 3D, 6C, 7D, 9H => 3S, 3D, 9H, 7D, 6C 
        }
    }

    /**
     * Swaps the two cards of the hand at the indexes taken as parameters
     * @param index1
     * @param index2
     */
    private void swapCardsByIndex(int index1, int index2) {
        PlayingCard temp = hand[index1];
        hand[index1] = hand[index2];
        hand[index2] = temp;
    }

    /**
     * Gives a unique value of any hand, based firstly on the type of hand, and then on the cards it contains
     * @return The Game Value of this hand
     * 
     * Firstly, a 24 bit binary string is created where the most significant 4 bits represent the value of the type of hand
     * (defined as constants private to this class), the last 20 bits represent the values of the 5 cards in the hand, where
     * the "most important" cards are at greater significant places. Finally, the binary string is converter to an integer.
     */
    public int getGameValue() {
        String handValue = addPaddingToBinaryString(Integer.toBinaryString(getHandValue()));

        for (int i = 0; i < HAND_SIZE; i++) {
            handValue += addPaddingToBinaryString(Integer.toBinaryString(getCardValue(hand[i])));
        }

        return Integer.parseInt(handValue, 2);
    }

    /**
     * @param binary
     * @return the same binary string padded to 4 bits long
     */
    private String addPaddingToBinaryString(String binary) {
        switch (binary.length()) {
        case 1: return "000" + binary;
        case 2: return "00" + binary;
        case 3: return "0" + binary;
        default: return binary;
        }
    }

    /**
     * @return Default value for the type of hand
     */
    private int getHandValue() {
        if (isRoyalFlush())     { return ROYAL_FLUSH_VALUE; }       
        if (isStraightFlush())  { return STRAIGHT_FLUSH_VALUE; }
        if (isFourOfAKind())    { return FOUR_OF_A_KIND_VALUE; }
        if (isFullHouse())      { return FULL_HOUSE_VALUE; }
        if (isFlush())          { return FLUSH_VALUE; }     
        if (isStraight())       { return STRAIGHT_VALUE; }      
        if (isThreeOfAKind())   { return THREE_OF_A_KIND_VALUE; }
        if (isTwoPair())        { return TWO_PAIR_VALUE; }
        if (isOnePair())        { return ONE_PAIR_VALUE; }
        return 0;
    }

    /**
     * @param card
     * @return the value for a given card type, used to calculate the Hand's Game Value
     * 2H = 0, 3D = 1, 4S = 2, ... , KC = 11, AH = 12
     */
    private int getCardValue(PlayingCard card) {
        return card.getGameValue() - 2;
    }

3 ответа

Решение

Есть 10 признанных покерных комбинаций:

9 - Royal flush
8 - Straight flush (special case of royal flush, really)
7 - Four of a kind
6 - Full house
5 - Flush
4 - Straight
3 - Three of a kind
2 - Two pair
1 - Pair
0 - High card

Если вы не учитываете масти, существует только 13 возможных значений карт. Значения карты:

2 - 0
3 - 1
4 - 2
5 - 3
6 - 4
7 - 5
8 - 6
9 - 7
10 - 8
J - 9
Q - 10
K - 11
A - 12

Требуется 4 бита для кодирования руки и 4 бита для кодирования карт. Вы можете кодировать всю руку в 24 битах.

Флеш-рояль будет 1001 1100 1011 1010 1001 1000 (0x9CBA98)

Стрит с высотой 7 будет 0100 0101 0100 0011 0010 0001 (0x454321)

Две пары, 10 и 5 (и туз) будут 0010 1000 1000 0011 0011 1100 (0x28833C)

Я предполагаю, что у вас есть логика, которая выяснит, какая у вас рука. В этом вы, вероятно, написали код, чтобы расположить карты в порядке слева направо. Таким образом, флеш-рояль будет организован как [A,K,Q,J,10]. Затем вы можете построить число, представляющее руку, используя следующую логику:

int handValue = HandType; (i.e. 0 for high card, 7 for Four of a kind, etc.)
for each card
    handValue = (handValue << 4) + cardValue  (i.e. 0 for 2, 9 for Jack, etc.)

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

Нормализуя руку

Вышеприведенный алгоритм зависит от нормализации покерной руки, в первую очередь с наиболее важными картами. Так, например, рука [K,A,10,J,Q] (все в одном костюме) - флеш-рояль. Это нормализовано [A,K,Q,J,10], Если вам дали руку [10,Q,K,A,J], это также будет нормализовано к [A,K,Q,J,10], Рука [7,4,3,2,4] это пара из 4-х. Будет нормализовано до [4,4,7,3,2],

Без нормализации очень трудно создать уникальное целочисленное значение для каждой руки и гарантировать, что пара из 4 всегда будет бить пару из 3.

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

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

Наибольшее значение для карты будет 14, при условии, что вы позволяете не-лицевым картам сохранять свое значение (2..10), тогда J=11, QK, A=14.

Целью подсчета очков было бы различие между руками в сценарии разрыва связи. То есть "пара" против "пара". Если вы обнаружите другую конфигурацию руки ("две пары"), это объединит результаты в отдельные группы.

Вы должны внимательно ознакомиться с вашими требованиями. Я подозреваю, что по крайней мере для некоторых рук участвующие карты важнее, чем не участвующие карты. Например, пара 4 с 7-хай бьет пару 3 с хай-хай? (4,4,7,3,2 > 3,3,Q,6,5?) Ответ на этот вопрос должен определить порядок карт в руке.

Если у вас есть 5 карт и значения < 16, преобразуйте каждую карту в шестнадцатеричное число: 2..10,JQKA => 2..ABCDE. Положите карты в порядке, как указано выше. Например, 4,4,7,3,2, вероятно, станет 4,4,7,3,2. Сопоставьте эти значения с шестнадцатеричным, а затем с целочисленным значением: "0x44732" -> 0x44732.

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

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

100000 + 4^1 + 6^2 + 11^1 + 13^1 = 100064
100000 + 3^1 + 4^1 + 7^2 + 8^1 = 100064

Тем не менее, дополнение не совсем правильный инструмент здесь. Вы уже используете ^ что означает, что ты на полпути. Вместо этого используйте умножение, и вы можете избежать двусмысленности. Рассматривать:

100000 + (4^1 * 6^2 * 11^1 * 13^1)
100000 + (3^1 * 4^1 * 7^2 * 8^1)

Это почти правильно, но все еще есть неясности (например, 2^4 = 4^2). Итак, переназначьте новые (простые!) Значения для каждой карты:

Ace => 2
3 => 3
4 => 5
5 => 7
6 => 11
...

Затем вы можете умножить специальные простые значения каждой карты, чтобы получить уникальное значение для каждой возможной руки. Добавьте свое значение для типа руки (пара, фулл-хаус, флеш и т. Д.) И используйте его. Возможно, вам придется увеличить величину значений типа вашей руки, чтобы они не мешали составлять значения карт.

Другие вопросы по тегам