Создание собственного инструмента моделирования

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

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

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

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

Представляем Numba для ускорения циклов Python для циклов

Numba позволяет нам компилировать наш код, просто добавляя декоратор. Он работает не со всем кодом Python, но очень полезен, если вы выполняете циклы for. Я написал статью о том, как сделать Python сверхбыстрым (на самом деле быстрее, чем новый код Julia), и Numba была самой производительной библиотекой.

Я также объединил Numba с методом lru_cache, который позволяет кэшировать код Python, это оптимизировало мой код и позволило ему работать довольно быстро.

В этом случае мы собираемся многократно перебрать все возможные руки в покере: это 2 598 960 рук. Причина в том, что мы хотим проверить, содержат ли эти руки определенные карты. Так что здесь много итераций, и классический Python будет мучительно медленным. Кроме того, Numpy Vectorizing кажется трудным, потому что у нас есть двойные циклы for. Так что я считаю это хорошим примером, чтобы познакомить вас с библиотекой Numba.

Хорошо, прояснив это, теперь мы можем перейти к построению нашей симуляции.

Проверка комбинаций на флопе

Перво-наперво: давайте смоделируем ситуацию, когда выпадет флоп.

Давайте заколлируем наши 2 карты на префлопе + 3 карты на флопе «present-cards». Эти 5 карт уже можно использовать для составления руки. Но на терне и ривере появятся еще 2 карты, поэтому в нашей последней руке может быть только 4 или 3 карты из наших present-cards. В последней руке на ривере должно быть минимум 3 карты из наших текущих карт, но не меньше.

Следовательно, мы можем построить все возможные комбинации с 3 и 4 или нашими картами присутствия, которые я называю c3 и c4 в моем скрипте. Затем мы вычисляем средний балл для всех комбинаций из 5 карт, содержащих любую из этих комбинаций. Это и будет нашим ожидаемым значением для следующих «карт будущего».

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

Проверка комбинаций на повороте

Теперь приходит очередь, и у нас есть 6 «подарочных карт». С этими картами можно составить 6 разных рук, одна из которых будет нашей лучшей рукой на данный момент. Но опять же впереди еще одна карта, поэтому в последней руке на ривере может быть как минимум 4 карты из наших «подарочных карт».

В ход можно составить 15 различных комбинаций из 4 карт. Мы проверим счет каждой руки из 5 карт, содержащей их, и вычислим среднее значение. Это будет наша ожидаемая стоимость в Терне.

Проверка стоимости в реке

Последний шаг прост. У нас есть 7 карт, из которых получается 21 разная комбинация из 5 карт. Здесь нет карт будущего, мы просто выбираем лучшую из 21, и это наше окончательное значение, хорошее или худшее.

Обертывание в функции Python

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

Затем он возвращает значение «present-cards», которое я называю maxi (потому что это наша максимальная оценка на текущий момент), и ожидаемое значение «future-cards», которое я называю средним (потому что оно является средним из всех возможных будущих сценариев).

Как я уже упоминал, двойной цикл for для итерации заключен в функцию Numba. Метод оценки score_hands, который мы импортировали из упражнения в моем предыдущем посте, также заключен в кешированную функцию для лучшей производительности.

Именно так:

Хорошо, теперь мы готовы к игре. Да начнется действие!

Игра на флопе

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

Теперь вам будет предложено ввести количество игроков за столом, размер банка, процентиль вашего счета и, наконец, цену ставки, которую вы должны сделать, если вы вообще хотите сделать ставку. Затем нам нужно проанализировать, какое решение лучше всего принять, и для этой цели я создал функцию под названием should_call:

Код для полного процесса флопа таков:

А теперь давайте запустим один пример нашего кода с префлопом 11-Spades и 11-Clubs. Затем на флопе 5-червы, 6-бубновые и 2-бубновые. В безлимитной игре участвуют 5 игроков, и мы оцениваем размер банка в 20 $. Мы сделали ставку 2 доллара на префлопе, и нам нужно решить, коллируем ли мы еще одну ставку в 2 доллара на флопе.

Как мы видим, в этом случае математическое ожидание положительно и составляет 2.35 $, поэтому мы делаем колл.

Разыгрывание тёрна

Теперь приходит очередь, и это 5 бубен. Такой же процесс, как и раньше, но с картами в Терне. Вот так:

Наш код вернет это:

Теперь у нас есть пары, и мы должны уравнять еще одну ставку в 4 доллара, поэтому мы ставим в общей сложности 8 долларов на банк в 20 долларов. Поскольку у нас очень сильная рука, ожидаемая стоимость будет положительной и составит 7,34 доллара. Предположим, мы снова уравняем и видим реку.

Игра на ривере

Наконец, нам сдается карта ривера - туз пик. Набираем и пересчитываем:

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

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

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

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

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

Вы можете скачать код полного скрипта здесь:

Https://github.com/dirusali/pokerodds/blob/release-1.0/poker.py

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

Надеюсь, вам понравилось. Удачного кодирования!