CODINGАверин «Tim» Евгений timreset@mail.ru, javatalks.ruÑÈÌÓËßÖÈßÏÎÊÅÐÍÎÃÎ ÎÐÃÀÇÌÀÂêóðèâàåì â êîäèíã ïîêåðíûõ áîòîâ:ñîçäàåì ñèìóëÿòîð òðåíèðîâêè ýòîé ñòàòüå ìû ðàññìîòðèì ñîçäàíèå ñèìóëÿòîðà ïîêåðà. Òàê êàêïðàâèëà ïîêåðà íåìíîãî îòëè÷àþòñÿ ìåæäó ñîáîé, òî â êà÷åñòâåïðàâèë äëÿ ñèìóëÿöèè ìû âîçüìåì ïðàâèëà Holdem No Limit Pokerñ ñàéòà PokerStars. Íà îñíîâå ñèìóëÿòîðà ìû ñäåëàåì äâå èãðû — èãðàêîìïüþòåðà ñ æèâûì èãðîêîì è ïðîñòî èãðà êîìïüþòåðíûõ èãðîêîâìåæäó ñîáîé. Ïåðâàÿ èãðà íàì ïîíàäîáèòñÿ äëÿ òåñòèðîâàíèÿ.ÈÍÒÅÐÔÅÉÑÛСоздадим два интерфейса — Ilogic и IEventSimulation. Первый интерфейснужен для того, чтобы унифицировать вызов различных логик.То есть у нас имеется один интерфейс, который реализует различныелогики, и нам не нужно беспокоиться о хранении различных логик— мы храним только массив интерфейсов ILogic и вызываем методэтого интерфейса. У данного интерфейса есть только один метод —int getAnswer(float p, float totalBet, float curBet,float pot, int betting, int minRaise), он возвращает 0,когда нужно сбросить (fold), 1 при принятии ставки (call) и 2 приувеличении ставки (raise). Немного упростим модель — при рейзене будем выставлять значение ставки, а просто увеличим ставку наминимально возможное значение.Рассмотрим параметры этого метода: p — вероятность выигрыша (пронее читай в статье «Натягиваем сетевые poker room’ы» в июньском ][ или на диске к этому номеру), totalBet — все поставленные игрокомденьги за игру, curBet — текущее количество денег, которое нужнопоставить, pot — размер банка, betting — номер круга торговли,minRaise — минимальное количество денег, на которое нужно повыситьставку при рейзе. Второй интерфейс нужен для создания различныхоболочек для симулятора. В нашем случае будет две оболочки — дляигры компьютерных игроков с человеком и для игры компьютерныхигроков между собой. В интерфейсе IEventSimulation определены методы,которые позволяют сообщать оболочке обо всех изменениях в игре.Перечислим эти методы:changeBoardCard(int[] board) — метод вызывается приизменении карт на столе, changePot(int pot) вызываетсяпри изменении размера банка, changeMoneyOfPlayers(int[]money)вызывается при изменении количества денег игроков,postDillerMessage(String message)вызывается приотправке сообщений дилера, changeDillerPosition(intposOfDealer)вызывается при изменении позиции дилера,changePlayerStatus(int player, int status, int[] hand)вызывается при изменении статуса игрока.ÑÕÅÌÀ ÑÈÌÓËßÒÎÐÀКак известно, правила покера неоднородны и склонны друг от другаотличаться. Например, в круге торговли. Так, по правилам с сайтаPokerStars после первого круга торговли первым ходит активный игрокслева от дилера, а по другим правилам первым ходит игрок слева отигрока, который ходил первым на прошлом круге торговли. В симуляторереализованы правила Holdem Poker с сайта PokerStars. По размеруставок будем делать не NoLimit и не Limit, а кое-что свое — ограничимразмер рейза текущей ставкой.Всего логик семь: AggressiveLogic (разыгрывает даже слабые руки),CautiousLogic (разыгрывает только сильные руки), RationalLogic (действуетрационально), RaiseLogic (все время повышает ставку), CallLogic(все время поддерживает ставку), FoldLogic (все время сбрасывает),RandomLogic (случайно ходит).AggressiveLogic, CautiousLogic и RationalLogic используют в принятии108 XÀÊÅÐ 08 /139/ 10
codingФорма HoldemConsoleФорма HoldemFormрешений формулу p*pot = win и сравнивает win со своими ставками. Иначеговоря, использует формулу, которую мы обсуждали в прошлой статье (еслихочешь освежить память — вставь в свою ЭВМ диск к этому журналу изачитай ее). Единственное, что — CautiousLogic уменьшает вероятность,чтобы разыгрывать меньше рук, а AggressiveLogic увеличивает, чтобыразыгрывать больше. Оболочка HoldemForm рисует форму на swing’e иреализует два интерфейса — IEventSimulation и ILogic. Первый интерфейснужен для того, чтобы отображать на форме все события симуляции — раздачукарт, сообщения дилера, изменения состояний игроков и т.д. Второйинтерфейс мы создаем, чтобы пользователь мог сообщать симулятору своидействия — Fold, Call или Raise. Форма отображает все карты игроков ивероятности их выигрыша, поэтому она не подходит для честной игры скомпьютером, но зато идеально подходит для отладки симулятора.HoldemConsole просто выводит все сообщения дилера на экран.ÏÎÐßÄÎÊ ÑÈÌÓËßÖÈÈДля начала — небольшой алгоритм. Итак:1) Поставить большой и малый блайнды;2) Раздать карты игрокам (Пре флоп);3) Провести круг торговли;4) Положить три карты на стол (Флоп);5) Провести круг торговли;6) Положить четвертую карту на стол (Терн);7) Провести круг торговли;8) Положить пятую карту на стол (Ривер);9) Провести круг торговли;10) Открыть карты и определить выигрышную комбинацию.Соответственно, после каждого круга торговли нужно проверять, неостался ли в игре только один игрок. Если да, то весь банк уходит ему.Количество игроков, которые будут играть, равно девяти. Во времяигры их может стать меньше, но в начале их будет именно девять. Этосделано в целях упрощения симуляции — не надо заботиться о длинахмассивов.XÀÊÅÐ 08 /139/ 10ÊÎÄ ÑÈÌÓËßÒÎÐÀОпределимся с тем, что должен знать симулятор. Во-первых, симулятордолжен иметь следующие данные об игроках: их деньги(moneyOfPlayers), карты (handOfPlayers) и их состояние (в игре иливышли) — stateOfPlayers. Во-вторых, должен знать позицию дилера(posOfDealer), количество денег в банке (pot), размер большого блайнда(bigBlind) и текущие карты на столе (board). Для работы логикипринятия решений нужно также запоминать, сколько денег положилв банк каждый из игроков за текущую игру(totalBet). И самое главное— симулятор должен знать, что за игроки играют за столом, тоесть у него должен быть список всех игроков (playersList). Методы,нужные для симуляции: trade(int betting) — метод торговли,startGame() — главный метод, в котором происходит игра, intgetSinglePlayer() — если в игре остался один игрок, то методвернет индекс этого игрока, int getActivePlayer() — количествоактивных игроков в игре. К этим методам добавляются несколько setметодовдля изменения значений по умолчанию — setBigBlind(intbigBlind), setRoundCount(int roundCount). Можно сделатьметод по изменению количества денег перед игрой, но я считаю, этоне критично, ведь, в конце концов, это симулятор для тестированияалгоритмов, а там не важно, сколько денег у игроков в начале игры.Хотя, если делать на основе этого симулятора приложение для игры впокер, то стоит реализовать данный метод, плюс сделать возможностьизменения количества игроков в начале игры. Теперь рассмотрим подробнееметоды игры и торговли.STARTGAMEШаги симуляции в теории расписаны выше, на практике же к нимдобавляются следующие действия: обнуление переменных передначалом каждого раунда, перемешивание карт перед началом каждогораунда, проверка на наличие более одного игрока в игре после каждогокруга торговли. Небольшое замечание: хотя перемешивание карти занимает больше времени, чем вытаскивание случайной карты (какбыло сделано при определении вероятности выигрыша в прошлой статье),более наглядно и удобно это демонстрируется при сдаче карт. Еслипроизводительности будет не хватать, то можно будет оптимизироватьэтот алгоритм.Еще можно свернуть код проведения игры в цикл, поскольку сейчас тамимеют место повторяющиеся участки с проверками и проведение кругаторговли. Однако, их всего четыре, они не занимают много места, и присворачивании в цикл нужно будет изменять алгоритм раздачи карт настол, поэтому пока оставим все как есть.В исходном коде часто встречается такая конструкция:x = (x + 1) % 9;Эта массивная конструкция представляет собой всего лишь циклическоеувеличение значения переменной x от 0 до 8-9. В данном случаеоно означает количество игроков за столом.TRADEВходной параметр в методе, которой проводит круг торговли109