Алгоритмы тестирования покерной руки на стрит-дро (от 4 до стрита)? - PullRequest
9 голосов
/ 28 октября 2010

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

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

Мой текущий план соответствует направлениям:

  • вычтите самый низкий ранг из ранга всех карт в наборе.
  • посмотрите, является ли определенная последовательность, т.е. 0,1,2,3 или 1,2,3,4 (для OESD), подмножеством модифицированной коллекции.

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

Будем благодарны за любые предложения и / или лучшие идеи.

Ответы [ 4 ]

7 голосов
/ 01 ноября 2010

Самый быстрый подход, вероятно, заключается в назначении битовой маски для каждого ранга карты (например, двойка = 1, три = 2, четыре = 4, пять = 8, шесть = 16, семь = 32, восемь = 64, девять = 128,десять = 256, джек = 512, ферзь = 1024, король = 2048, туз = 4096) и ИЛИ вместе значения масок всех карт в руке.Затем используйте справочную таблицу из 8192 элементов, чтобы указать, является ли рука стритом, открытым эндером, броском в живот или ничем не значимым (можно также включить различные типы бэкдора для стрит-дро, не влияя на время выполнения).

Между прочим, используя различные значения битовой маски, можно быстро обнаружить другие полезные руки, такие как «два в своем роде», «три в своем роде» и т. Д. Если в распоряжении имеется 64-битная целочисленная математика, используйтекуб указанных битовых масок выше (т. е. двойка = 1, три = 8 и т. д. до туза = 2 ^ 36) и сложите значения карт.Если результат с 04444444444444 (восьмеричное) не равен нулю, рука является четверкой в ​​своем роде.В противном случае, если при добавлении плюс 01111111111111 и '0 с 04444444444444 получится ненулевое значение, рука будет тройкой или фулл-хаусом.В противном случае, если результат с 02222222222222, отличным от нуля, является комбинацией пары или пары.Чтобы увидеть, содержит ли рука две или более пары, 'и' значение руки с 02222222222222, и сохраните это значение.Вычтите 1, а 'и' результат с сохраненным значением.Если не ноль, рука содержит как минимум две пары (поэтому, если она содержит тройку в своем роде, это фулл-хаус; в противном случае это две пары).

В качестве прощальной нотыВычисление, выполненное для проверки на стрит, также позволит вам быстро определить, сколько разных рядов карт в руке.Если есть N карт и N разных рангов, рука не может содержать ни одной пары или лучше (но, конечно, может содержать стрит или флеш).Если существует N-1 разных рангов, рука содержит ровно одну пару.Только если есть меньше разных рангов, один должен использовать более сложную логику (если есть N-2, рука может быть двумя парами или тройкой в ​​своем роде; если N-3 или меньше, рука может быть "«три пары» (две пары), фулл-хаус или четыре в своем роде).

Еще одна вещь: если вы не можете управлять справочной таблицей из 8192 элементов, вы можетеиспользуйте справочную таблицу из 512 элементов.Вычислите битовую маску, как указано выше, а затем выполните поиск по массиву [bitmask & 511] и массиву [bitmask >> 4], а также по ИЛИ результатам.Любой законный стрит или ничья будет зарегистрирован на тот или иной поиск.Обратите внимание, что это не даст вам напрямую количество разных рангов (поскольку карты с шести по десять будут учитываться в обоих поисках), но еще один поиск в том же массиве (используя массив [bitmask >> 9]) будет считать только гнезда.сквозь тузы.

7 голосов
/ 29 октября 2010

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

Идея состоит в том, что мы создаем своего рода хеш-функцию, которая имеет следующие свойства:
1) вычисляет уникальное значение для каждого различного набора рангов карт
2) является симметричной в том смысле, что она независит от порядка карточек
Цель этого состоит в том, чтобы уменьшить количество элементов, необходимых в справочной таблице.

Удобный способ сделать это - назначить простое число каждому рангу (2-> 2, 3-> 3, 4-> 5, 5-> 7, 6-> 11, 7-> 13, 8-> 17, 9-> 19, T-> 23, J-> 29, Q-> 31, K-> 37, A-> 41), а затем вычислите произведение простых чисел.Например, если карты 39TJQQ, то хеш 36536259.

Чтобы создать таблицу поиска, вы проходите все возможные комбинации рангов и используете некоторый простой алгоритм, чтобы определить, образуют ли они стрит-дро.Для каждой комбинации вы также вычисляете значение хеша, а затем сохраняете результаты на карте, где Key - это хеш, а Value - результат проверки на прямую ничью.Если максимальное количество карточек невелико (4 или меньше), то возможен даже линейный массив.

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

Вот пример на C ++.Я не гарантирую, что он работает правильно и, вероятно, его можно было бы оптимизировать, используя отсортированный массив и бинарный поиск вместо hash_map.hash_map довольно медленный для этой цели.

#include <iostream>
#include <vector>
#include <hash_map>
#include <numeric>
using namespace std;

const int MAXCARDS = 9;
stdext::hash_map<long long, bool> lookup;

//"Hash function" that is unique for a each set of card ranks, and also
//symmetric so that the order of cards doesn't matter.
long long hash(const vector<int>& cards)
{
    static const int primes[52] = {
        2,3,5,7,11,13,17,19,23,29,31,37,41,
        2,3,5,7,11,13,17,19,23,29,31,37,41,
        2,3,5,7,11,13,17,19,23,29,31,37,41,
        2,3,5,7,11,13,17,19,23,29,31,37,41
    };

    long long res=1;
    for(vector<int>::const_iterator i=cards.begin();i!=cards.end();i++)
        res *= primes[*i];
    return res;
}

//Tests whether there is a straight draw (assuming there is no
//straight). Only used for filling the lookup table.
bool is_draw_slow(const vector<int>& cards)
{
    int ranks[14];
    memset(ranks,0,14*sizeof(int));

    for(vector<int>::const_iterator i=cards.begin();i!=cards.end();i++)
        ranks[ *i % 13 + 1 ] = 1;
    ranks[0]=ranks[13]; //ace counts also as 1

    int count = ranks[0]+ranks[1]+ranks[2]+ranks[3];
    for(int i=0; i<=9; i++) {
        count += ranks[i+4];
        if(count==4)
            return true;
        count -= ranks[i];
    }

    return false;
};

void create_lookup_helper(vector<int>& cards, int idx)
{
    for(;cards[idx]<13;cards[idx]++) {
        if(idx==cards.size()-1)
            lookup[hash(cards)] = is_draw_slow(cards);
        else {
            cards[idx+1] = cards[idx];
            create_lookup_helper(cards,idx+1);
        }
    }
}

void create_lookup()
{
    for(int i=1;i<=MAXCARDS;i++) {
        vector<int> cards(i);
        create_lookup_helper(cards,0);
    }
}

//Test for a draw using the lookup table
bool is_draw(const vector<int>& cards)
{
    return lookup[hash(cards)];
};

int main(int argc, char* argv[])
{
    create_lookup();

    cout<<lookup.size()<<endl; //497419

    int cards1[] = {1,2,3,4};
    int cards2[] = {0,1,2,7,12};
    int cards3[] = {3,16,29,42,4,17,30,43};

    cout << is_draw(vector<int>(cards1,cards1+4)) <<endl; //true
    cout << is_draw(vector<int>(cards2,cards2+5)) <<endl; //true
    cout << is_draw(vector<int>(cards3,cards3+8)) <<endl; //false

}
3 голосов
/ 28 октября 2010

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

Если снова предположить, что карточки представлены числами 1 - 13, то, если ваши 4 карточки имеют числовой диапазон 3 или 4 (от наивысшего до низшего ранга) и не содержат дубликатов, то у вас есть возможностьстрит-дро.

Диапазон 3 означает, что у вас открытая ничья, например, 2,3,4,5 имеет диапазон 3 и не содержит дубликатов.

Диапазон 4 означает, что у вас есть гатшот (как вы его назвали), например, 5,6,8,9 имеет диапазон 4 и не содержит дубликатов.

0 голосов
/ 28 октября 2010

Обновление: согласно комментарию Кристиана Манна ... это может быть так:

скажем, A представляется как 1.J как 11, Q как 12 и т. Д.

loop through 1 to 13 as i
  if my cards already has this card i, then don't worry about this case, skip to next card
  for this card i, look to the left for number of consecutive cards there is
  same as above, but look to the right
  if count_left_consecutive + count_right_consecutive == 4, then found case

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

...