Схема покербота: — интерфейс пользователя — блок принятия решения — блок сбора статистики должно быть десять функций по определению комбинации: - isHighCard - isOnePair - isTwoPair - isSet - isStraight - isFlush - isFullHouse - isQuads - isStraightFlush - isRoyalFlush ============================================================= K = (suit, rank) Проверку на флеш делаем очень быструю: Флеш - это когда K1(1) == K2(1) == K3(1) == K4(1) == K5(1) Поверки по рангам: 1. Создаем масив S размером в 13 элементов: от 0(двойка) до 12 (туз) 2. Инициализируем его нулями - это счётчики 3. Для каждой карты K увеличиваем счётчик соответствующего номинала 4. Далее сортируем массив S по убыванию и проверяем результат: 1. если S[0] == 4 : "каре" 2. если S[0] == 3 2а. если S[1] == 2 : "фул" 2б. если S[1] < 2 : "тройка" 3. если S[0] == 2 3а. если S[1] == 2 : "две пары" 3б. если S[1] < 2 : "пара" проверку на Straight можно делать уже после определения всех остальных комбинаций. Straight получится, только если нет даже пары. Ведь все карты должны быть _разные_ по номиналу Пусть будет массив S. Не сортируя его задаем поиск первого элемента, содержащего единицу i = Array.IndexOf (S, 1) a затем рассматривать следующие варианты i < 0 элементов содержащих единицу нет, значит неповторяющихся номиналов нет, а это возможно только в случае 3+2 - значит Full_House i = 0 Самый первый элемент массива имеет значение 1. Значит у нас в руке один Ace. Проверяем на наличие Straight Если А2345, S(0)=S(1)=S(2)=S(3)=S(4) или 10JQKA, к S(0)=S(9)=S(10)=S(11)=S(12), то у нас Straght в противном случае сортируем массив и проверяем совпадение номиналов (на Full_House уже проверять не надо) i <= 8 Проверяем на наличие Straight таким образом: Если S(i) = S(i+1) = S(i+2) =S(i+3) = S(i+4), то у нас Straight В противном случае сортируем массив и проверяем совпадение номиналов i > 8 В этом случае Straight уже быть не может, т.к. 9й элемент массива - десятка сортируем массив и проверяем совпадение номиналов проблему подсчета совпадений номиналов он решил так: For i = 0 to 4 For j = 0 to 4 If hand(i).face = hand(j).face AndAlso i <> j matches += 1 End If Next j Next i hand(i).face - берется из структуры данных (колоды) этот кусок кода определит только то, что на руке есть что-то "не меньше пары", а вот что именно? счетчиков д.б. не два, а массив счетчиков (по кол-ву номиналов), тогда достигаем нужной гибкости: For i = 0 to 4 For j = 0 to 4 If hand(i).face = hand(j).face And i <> j then matches(face) += 1 End If Next j Next i соответственно затем: For i = 0 to maxfaces if matches(i) = 2 pairs += 1 elseif matches(i) = 3 threes += 1 elseif matches(i) = 4 kare += 1 endif Next i это будет работать при любом кол-ве карт в руке, т.е. покажет все пары, тройки и каре. >>> слишком много переменных вводится, тогда уж лучше массив... ============================================================= Как по вашему лучше устроить перебор всех пятикарточных комбинаций? У меня была мысль завернуть это дело в 5 циклов, но тогда встает проблема дубликатов, т.е. получается, что учитывается порядок карт. Как от этого можно избавиться? Можно не избавляться, а рассмотреть 52!/47! вариантов, что будет дольше в 5! = 120 раз. Я решил, что делать в 120 раз больше рассчетов - неэфективно, поэтому пошел вторым путем. В итоге получился пятиэтажный цикл. Но отрабатывает за доли секунды. Теперь буду искать и придумывать алгоритмы для оценки силы комбинации. Для начала - не буду учитывать кикеры, ограничиваясь просто типом комбинации (топ-карта, пара, две пары, сет, стрит, флеш, фул хаус, карэ, стрит-флеш: всего девять вариантов результата). Сразу приходит на ум посчитать промежуточные значения - количества карт одной масти, одного достоинства. Дальше - делать выводы на основании этих значений. ============================================================= Если взять ривер, то есть на столе лежат все пять карт, возможных комбинаций: 1 (без карманных карт) + 5*2 (с одной карманной картой) + 10 (с двумя карманными картами) = 21 если взять ситуацию, когда на столе лежат 4 карты из 5ти и нам известны 2 наших карманных карты, чтобы определить наши шансы на победу, нам перебором нужно будет проверить много комбинаций. В такой ситуации оптимальнее определить старшую комбинацию и, соответственно, карты, которые помогут младшей руке и не усилят старшую руку до натса. уже становится сложнее, не говоря про подсчеты шансов одной руки против другой, когда на столе нет ни одной карты, то есть префлоп у тебя есть 7 карт, опиши алгоритм, как ты определишь, что у тебя стрит? 1. Сортировка по возрастанию номинала 2. перебор с сравнением с предыдущей картой 3. Если разница с предыдущей больше 1 номинала - значит не стрит Если дошли до конца без прерывания - значит стрит пример руки где твой алгоритм не работает 245678A Это частный случай. Можно исключить его предварительным поиском туза и двойки. Если есть - то тузу присваивается низший приоритет A2TJKQ Ставишь счетчик сколько карт прошло подряд, если следующая не по порядку - скидываешь в 0. Если после перебора счетик >= 5 - значит стрит. + если с низким тузом не достигая 5 вышел сброс счетчика, то тузу возвращается высший приоритет туза "делим" на две карты с номиналом 1 и 14 соответственно (для стрита). тока непонятно зачем тебе это? без размеров стэков, банка, нет смысла считать шансы делаем два словаря: в одном ключами являются ранги карт, в другом - их масти, а в качестве значений - количество вхождений. по размерам словарей уже можно будет сделать определенные выводы, анализировать их или отбрасывать =================================== код расчёта вероятностей в холдеме делал просто - проверял пару тысяч случайных партий и смотрел результаты. результат довольно-таки правильный получается, а время расчёта мало. Самые первые покерные боты так и делали. для префлопа достаточно достаточно найти самую сильную руку, а для этого не обязательно сравнивать все-со-всеми. если это игрок, то он победил, иначе - выйграл бот ------- необходимо узнать вероятность того что после вскрытия противник окажется сильнее... есть набор комбинаций разной силы... (разные стриты суть разные комбинации) известно от 5 до 7 наших карт и от 3 до 5 карт соперника и надо знать вероятность того, что при вскрытии комбинация соперника окажется сильнее/слабее (я не учитываю вариант равносильных комбинаций когда вступают нетривиальные правила - такое маловероятно и этим вполне можно пренебречь). во первых - необходимо изучить вероятность того, что в 7 картах окажется комбинация и понять как те или иные открытые карты влияют на эту вероятность... (при этом набор открытых карт можно считать упорядочеными == допустим в руке карты 1 и 2, на столе 3,4,5,6,7) например, рассмотрим тузовое каре: вероятность всего есть вариантов расклада которые нас устраивают: (4*3*2*1) * (46*45*44) * (5*5*5) = С (выбираем в какой последовательности идут тузы, потом оставшиеся 3 карты и то, между какими тузами будут каждая из них) в итоге получаем вероятность данной комбинации С/n где n=50*49*...*44 - всего комбинаций (упорядоченых) теперь по очереди рассматриваем известные карты... Допустим первая карта у нас не туз... Тогда C становится = (4*3*2*1) * (45*44) * (5*5) (то есть теперь точно знаем какой у нас не туз и где он лежит) Соответственно если туз, то С= (3*2*1) * (46*45*44) * (4*4*4) и вероятность четверки - С/n, причем n теперь 49*48*..*44 (одну карту уже знаем).... ....Итак, проанализировав вероятность каждой комбинации для себя и для соперника (думаю вполне понятно как) предстоит последний шаг: оценить вероятность победы... тут все просто. допустим мы знаем что с вероятностью x0 мы получим кукиш с маслом - в таком случае противнику достаточно выкинуть хоть что-то содержательное - это равно сумме вероятностей для каждой комбинации (не тупая сумма, а надо учеть что вероятность выпадения каре уже включает в себя уже посчитаную вероятность выпадения сета ... считаю что это надо вкладывать в рассчет С для таких комбинаций и считать что появление, например, четвертого туза убивает комбинацию "тузовая тройка", которая просто неприемлет наличия четырех тузов). Так вот -- допустим что при кукише в x0 случаев противник с вероятностью y0 выигрывает.... Тогда вероятнность события "противник выиграл при том что у нас кукиш" равна x0*y0 ... Аналогично оценивается вероятность выигрыша противника для каждой нашей возможной комбинации (вполне понятным образом)... Сумма таких вероятностей и есть полная вероятность выиграша.... А еще можно попробовать то же самое, но для неупорядоченых наборов карт... Быть может первая часть получится проще... ---------------- Можем сравнивать свою руку со всеми наиболее вероятными руками противника. Всех возможных комбинаций рук (по силе) чуть меньше 8-ми тысяч. Нужно подумать насколько будет просто расчитать вероятность каждой достаточно много игроков в покер просто оперируют вероятностями ( т.е., например - вероятность того, что у соперника при известных своих 2 картах + 5 картах флопа стрит примерно 20%. у меня пара тузов, а во флопе ничего, кроме недострита . А дальше всё зависит от степени агрессивности нашей игры - увеличить ставку или, например, вообще уйти из этой раздачи. Опять-же соперник может блефануть, а мы можем испугаться, ну и конечно с точностью до наоборот). А по поводу психологии - это да, если ты садишься в казино против дилера играть, то тут никакой психологии, т.к. дилеру по барабану - он автоматом работает: раздать всем по 2 карты включая себя -> подождать решения игроков - > вскрыть свои карты -> сравнить свою руку с рукой игрока и либо оплатить в зависимости от комбинации, либо забрать ante и bet. Но если играть с соперником, то это совсем другое дело - как можно оценить математически такую ситуацию - в руке пара дам, во флопе три валета + ну и там ещё 3 и 4, к примеру, и соперник ставит максимальную ставку. И тут уже можно понервничать - вдруг у него каре на валетах ? А может фул-хаус, но младше ? А если каре, то в деньгах теряешь очень много? Вот тут уже включаются психология и опыт анализировать предыдущие действия соперника. Для таких игр как покер можно теоретически расчитать вероятности действий (fold, call, raise) в любой ситуации. Т.е. в каждой конкретной ситуации нет единствненно правильного решения. Нужно их чередовать с определенной частотой. Но найти теоретическое решение жутко сложно. Нам остается лишь приближаться к нему. Люди это делают с помощбю психологии. У машин может быть другой путь. --- Для игры в блэк джек существует базовая стратегия, т.е. стратегия, при игре в которую человек минимизирует свой проигрыш в казино. НО - казино не борется с людьми, которые играют по базе. Вот только когда человек начинает считать карты, тогда да - человеку в блэк джек запрещают играть. Это я к тому, что оптимальная стратегия не всегда даёт положительный результат. Иногда уверенность в том, что вот она - система мечты, которая должна грести деньги лопатой, может сыграть очень плохую роль. В игре 6 колод - они перед игрой тасуются, затем специальной пластиковой картой делят эту большую колоду на 2 . Просто втыкают её где-то в районе 1/3 от конца большой колоды и всю эту конструкцию пихают в башмак. Игра идёт до выхода этой пластиковой карты (красной) . В колоде одинаковое количество считаемых больших и малых карт. Мы считаем дисбалланс больших и малых карт. Т.е. если у нас выходит много мелких, то счёт растёт, если выходит много крупных, то падает. В случае, когда реальный счёт (это текущий делённый на оставшееся кол-во колод в башмаке) становится равным 3 или больше, то вероятность начала выхода крупных карт достаточно высока (какая - сказать не могу - не знаю ). На это и играют. А после выхода красной карты всё опять перемешивается. Ах, да - отыгранные карты в колоду по ходу текущего шафла не возвращаются, они возвращаются только после выхода красной карты (т.е примерно 4 колоды разыгрываются). Казино вообще никакой информацией не пользуется - тупо набираются карты до 17 очков, - 17-21 - останавливается, сравниваются очки и если у игрока больше, то платит 1:1 иначе забирает, - если нет 17 - берёт карту 22 - перебор - всем оплачивается ставка 1:1 (или 3:2 для BlackJack) ------- Монте-Карло может помочь посчитать вероятность раскладов. Но как он может помочь делать правильные заявки? Не надо забывать что кроме Выложенных карт - информацию о ситуации в партии дают заявки соперников, а своими заявками мы предоставляем информацию соперникам. Тех, кто приспосабливается к стилю соперника, можно ловить на этом - тотальным и резким переключением на другой стиль. Когда соперник заметит, что мы играем совсем иначе - мы переключимся на еще более другой :) игра: два игрока, каждый берет взакрытую по одной карте (из колоды в 52 карты). В результате открытия выигрывает сильнейшая по номиналу карта (всего 13 номиналов) - выигрывает большую ставку. Если игрок пасует - то он проигрывает малую ставку. Первой заявкой SB может либо ставка либо полблайнда (уравнивает с BB). После этого у игроков возможность только либо спасовать либо сравнять. Сказать "Ставка" за торговлю может только один игрок, и только один раз. После ответа "чек" на "уравнивание блайнда" первым игроком, либо если номиналы карт после открытия одинаковы - партия переигрывается (право сдачи переходит ко второму игроку) для техасского холдема никакого толкового AI не написать в принципе. Оперировать ведь приходится только своими картами, плюс "общими" - а их всего 2 вначале и 7 в конце - какие бы предположения мы ни делали, мы ничего не сможем сказать о том, что у противников во взятке. Единственные данные у нас - их поведение, то есть, делает противник check, bet, или повышает ставку, и насколько. Пасующий нас уже не интересует - все что из него можно выжать, так это то, что у него нет "парирующих карт", но это увеличивает вероятность выпадения этих карт следующим пиком на довольно незначительную величину. Разве что игра идет против 20 противников, а пасуют сразу 19 :). Приходится оперировать настолько недостоверной информацией, что гораздо большее значение имеет расклад, чем все твои выкладки :). Я пробовал играть в холдем с компом. Вся стратегия сводится к пасованию, когда противник делает большие ставки, а у тебя нет ничего интересного, игре наудачу, когда у тебя неполный флеш или стрит, и пробиванию большими ставками когда вначале приходит пара. Вот что нужно: 1) на префлопе у нас две карты. Надо узнать их "вес", и может быть лучше это все скинуть. 2) На флопе получается 5-ти карточная комбинация. Надо просчитать что выпало или что может выпасть если придут еще две карты и какой процент вероятности что это выпадет. Надо узнать какие комбинации с этими картами могут выпасть у соперников. Может быть и играть не стоит. 1. у тебя только считаются вероятности, но нет стратегии 2. в твоем подсчете не учитываются действия игроков (заявки) Например для упрощенного варианта с одним циклом торговли есть сведение к модификации задачи матричных игр, и соответственно методы получения вероятностной оптимальной стратегии (методами линейного программирования) - то есть есть алгоритм четкого и полного решения. Есно алгоритм учитывает всё - и предшедствующие заявки, и информацию о своей карте (картах). нет проблем с написанием простейших Ботов (определением возможных действий). так как условия на действия элементарны - пока нет ставки возможен чек, если весь круг прочекал, то торговля завершена, повышение/ставка возможны не более четырех раз за круг торговли, посленим делает заявку игрок перед последним поднявшим/поставившим ставку, две первые ставки (в темную) ставкой/повышением не считаются. Да, посчитать вероятность это лишь малая часть дела. И поможет только чтоб бот совсем не тупил. объясни что ты подразумеваешь под "перебором". Перебираем возможные действия игроков (fold, check, call, bet, raise). Что в результате такого перебора мы получим? И каким образом? псевдокод: Eval(Var OF[КоличествоСоперников]) // Searсh(Соперник;Depth;Var OF[КоличествоСоперников]) Var OF1[КоличествоСоперников]; if Depth=0 then Eval(OF) Else OC:=-inf; ЛучшееДействие:=0; Для Каждого действия (Заявки) ВыполнитьДействие; Search(?(Соперник=КоличествоСоперников,1,Соперник+1),Depth-1,OF1); if OF1[Соперник]>OC Then ЛучшееДействие:=ТекущееДействие; OC:=OF1[Соперник]; OF:=OF1; // передача на уровень вниз Endif; КонецЦикла; Endif; Вот перебор на торговлю... (Это если у нас есть решение в чистых стратегиях) То есть для игр на несколько соперников с полной информацией, где каждый пытается максимизировать свою оценку. Не рассмотрен случай когда два (и более) хода дают одинаковую оценку. Есть другой вариант перебора - пессимистичный, это когда каждый соперник не максимизирует свой выигрыш, а минимизирует наш... В твоем переборе упущен самый ключевой момент. Когда игра заканчивается еще при depth > 0. Как посчитать оценку не зная карт соперника? Оценка считается методом монте-карло, причем неважно завершен перебор или нет. (то есть кидаем случайным образом карты, и оцениваем ожидаемый выигрыш) Осталось увязать монте-карло с заявками (и открытыми картами) в переборе и вероятностными стратегиями. Четкой мат. модели на более чем 2 соперников в любом случае не существует, и программа выигрывающая слабому сопренику будет проигрывать сильному, и наоборот. То есть всё-таки нужны адаптивная модель! Но в любом случае получается что лучше делать пессимистичекий вариант - в переборе все пытаются минимизировать оценку одной стороны, а эта сторона (за кого обдумываем) Пытается максимизировать свою оценку. Да еще и в играх более чем двух соперников нет лучшего хода (не работает правило Цермело, нет лучшего хода и нет оптимальной стратегии), То есть игра математически несколько бессмысленна. :) Ну не совсем. Можно рассматривать игру как 1 против всех, т.е. получается игра двух соперников. А т.к. в покере наши соперники не могут обмениваться информацией между собой (по крайней мере это запрещено) у них нет значимого преимущества. Также можно быть уверенным, что они ни о чем не договорились перед игрой. Т.е. покер вполне можно рассматривать как игру двух противников, если очень хочется. В покере мы не можем за какое-то реальное время посчитать вес игры :( Даже зная стратегии обоих игроков. И стратегии слишком велики (если все варианты всех четырех кругов торговли перевести в возможные стратегии - их становится слишком много) То есть мы можем посчитать вес игры только серией тестовых матчей. --- Есть еще идея составлять стратегии не для каждой возможной комбинации карт, а для вероятностей выигрыша против средних карт. Т.о. в стратегии будет столько параметров сколько мы захотим. Чем больше параметров тем точнее будет учитываться вероятность выигрыша, но труднее будет оптимизировать матожидание. Когда я только начал думать о боте для покера, сразу пришла идея просто торговаться в зависимости от вероятности выигрыша, т.е. иметь только один набор параметров. Например, на префлопе открываемся или отвечаем когда МО * Параметр1 >= сумма ставки на флопе: МО * Параметр2 / коэффициент агрессивности противников в предыдущей торговле >= сумма ставки на торне: МО * Параметр3 / коефициент агрессивности противников в предыдущей торговле >= сумма ставки на ривере: МО * Параметр4 / коефициент агрессивности противников в предыдущей торговле >= сумма ставки И поднимать reraise, когда это соотношение превосходит какие-то заранее заданные коэфициенты. эту фишку можно получить автоматически, причем на порядки (на сколько позволят ресурсы компа) более правильно. Осталось только научится определять вероятности выигрыша против средних карт. Я думаю, что это решаемая задача. Мы можем позволить перебор + Монте-Карло (случайные расклады), который при увеличении глубины перебора, и при увеличении количества случайных раскладов даст точное решение... При этом в ОФ если досчитались до конца - то присваиваем сам выигрыш, если нет - то ожидаемый... Если нам при этом нет надобности хранить все перебранные расклады - то хватает памяти, и значит мы можем написать программу которая при бесконечном времени на обдумывание дойдет до финальной позиции в переборе, а при увеличении количества попыток (испытаний) в Монте-Карло - даст точную оценку финальных позиций. При этом ожидаемый выигрыш считается легко (если досчитались до конечной позиции) по Байесу. Для каждого расклада - Его вероятность изначально * Вероятность каждого хода (действия) в дереве перебора * Выигрыш в конечной позиции (Для случая если перебрали все действия и все расклады) . А вот проблема - посчитать вероятность каждого хода (Дествия) которая зависит от ожидаемого выигрыша (чтоб её посчитать нужно построить дерево перебора), а Сам ожидаемый выигрыш (вес игры) считается через эту вероятность... ------- играют 10 человек. статистика побед: Straight 29.2345 Two Pair 23.7132 Full House 15.1937 Flush 14.3733 Pair 8.3290 Three Of A Kind 7.6079 Four Of A Kind 1.4532 Straight Flush 0.0627 Royal Flush 0.0326 High Card 0.0002 Еще статистика: Pair 44.00937 Two Pairs 23.50828 High Card 17.65895 Straight 6.87433 Flush 3.03835 Full House 2.60682 Three Of A Kind 2.12946 Four Of A Kind 0.16525 Straight Flush 0.00672 Royal Flush 0.00277 Это процент выпадания рук. ---------------------------- Вот посчитал табличку матожидания выигрыша на префлопе Условия таковы: - все игроки (10) делают одну ставку перед раздачей - больше не торгуются - разделенные поты не учитываются (и тут все более-менее хорошо с бродвеем, но полный швах с мелкими немастевыми фосками (особенно - дырявыми) !!! Сделал 128 млн. случайных раздач и посчитал какие карты сколько ставок в среднем выигрывают: AAu - 3.027 KKu - 2.540 QQu - 2.174 AKs - 2.030 JJu - 1.899 AQs - 1.889 KQs - 1.825 AJs - 1.788 KJs - 1.732 ATs - 1.711 QJs - 1.698 TTu - 1.697 AKu - 1.681 KTs - 1.664 JTs - 1.642 QTs - 1.639 99u - 1.549 A9s - 1.521 AQu - 1.520 T9s - 1.483 KQu - 1.474 A8s - 1.465 K9s - 1.462 J9s - 1.451 88u - 1.447 A5s - 1.446 Q9s - 1.441 A4s - 1.420 A7s - 1.417 AJu - 1.399 A3s - 1.394 77u - 1.380 A6s - 1.374 KJu - 1.364 A2s - 1.358 T8s - 1.357 K8s - 1.347 QJu - 1.345 98s - 1.342 J8s - 1.320 ATu - 1.311 66u - 1.311 Q8s - 1.308 K7s - 1.303 JTu - 1.292 87s - 1.286 KTu - 1.285 QTu - 1.272 K6s - 1.264 55u - 1.262 97s - 1.256 K5s - 1.247 44u - 1.244 76s - 1.240 T7s - 1.236 33u - 1.234 22u - 1.230 K4s - 1.227 K3s - 1.209 J7s - 1.203 Q7s - 1.199 86s - 1.196 K2s - 1.195 54s - 1.191 65s - 1.187 75s - 1.167 Q6s - 1.163 Q5s - 1.150 96s - 1.144 Q4s - 1.130 T9u - 1.130 T6s - 1.119 Q3s - 1.117 64s - 1.110 53s - 1.108 85s - 1.103 Q2s - 1.102 A9u - 1.102 J6s - 1.097 J5s - 1.083 J9u - 1.079 J4s - 1.069 43s - 1.065 K9u - 1.064 74s - 1.063 Q9u - 1.054 J3s - 1.050 95s - 1.048 A8u - 1.038 J2s - 1.033 T5s - 1.033 T4s - 1.015 A5u - 1.013 52s - 1.009 63s - 1.008 84s - 1.001 T3s - 0.997 T8u - 0.990 A4u - 0.989 A7u - 0.988 98u - 0.987 42s - 0.987 T2s - 0.985 A3u - 0.961 73s - 0.959 94s - 0.956 32s - 0.944 93s - 0.939 J8u - 0.938 A6u - 0.938 87u - 0.934 K8u - 0.932 92s - 0.925 A2u - 0.923 Q8u - 0.914 83s - 0.910 62s - 0.907 82s - 0.895 97u - 0.894 76u - 0.893 K7u - 0.884 72s - 0.873 T7u - 0.862 54u - 0.845 65u - 0.841 K6u - 0.840 86u - 0.838 K5u - 0.822 J7u - 0.812 75u - 0.810 K4u - 0.799 Q7u - 0.795 K3u - 0.780 96u - 0.772 K2u - 0.764 53u - 0.759 64u - 0.755 Q6u - 0.754 85u - 0.739 Q5u - 0.737 T6u - 0.736 Q4u - 0.717 43u - 0.713 74u - 0.704 Q3u - 0.699 J6u - 0.698 Q2u - 0.683 J5u - 0.680 95u - 0.671 J4u - 0.660 52u - 0.653 63u - 0.649 T5u - 0.645 J3u - 0.643 42u - 0.629 J2u - 0.629 84u - 0.628 T4u - 0.623 T3u - 0.607 73u - 0.593 T2u - 0.590 32u - 0.584 94u - 0.574 93u - 0.554 62u - 0.540 92u - 0.538 83u - 0.534 82u - 0.514 72u - 0.500 Получилось похоже на правду. Кто-нибудь еще может сделать такое? Хочется проверить. Вот для игры двух игроков (и тут чуть лучше ситуация с мелкими фосками и уже почти что правдивая ситуация с бродвеями !!!) : AAu - 1.697 KKu - 1.641 QQu - 1.592 JJu - 1.546 TTu - 1.499 99u - 1.440 88u - 1.384 AKs - 1.328 77u - 1.325 AQs - 1.312 AJs - 1.298 AKu - 1.295 ATs - 1.282 AQu - 1.277 66u - 1.268 AJu - 1.262 KQs - 1.260 A9s - 1.248 ATu - 1.245 KJs - 1.245 A8s - 1.231 KTs - 1.230 KQu - 1.221 A7s - 1.211 55u - 1.210 A9u - 1.207 KJu - 1.204 QJs - 1.201 K9s - 1.195 A5s - 1.191 A8u - 1.189 A6s - 1.189 KTu - 1.189 QTs - 1.186 A4s - 1.174 A7u - 1.168 K8s - 1.161 A3s - 1.158 QJu - 1.158 K9u - 1.151 JTs - 1.151 Q9s - 1.150 K7s - 1.146 A5u - 1.146 44u - 1.145 A6u - 1.145 A2s - 1.141 QTu - 1.141 A4u - 1.127 K6s - 1.127 Q8s - 1.120 J9s - 1.114 K8u - 1.114 K5s - 1.110 A3u - 1.110 Q9u - 1.104 JTu - 1.104 K7u - 1.098 K4s - 1.092 A2u - 1.091 T9s - 1.086 Q7s - 1.082 J8s - 1.081 33u - 1.080 K6u - 1.076 K3s - 1.075 Q6s - 1.069 Q8u - 1.068 J9u - 1.064 K5u - 1.061 K2s - 1.060 Q5s - 1.052 T8s - 1.050 J7s - 1.046 K4u - 1.042 Q4s - 1.035 T9u - 1.032 Q7u - 1.032 J8u - 1.029 K3u - 1.023 98s - 1.020 Q3s - 1.018 Q6u - 1.017 22u - 1.015 T7s - 1.015 J6s - 1.012 K2u - 1.005 Q2s - 1.001 J5s - 1.001 Q5u - 1.000 T8u - 0.996 J7u - 0.993 97s - 0.987 J4s - 0.983 T6s - 0.981 Q4u - 0.981 J3s - 0.966 98u - 0.966 87s - 0.965 Q3u - 0.963 T7u - 0.961 J6u - 0.957 96s - 0.950 J2s - 0.948 T5s - 0.947 J5u - 0.944 Q2u - 0.943 T4s - 0.933 86s - 0.931 97u - 0.930 J4u - 0.925 T6u - 0.922 95s - 0.918 T3s - 0.917 76s - 0.915 87u - 0.907 J3u - 0.905 T2s - 0.900 85s - 0.898 96u - 0.893 T5u - 0.888 J2u - 0.887 94s - 0.884 75s - 0.880 T4u - 0.874 93s - 0.869 65s - 0.869 86u - 0.869 84s - 0.861 95u - 0.858 T3u - 0.855 76u - 0.854 92s - 0.853 74s - 0.846 54s - 0.839 T2u - 0.836 85u - 0.836 64s - 0.834 83s - 0.825 94u - 0.818 75u - 0.817 82s - 0.811 73s - 0.808 65u - 0.806 93u - 0.805 53s - 0.803 63s - 0.797 84u - 0.795 92u - 0.786 43s - 0.783 74u - 0.779 54u - 0.771 72s - 0.771 64u - 0.767 52s - 0.767 62s - 0.761 83u - 0.756 42s - 0.745 82u - 0.742 73u - 0.739 53u - 0.735 63u - 0.730 32s - 0.727 43u - 0.713 72u - 0.700 52u - 0.694 62u - 0.690 42u - 0.673 32u - 0.656 -------------------------------- При отсутсвии статистики на противника, можно попытаться "пробить" его характер Т.е к примеру играть 20 (ну или какое то определенное число) рук в характере полного маньяка - постоянно рейзим. Т.о. можно определить насколько тайтово играет противник, насколько можно его "запугать" Затем играем 20-40 рук в полном тайте - играем только пары или овер карты. Определяем таким образом агрессивность противника Затем играем в нормальном стиле (что бы это не было :) Смотрим во всех трех случаях собранную статистику VPIP, PFR, AF, WSD - делаем выводы ------------------------------ Если сидишь на SB и делаешь рейз, то это идет в VPIP А в остальном все упирается в чистую статистику. При наличии достаточного количества сыгранных рук, можно довольно точно спрогнозировать поведение противника. *** VPIP - voluntary put $ in the pot (%) - процент рук, когда игрок делал ставку на префлопе. Здесь все его call & raise, но нет check на BB и fold на SB Это важный показатель, делящий игроков на лузовых и тайтовых у хороших игроков VPIP не превышет 25%. По этому показателю сразу видно с чем человек играет. Чем меньше этот показатель, тем с лучшими картами игрок входит в игру PFR - preflop raise (%) - процент рук, когда игрок делал raise или re-raise до флопа второй по важности параметр, делящий игроков на пассивных и агрессивных Адекватное значение в районе 7-13%. Рейз от игрока с низким PFR значит что у него что-то реальное, типа старшей пары или AK. Высокий PFR в сочетании с высоким VPIP указывает на маньяка. AF - agression factor - определяется как (% bet + % raise) / % call Это, третий по важности параметр, позволяющий оценить агрессивность игрока, начиная с флопа Средний фактор агрессии на флопе и терне 3 (диапазон 2-4), на ривере ниже - 2 (1,5-2,5) WSD - went to showdown (%) - процент сдач, в которых игрок смотрел флоп и дошел до вскрытия Еще одна важная характеристика, позволяет оценить лузовость/тайтовость противника после префлопа Средняя величина - 32%, диапазон 27-38%. По величине WSD лучше всего можно определить “сопротивляемость” блефу. Игрок с высоким WSD (так называемый “колинг стейшн”) будет отвечать со своей младшей парой или даже голым тузом до упора, блеф становится бессмысленным. Игроки с низким значением на опасном флопе они могут сбросить любую карту, кроме натса, против них блеф наиболее эффективен. Указанные выше цифры - для длинных столов. Для столов 6 max картина должна быть примерно следующая: VPIP 23-25%, PFR 14-16%, при желании можно играть чуть тайтовее, но соотношение между VPIP и PFR так и должно оставаться чуть больше чем 2:1 (например, 22% и 12%). AF на флопе и торне должен быть порядка 2,5-3,5, на ривере - 1,5-2,5 WSD в пределах 35-38%. >VPIP при игре один-на-один такой фишки нет :) есть. Если сидишь на SB и делаешь рейз, то это идет в VPIP -------- Почему вы решили использовать только самые простые параметры типа VPIP, AF, WtSD? я бы наделил бота более серьезными анализаторными характеристиками а именно: Preflop-статистика: - VP$IP: значение VP$IP показывает, сколько процентов своих рук игрок действительно играет. С его помощью можно узнать, к какой категории игроков относится противник. Показатель VP$IP должен быть 13-20% на полных столах с 7-10 игроками (Full-Ring, FR) и достигать 17-23% на коротких столах с 5-6 игроками (Shorthanded, SH). - PFR: Preflop Raise показывает, сколько процентов своих рук соперник рейзит на префлопе. Соотношение между показателями VP$IP и PFR является важным для нашей собственной игры. А именно: значение PFR всегда должно быть константным по отношению к VP$IP и составлять примерно 2/3 от VP$IP. Например если у нас VP$IP между 13 и 20% при игре за полным столом (FR), то PFR должен быть между 8 и 14%. В игре за короткими столами соответственно он должен быть 11-16%. - Cold Call Preflop: как часто соперник вступает в руку, коллируя чужой рейз. Если значение этого показателя соперника превышает 5%, то надо осторожно действовать против него после флопа, потому что ему трудно сбросить свою руку. - Folded Small Blind to Steal: как часто игрок, находясь на позиции Small Blind (SB), сбрасывает свою руку в ответ на попытку украсть блайнды. Значение этого показателя в нашей игре должно составлять примерно 85%. Если вы хотите защитить свой SB, тогда вы должны в ответ сделать ставку в три раза большую чем соперник и тем самым удержать инициативу. -Folded Big Blind to Steal: по аналогии к предыдущему показателю, указывает, как часто игрок, находясь на позиции BB, сбрасывает свою руку в ответ на попытку украсть блайнды. Значение этого показателя в нашей игре должно составлять примерно 70%. Очень важно не стараться защищать свой ВВ со слабыми руками. Postflop-статистика - Continuation Bet: как часто соперник делает продолженную ставку на флопе (continuation bet), если он повысил на префлопе. - Calls Continuation Bet / Raises Continuation Bet / Folds Continuation Bet: эти показатели очень важны для нашей собственной игры. Если соперник имеет большое значение Calls Continuation Bet, тогда не стоит делать против него продолженную ставку, так как он скорее всего её уравняет. Также стоит принять во внимание, если кто-нибудь часто поднимает в ответ на продолженную ставку (Raises Continuation Bet). При игре один-на-один против соперника с высоким показателем Folds Continuation Bet, можно, наоборот, чаще использовать продолженную ставку и выигрывать таким образом. - Flop Aggression / Turn Aggression / River Aggression / Total Aggression Postflop (AF): все эти четыре показателя являются важными индикаторами степени агрессивности игрока как в общем, так и в отдельных раундах торговли. Они помогут при принятии решений, как например: стоит ли коллиривать ставку агрессивного игрока или сбросить карты против пассивного. Обратите внимание: если у соперкика очень высокое значение показателя River Aggression в сравнении с другими (Flop, Turn), то стоит быть начеку на ривере, так как оппонент склонен к тому, медленно разыгрывать очень сильные руки (slowplay). Total Aggression Postflop не должен превышать 3.5. Значение Flop Aggression должно находиться между 2.5 и 4.5. Если оно составляет больше, чем 4.5, то вам иногда надо отказываться от ставки на флопе со слабой рукой. Показатель River Aggression должен находиться в пределах между 1.0 и 2.5. Если он меньше, то вы часто теряете вэлью и долгосрочно будете иметь меньше выигрыша. - Bet Flop / Bet Turn / Bet River / Bet Total: Если какой-нибудь игрок очень часто делает ставки, то это значит, что он играет не только свои сильные руки, но также и слабые. Если же он редко ставит, то он скорее всего пассивный и осторожный игрок, который не может решиться сделать ставку, имея хорошую руку. - Check Raise Flop / Check Raise Turn / Check Raise River / Check Raise Total: самый важный показатель из этих четырёх - Check Raise Flop, так как если у соперника высокое значение этого показателя, то лучше отказаться от продолженной ставки (continuation bet) на флопе. Если у нас очень сильная рука на терне и мы не боимся предоставить сопернику возможность бесплатно увидеть следующюю карту, тогда мы можем сыграть check/raise на терне. В отличие от терна, check/raise на ривере связан с большим риском, так как у соперника появляется шанс увидеть шоудаун бесплатно. Значение вашего показателя Check Raise Total не должно превышать 1%, так как иначе вы теряете вэлью. - Raise Flop / Raise Turn / Raise River / Raise Total - Folded to Flop Bet: если у кого-нибудь из ваших соперников значения этого показателя выше среднестатистического, тогда может быть выгодным, имея слабую пару или хороший дро, сделать ставку и тем самым полублефовать, если на флопе ещё никто не вступил в игру. - Folded to Turn Bet: иногда встречаются соперники с явно низким значением Folded to Flop Bet, но которые много сбрасываются на терне (высокий Folded to Turn Bet). В таком случае имеет смысл, при наличии у нас флеш-дро или топ пары с хорошим кикером (Top Pair/Good Kicker, TPGK) и борде, на котором возможны дро, сделать вторую ставку на терне, чтобы выиграть руку, не доходя до шоудауна. Игроки, которые редко сбрасывают свои карты на терне, чаще всего также редко сбрасывают на ривере и поэтому против них стоит меньше блефовать. - Folded to River Bet: если наш соперник редко сбрасывает свою руку в ответ на ставку на ривере, тогда можно, при наличии сильной руки, сделать большую ставку, чем обычно. В том случае, если кто-то, наоборот, часто сбрасывается на ривере, можно чаще блефовать и тем самым больше выигрывать. Некоторые игроки чрезмерно много коллируют на флопе и терне и затем сбрасываются на ривере. В такой ситуации можно сделать, так называемую 3rd Barrel Bluff ставку. Однако такой ход связан с большой долей риска - Went to Showdown: WTS тесно связан с тремя, только-что представленными, показателями Folded to .... При игре против соперника с высоким значением WTS надо полностью отказаться от блефа и играть, в основном, только ваши сильные руки, так как соперник готов заплатить много денег. Также не стоит чрезмерно агрессивно играть дро против таких соперников, потому что они редко сбрасывают карты. Банк в анлиме очень важен - ситуация типичная: опп (TAG) из EP делает рейз 4ББ, его стек 100ББ, все жмут фолд, мы на баттоне у нас тоже 100ББ - у нас карманные 77, мы знаем что игрок тайт и из ранней позиции делать рейз будет только с очень сильными руками (например JJ+, AQs+, AKo) согласно этого спектра наши оддсы на выигрыш 7.5:1, взглянем на потоддсы - они где-то 1.30:1, текущих потоддсов очень сильно не хватает для колла, но смотрим ситуацию после флопа - 1) 89% - мы не усилились в ответ на ставку соперника - фолд. 2) 10% мы усилились, если у соперника действительно сильная рука, то с ней он будет активно ставить и до ривера в среднем поставит 40ББ, 3) 1% Мы усилились, но соперник усилился тоже до старшего сета или лучше, здесь скорее всего мы проиграем 40ББ. проверим достаточно ли этого - текущая ставка 4ББ - предполагаемый средний выигрыш в случае усиления - 0.90*40 (сет выиграет в 90% случаев) ББ, средний проигрыш в случае усиления нас обоих - 0.1 * 40 ББ (сет проиграет против старшего сета или лучше в 10% случаев) тем самым предполагаемые винпотоддсы составляют 36ББ - 4ББ = 32:4 (плата за колл) или 8:1, что немного лучше чем 7.5:1 - можно коллировать А вот если бы у нас или соперника было бы стек скажем в 20ББ, то сам видишь: 5:1 это фолд ани колл. -------- У ПТ в базе есть такие поля pre_flop_raise flop_raise flop_ck_raise turn_bet_raise turn_bet_ck_raise river_bet_raise river_bet_ck_raise а так же типа preflop_call preflop_raise preflop_flop preblop_bet и т.д на каждого игрока, игру, сессию. --- бот для каждой возможной руки соперника счиатет коэфициент правдоподобия, т.е. насколько похоже, что имея такую руку, соперник бы стал так торговаться. И при оценке силы своей руки он при моделировании по Монте-Карло выбирает руки соперника не с одинаковой вероятностью, а в соответствии с их коэф. правдоподобия. Правда, для оценки коэф. правдоподобия для каждой возможной руки пришлось ограничиться всего 6 симуляциями, т.к. если сделать больше, то возникала угроза, что бот может проиграть по времени. А для оценки силы собственной руки проводится 1000 симуляций. Я сделал бот, который делает ставки прямо пропорционально вероятности выигрыша. я не вводил угадывание карт соперника, так что он считает, что все руки соперника равновероятны Второй бот играет по заманивающей стратегии. На первых кругах торговли делает случайные ставки от нуля до той величины, которую поставил бы первый бот, а после ривера делает ту же ставку, что и первый бот (по-типу считает, что уже заманил и теперь можно требовать по максимуму). Таким простым ботам явно ничего не светит против нормальных противников. Например, вот как я понимаю идеалогию один-на-один: - делаем рейз, если считаем нашу руку сильнее руки противника, иначе, если возможно, чек; - делаем колл, если считаем свою руку хуже, но все же пот-оддсы (с учетом того, что соперник в дальнейшем может вынудить еще коллировать его ставки) выгодные для колла; - сбрасываем, если пот-оддсы невыгодны Так играют мои два турнирных бота. В этой идеалогии не хватает только стратегии чек-рейза: если мы считаем, что соперник в большинстве случаев (если мы сделаем чек) сделает ставку, причем в большинстве из этих случаев наши карты окажутся сильнее карт соперника, то делаем чек, а на рейз отвечаем ререйзом. Пока структура ботов не позволяет сколь-нибудь приемлемо втолковать ему эту хитрость. Ах, да! Еще забыл про блеф :) Итак, если мы считаем, что на нашу ставку противник сбросит некоторые руки и их количество таково, что пот * вероятность_сброса - ставка > (пот + 2*ставка) * вероятность_выигрыша то блефуем. Для такой игры надо угадывать карты соперника и поэтому в моих ботах я это пока и не пытался делать. >>> НС на 8 гБ просто не успевала бы считать :) Там просто заранее просчитаны все возможные игровые ситуации для префлопа и флопа. Ривер и терн расчитываются на ходу. =========== На префлопе работают префлоп-таблицы. А дальше простая логика else if(handValue >= twoPairsHand) { if(gameInfo.pairCount == 2) { if(gameInfo.cicleBet == 0) { check(); } else { call(); } } else if(gameInfo.pairCount == 1) { if(gameInfo.cicleBet == 0) { bet(); } else { call(); } } else { if(gameInfo.cicleBet == 0) { bet(); } else { if(gameInfo.raiseCount > 0) { raise(); } else { call(); } } } } Статы не учитываются, действия принимаются исходя из текущих карт у игрока и карт на столе. --------- расмотрим хеадс-ап безлимит.. 1. два игрока. руководствуясь своими внутренними мыслями, делают действия в зависимости от игровой ситуации. (действия не обязательно однозначные в каждой игровой ситуации.. к примеру игрок думает типа а вот тут я в 30% скажу пас а в 50% райзану полбанка а в 20% проколю.. типа чтобы запутать соперника 2. Теперь самое главное.. вот есть некая игровая ситуация.. к примеру ривер .. мы знаем свои катры и игровую ситуацию (история торгов, карты на доске и тп) .. мы НЕ знаем две вещи 1. карты противника 2. что он с этими картами решит сделать в ответ на наш ход. Если бы мы знали это наверняка, то безусловно приняли бы оптимальное решение. мало того .. если бы мы знали это хоть примерно .. мы бы приняли тоже ОПТИМАЛЬНОЕ решение, основанное на нашей МОДЕЛИ СОПЕРНИКА. да да .. именно .. это слово прозвучало .. и это ключ к пониманию .. если у нас есть модель соперника то он повержен .. тупо математически высчитывается АНТИМОДЕЛЬ которая его в хвост и в гриву Вот тут то и становится понятным зачем при одной и той же игровой ситуации делать разные ходы (хотя ведь безусловно наше матожидание от хода для разных ходов не одинаковое и мы принимаем заведомо неоптимальное решение) .. да потому что покер - это игра "скрой или меняй свою стратегию, при этом не забывай выигрывать". и как показала практика - к хеадс-апу оч трудно применять статистику .. все предистории и оценка тактики игрока влияют оч сильно на принятие игровых решений --------- Рассмотрим упрощенный вариант: каждый игрок может до флопа либо пойти all-in, либо сбросить карты. Стек первого игрока X, стек второго игрока Y, малый блайнд s, большой, соотвественно 2*s, первым принимает решение первый игрок. Стратегия игрока в данном случае - диапазон (набор) сданных ему карманных карт (2 штуки), с которыми он принимает решение all-in. Я буду утверждать, что равновесия по Нэшу, а следовательно и оптимальных стратегий не существует. Одно дело - какие-то базовые стратегии, применяемые игроками на практике, другое дело - использование фундаментального подхода (теория игр) для получения математически выверенных оптимальных стратегий. Если говорить о боттинге, то, несомненно, с помощью АВС-стратегий боты могут бить невысокие лимиты, но здесь совсем другие подводные камни --- Берем все возможные стратегии первого игрока, и помещаем их по горизонтали, берем все возможные стратегии второго игрока, и помещаем по вертикали. Выводим в матрицу цену игры для каждого сочетания стратегий. Получаем матрицу матричной игры двух соперников... Вам доказать что число стратегий в Покере конечно? что такое стратегия? Стратегия - это таблица в которой для каждой игровой ситуации (сочетания карт на руках, пордок выкладки карт на стол, предшествующая торговля) проставлена конкретная заявка игрока. Раз число карт конечно, торговля ограничена - то и число игровых ситуаций ограничено. Раз число игровых ситуаций ограничено - значит и число стратегий ограничено. А вообще есть известная теорема теории игр о последовательных играх с неполной информацией с конечным числом шагов - о том что они сводятся к матричным играм. Однако я не представляю каким образом можно свести к матричной игре покер в общем виде, когда у нас 4 улицы торговли, и возможность принимать решение каждом игроком на каждой улице по несколько раз. Совершенно не исключаю, что это возможно теоретически, но совершенно не реализуемо на практике из-за невероятно большого количества вариантов развития событий. Практически - конечно матрицу не построить, она чудовищных размеров. Но ввиду того что покер ТЕОРЕТИЧЕСКИ сводится к матричным играм - существует его решение в смешанных стратегиях. А это говорит о том что нет никакой психологии в покере - есть алгоритм беспроигрышный (оптимальная смешанная стратегия), и например блеф в покере - это просто ненулевая вероятность хорошей заявки при плохой карте. И конечно же оптимальная стратегия не зависит от стратегии соперника. Но чтоб выигрывать больше у слабого соперника не нужно придерживаться оптимальной стратегии, а нужно использовать недостатки стратегии СЛАБОГО соперника Насколько я знаю, оптимальная стратегия - рассчет на совершенную игру оппонента - т.е. что он не допускает ошибок. Если игрок допускает ошибки, то оптимальная стратегия в строгом определении, не действует оптимально, а действительно оптимальной будет та, которая использует эти слабости оппонента, она рассчитывается значительно сложнее. В покере не делать ошибок - значит прослеживать дерево игры (совершенной игры) до самых листьев, что в реальном времени невозможно. Но возможен предрасчет, и то, на основе редуцированного дерева (что исследователи по ИИ в покере называют абстракцией (abstraction)), который занимает не один день на современной персоналке. Реализованная таким образом стратегия также не оптимальна, т.к. дерево абстрагировано, и она называется псевдо-оптимальной. Проблемы: насколько использование абстракции искажает оптимальную стратегию. Кроме того - таким образом полученная стратегия является статической. Возьмём к примеру игру камень ножницы бумага... это игра? о да... имеет ли она оптимальную стратегию - безусловно.. оптимальная стратегия сдесь это смешаная стратегия 33% камень 33% ножницы 33% бумага несмотря на всю свою оптимальность с математической точки зрения - выигрыш будет = 0. притом независимо от стратегии противника. Теперь усложним игру.. добавим рейк 3% с каждой игры :) И наша прошлая оптимальная стратегия превращается в убыточную! А единственная оптимальная стратегия - не играть! Теперь ещё усложним задачу... я к примеру подметил что определённые игроки в силу своих какихто религиозных убеждений предпочитают реже использовать ножницы в игре. Подметив этот факт я могу написать бота используешего эту информацию. к примеру 50% камень 30% ножницы 20% бумага. Он будет выигшрышный несмотря на всю свою математическую неоптимальность. И я буду его использовать втихаря до тех пор пока он будет работать (пока те которым вера не позволяла использовать ножницы либо не разорятся либо не изменят стратегнию). -------- Задача 1. Есть массив ячеек, каждая из которых соответствует одной паре карт из всех возможных двухкарточных комбинаций, т.о. это массив из 1326 ячеек (колода стандартная). В этих ячейках хранятся вероятности того, что соответствующая пара карт сейчас у оппонента. После каждого игрового хода оппонента я произвожу обновление вероятностей карт (вероятностный вывод). Упрощенно это обновление выглядит как P[j] = P[j] * kj, где P[j] - ячейка вероятности, kj - множитель-коэффициент обновления для данной пары карт. Т.о. нужно для обновления получить доступ к каждой ячейке. Причем какие-то из ячеек станут =0, в зависимости от текущих deadCards (карты у меня в руке и карты борда), которые сокращают число возможных комбинаций оппонента. Задача 2. Вычислить силу руки, но инкрементируя переменные behind, tied, ahead не единицами, а вероятностями карт (как описано в The challenge of poker), т.е. значениями того самого массива P, который мы вычислили на предыдущем этапе. =========== >>>> В корне неправильное видение игры... это в шахматах так... в покере же нет никаких деревьев.. есть стратегии. в том же "камень ножницы бумага" о каком дереве идёт речь? покер всего лишь чуть сложнее. Речь идет о дереве с неполной информацией, которое наравне с узлами MIN, MAX (как в шахматах) содержит узлы жеребьевки или узлы шанса. Узлы шанса представляют вероятностные исходы в игре с неполной информацией. Что в покере мы отнесем к неполной информации (подводя покер под математическую модель): 1 скрытые карты оппонентов 2 выбор действий оппонентом или, по-иному, результат разворачивания торговли оппонентами после того, как мы выбрали свое действие 3 следующие карты которые придут на борд Последние два пункта не относятся к настоящему, а к будущему, поэтому это можно считать неполной информацией другого характера. К неполной информации конкретно можно отнести только неполноту информации о текущем состоянии, т.е. скрытые карты оппонентов Для техасского холд'ема с 2мя игроками узлами шанса будут: 1: узел раздачи карт 2: каждый второй полуход (оппонента), где идет ярус узлов, в которых оппонент принимает решение: мы не знаем что выпадет у оппонента, в первую очередь из-за неизвестности его карт, а он еще может блефовать... 3: узел выбрасывания новых общих карт на борд - идет после ярусов узлов торговли -- корневой узел шанса почкуется на все возможные сочетания наших карт и карт оппонента (или наоборот) на префлопе: С(52,2)*C(50,2)=1624350; -- узлы шанса выброса флопа - на все возможные сочетания карт флопа C(48,3)=17296; -- узлы шанса выброса терна - все возможные карты терна C(45,1)=45; -- узлы шанса выброса ривера - все возможные оставшиеся карты ривера C(44,1)=44. Эти цифирки собственно - коэффициент ветвления узлов шанса (не слабый). Узлы Min, Max называются узлами принятия решения. Т.о., как оценивают в The challenge of poker, дерево игры в техасском холд'еме составляет 10^18 узлов (!). В случае 2х игроков узел описывается полезностью в виде числа (т.к. полезности в игре с 0й суммой противоположны). Когда игроков больше 2х, описывается вектором полезности как и в обычном дереве. По-сути, в покере все узлы, кроме узлов, в которых мы принимаем решение, являются узлами шанса. Это и есть состояния игры. Мы переходим из состояния в состояние, принимая решение. Приняв решение, происходит цепь событий: мы двигаемся в дереве и проходим узлы принятия решения оппонентами, на пути встречаем узлы выпадения карт борда... Все это ведет к новому состоянию, в котором мы снова должны принимать решение, если игра еще не закончилась... Если же она закончилась - мы в терминальном состоянии. Терминальное состояние в дереве покера - это вскрытие, здесь мы получаем значение полезности, соответствующую всей последовательности выполненных нами действий за игру. Обычно в теории игр для этого используется функция полезности. В покере только в терминальном состоянии мы узнаем окончательную полезность, в остальных состояниях поступают наблюдаемые вознаграждения. Это и затрудняет прямое применение алгоритмов контролируемого обучения (обучения с учителем) для аппроксимации функции полезностей - слишком мало примеров и они слишком поздно поступают... В общем случае, главные отличия дерева с неполной информацией от детерминированного дерева: 1 -- Переход в дереве описывается моделью перехода и восприятия. Мы можем "двигаться в один узел, а попасть совсем в другой" (в общем случае) - это как в примере из Норвига и Рассела в разделе о задачах с последовательным принятием решений: среда 4x3 с таблицей вероятностей перехода из квадрата в квадрат: выполняемое из начального квадрата (1,1) действие Up перемещает агента в квадрат (1,2) с вероятностью 0.8, но с вероятностью 0.1 он движется вправо, в квадрат (2,1), а с вероятностью 0.1 - влево, ударяется о стену и остается в квадрате (1,1). В теории принятия решения такая среда описывается марковским процессом принятия решения (Markov Decision Process - MDP). 2 -- мы никогда точно не знаем в каком узле принятия решения мы находимся в данный момент, чтобы принять соответствующее решение (фолд, колл, рейз). В итоге для описания процесса принятия решения в покере наиболее подходит модель марковского процесса принятия решения в частично наблюдаемой среде (Partially Observable MDP) [Норвиг-Рассел, С.831-839]. Она в добавок к модели перехода MDP добавляет доверительные состояния и модель восприятия. Каждый переход из состояния i в состояние j происходит с некоторой вероятностью p(i,a,j) от предпринятого действия 'a' и сопровождается наблюдаемым новым состоянием j, наблюдаемым вознаграждением (или мгновенной стоимостью перехода) g(i,a,j) и результатами восприятия e(i,a). Неполное знание о текущем состоянии имеет математическую модель доверительного состояния - абстракции над всеми возможными состояниями среды, представляющей собой точку в пространстве вероятностей по всем состояниям. Функция доверительного состояния представляет собой закон распределения вероятностей по всем возможным состояниям среды. В среде, описываемой с помощью доверительного состояния не рассматриваются конкретные состояния. Каждое состояние может быть и тем и другим и третим..., с некоторой вероятностью. Нам неизвестно текущее состояние среды, а известна его абстракция. Оптимальная стратегия, как и в играх с полной информацией опирается на совершенную игру оппонента. Это по-сути тот же минимаксный алгоритм только в условиях неполной информации, - таким же образом производится резервирование значений полезности действий от некоторого горизонта к корню, но при этом полезность узлов жеребьевки усреняется по всем возможным результирующим состояниям. Это называется "усреднение по прогнозам". Т.о. полезости состояний здесь не детерминированные а ожидаемые. Т.к. это минимаксный алгоритм, для него применим альфа-бета отсечения, но при условии, что мы можем определить пределы функции полезности в каждом узле шанса [Норвиг-Рассел, С.261,262]. Характерные минимаксные алгоритмы для MDP - алгоритм итерации по значениям и итерации по стратегиям. Однако для покера они не годятся в исходном виде, т.к. требуют по уравнению для каждого состояния + состояний, как мы помним, нет в покере - есть их абстракции, и пространство этих состояний непрерывно (вероятности), в отличие от дискретного пространства S физических состояний. Притом для итерации по значениям эта система уравнений нелинейная, что не позволяет применить обычные методы линейной алгебры. Но конечно не должно значить, что описание покера в такой модели бессмысленно, т.к. существуют хитрости, которые опытным исследователям известны и они обходят все перечисленные проблемы, модифицируя стандартные методы. Что касается смешанной стратегии в покере, это отдельная тема, но смешанная стратегия также может находиться по этому же дереву, рассматривая последовательности ответов оппонентов на наше каждое действие с присвоенной ему вероятностью и результат каждой этой последовательности - вероятностыный результат (гиперплоскость в пространстве вероятностей наших действий). Но для этого сначала применяют методы абстракции дерева игры, чтобы задача линейного программирования оказывалась приемлемой по размеру. При этом задача ЛП становится разрешимой даже в реальном времени. Т.е. компьютерный игрок получает аппроксимацию оптимальной смешанной стратегии на ходу, применяя ее как базовую линию поведения. Все это конечно формальное описание. Игра представляется в виде дерева, подводится под обкатанные модели, чтобы можно было применять известные методы и подходы, накопленные из таких игр как шахматы и т.д. --- В "камень ножницы бумага" я так понимаю игра 33/33/33 рассчитана, что все оппоненты играют 33/33/33 - тогда дается гарантия что мы не проиграем вообще. Если же оппоненты отклоняются от 33/33/33 мы еще и выиграем note: Здесь оговорился. На самом деле действительно, независимо от того, что оппонент будет делать, оптимальная смешанная стратегия не даст нам ни плюса ни минуса, т.к. мы находимся в этом случае в равновесии - точке пересечения плоскостей результатов, которая составляет для нас нулевую полезность. При этом какую бы стратегию (плоскость результатов) ни выбрал оппонент, результат не будет для нас больше нуля Дерево здесь такое: уровень 1: корень - узел принятия решения, в котором мы можем сделать выбор из трех вариантов; уровень 2: по узлу шанса для каждого решения: для "камень", для "ножницы" и для "бумага"; уровень 3: узлы шанса разветвляются на 3 действия оппонента Если оппонентов больше одного, то на возможные сочетания действий оппонентов, которых будет 3^n_players, то есть как размещений с учетом повторения элементов. На данном уровне имеем 3^n_players узлов принятия решения; уровень 4: узлы принятия решения разветвляются по 3 узла шанса на каждый узел ... Такое дерево бесконечно, если мы не рассматриваем условий достижения терминального состояния --- На ривере 2 809 475 760 возможных комбинаций карт, которые мы можем увидеть у себя и на столе. А еще для каждой из этих комбинаций нужно учесть всю торговлю начиная от префлопа. Чтобы записать настоящую оптимальную стратегию просто не хватит емкости жестких дисков. Имеет смысл искать только сильно упрощенные стратегии. Насколько они будут близко к оптимальной зависит от того, как удачно мы сделаем упрощения. Поэтому прежде чем говорить о нахождении оптимальной стратегии нужно пояснить какие именно упрощения игры ты предлагаешь. > Так и не понял, как ты предлагаешь строить стратегию антибота. Путем случайного > изменения параметров текущей лучшей стратегии? Тогда какие гарантии, что не > попадешь в точку локального экстремума, когда любое малое изменение стратегии > дает результат хуже, но стратегия не является глобально оптимальной? Это два совершенно разных действия "нахождение несмешаного антибота" и "нахождение оптимальной стратегии" Про локальные оптимальные стратегии - думаю это не грозит системе нахождения описанной выше. Так как мы НЕ "случайно меняем параметры", а находим НЕСМЕШАНОГО антибота. Что означает его несмешаность? Зачем вообще нам искать смешаную оптимальную стратегию? Ведь казалось бы - какую бы модель мира мы не использовали ВСЕГДА при выборе хода мы оцениваем матожидание прибыли. И логично выбрать ход с максимальным матожиданием! НО НЕТ! Мы жертвуем сиюминутной прибылью! Осознанно! И ходим СЛУЧАЙНО с заданным распределением. ЗАЧЕМ? А затем, что мы ШИФРУЕМСЯ таким образом от противника и не даём ему возможность под нас подстроится. Так вот - когда мы высчитываем несмешаного антибота против нашего смешаного ... У несмешаного ОГРОМНАЯ фора: ему не надо шифроватся - никто не собирается под него подстраиватся. Это как два гладиатора - один с шитом, а другой - с мечём :) Мы совершенствуем щит до тех пор, пока он станет неуязвим против ЛЮБОГО меча! Тут нет места локальным экстремумам ============================================================= Некоторые карточные комбинации эквивалентны между собой, так, например, на префлопе стартовая комбинация из двух тузов A♥A♦ идентична любой другой паре тузов. Таким образом, рассматривая первый круг торговли, на котором у игроков всего по две карты на руках, мы можем выделить всего 169 различных неэквивалентных двухкарточных комбинации. В качестве примера построения абстракции в покере, рассмотрим префлоп, где лучшими стартовыми комбинациями являются пара тузов AA и одномастные сочетания туза с королем AKs. Совершенно очевидно, что стратегии розыгрыша этих карт на первом круге торговли несильно будут отличаться друг от друга, поэтому можно без зазрения совести объединить эти две пары карт в один абстрактный класс стартовых комбинаций. Указанный трюк можно повторить и для других карточных комбинаций, получив в итоге «абстрактный» покер с меньшими запросами к вычислительным ресурсам. Существуют и другие способы построения абстракций, которые применяются не только к карточным комбинациям, но и к паттернам ставок в безлимитных дисциплинах, что позволяет в итоге получить абстрактную игру, поддающуюся исследованию и анализу. Самые первые покерные агенты являлись экспертными системами (наборами правил if-then для различных игровых ситуаций). Правила получались с помощью анализа большого количества реальных раздач. Недостаток такого подхода очевиден — искусственный оппонент легко эксплуатируется. Основная концепция покерных ботов-эксплуататоров основана на построении контр-агентов к известным оппонентам, использующих их слабые места. Используются либо моделирование поведения оппонента в той или иной игровой ситуации, либо заранее посчитанные стратегические профили под определенного оппонента. Принятие решения в системах с моделированием оппонента сводится к многократному проигрыванию «виртуальных» раздач с различными исходами для определения наиболее выгодного решения. Адекватность таких покерных агентов обусловлена адекватностью модели оппонента. В академических кругах известны эксплуататоры основанные на нахождении стратегий наилучшего отклика. Идея данного подхода заключена в том, что в рамках заданной абстракции подбирается контр-стратегия к известному оппоненту. Это позволяет эксплуатировать слабости конкретного оппонента максимально эффективно, но против произвольного соперника результаты могут оказаться плачевными. Для любого бота-эксплуататора существует один недостаток. Он заключен в необходимости реализации начальной стратегии, так как очевидно, что невозможно смоделировать и эксплуатировать оппонента после десятка раздач. Более того, речь идет о десятках тысяч раздач с конкретным противником, прежде чем эксплуатация начнет приносить свои плоды. При игре с людьми на начальном этапе часто используется подход, при котором эксплуататор заранее обучается по истории раздач против группы игроков, объединенных общими характеристиками, такими как частота захода в игру, желание идти на вскрытие и так далее. Затем, по мере наигрывания раздач, характеристики человека-оппонента проясняются все сильнее и сильнее, что позволяет эксплуататору быстрее и адекватнее подобрать и дообучить соответствующую контр-стратегию. До недавнего времени теоретико-игровые методы построения покерных ботов относились к области академического интереса, поскольку их осуществление было сопряжено с вычислительными сложностями. Речь прежде всего идет о поиске равновесий Нэша. Остановимся подробнее на концепции равновесных стратегий и рассмотрим всем известную игру "Камень - Ножницы - Бумага". Пусть первый игрок следуя чистой стратегии играет все время "камень". Тогда второй игрок, раскусивший игру первого, для получения максимального профита будет играть все время "бумагу". Первому игроку, чтобы не проигрывать да еще и максимизировать свои выигрыш, нужно переключиться на "ножницы". Очевидно, что второй игрок изменит свою стратегию на "камень" и весь цикл смены стратегий повторится вновь. Как легко заметить, если бы первый игрок выкидывал "камень", "ножницы" или "бумагу" равновероятно, то второй игрок, как бы ни старался изменить свою стратегию, не смог бы эксплуатировать противника. Вышеописанный пример можно рассматривать как игру двух эксплуататоров, постоянно подгоняющих свою смешанную стратегию под оппонента. Очевидно, что рано или поздно стратегии обоих игроков сойдутся к равновероятному выкидыванию "камня", "ножниц" или "бумаги". Такая ситуация получила названия равновесия Нэша - находясь в равновесии ни один из игроков не может сыграть лучше другого, изменив свою стратегию. Нечего и думать о поиске равновесных стратегий для покера — мы не располагаем ресурсами, достаточным для решения игр такого размера. Поэтому исследователи сосредоточились на равновесных стратегиях абстракций, но тут возникает вопрос, связанный с тем, насколько адекватно равновесие абстрактной игры соответствует равновесию полной игры. Равновесия Нэша абстрактной игры принято называть приближенными равновесиями или ε-равновесиями, где ε символизирует потенциальную эксплуатируемость стратегии. У ε-равновесий главной характеристикой выступает их эффективность при игре с супер-эксплуататором. Или, другими словами, насколько далеко от истинного равновесия исходной игры располагается найденное ε-равновесие. Вполне ожидаемо, что использование грубых или неадекватных абстракций дает грубое и неадекватное ε-равновесие. Также вполне ожидаемо, что чем меньше информации о полной игре теряется при переходе к абстракции, тем больше код абстракции и тем выше требования к алгоритмам обучения и вычислительным ресурсам. На сегодняшний день существует несколько эффективных алгоритмов поиска равновесия в больших абстракциях покера. Самыми популярными из них являются алгоритмы, основанные на "минимизации сожаления". Главным преимуществом равновесных стратегий является их неэксплуатируемость. В то же время, эти стратегии не особо прибыльны даже против очень слабых игроков. Для покера можно выделить два класса проблем, решаемых переходом к абстракции: Проблема ставок в безлимитных и пот-лимитных играх. Проблема представления рук игроков. Одним из наиболее популярных приемов тут является дискретизация ставок, когда диапазоны ставок разбиваются на интервалы и все манипуляции в дальнейшем осуществляются со ставками, привязанным к диапазонам. Рассмотрим вариант дискретизации для безлимитного покера, где ставки определены следующим образом: h — половина банка, p — ставка в банк, d — удвоенный банк, a — ва-банк. Все остальные ставки будем соотносить к ближайшим из вышеприведенных. На первый взгляд такая абстракция ставок вполне адекватна и, действительно, боты использующие ее демонстрируют хороший уровень игры с оппонентами, придерживающимся той же схемы ставок. Но если противник вдруг совершит произвольную ставку, сильно отличающуюся от дискретной, то неадекватное поведение бота нам обеспечено. Самая очевидная потенциальная уязвимость это совершение оппонентом минимально допустимой ставки, после чего бот может просто сбросить карты. Кроме того, ставки на границах между дискретными диапазонами приводят к принятию ботом «переоцененных» или «недооцененных» решений, что чревато проигрышем денег на дистанции и раскрытием бота человеком. Добавление большего количества дискретных диапазонов не спасает ситуацию, так как увеличивается количество абстрактных игровых состояний и приходится снижать качество абстрактного представления карт. Вышеописанная схема перевода ставок реальной игры в абстрактную получила название «жесткой». Существует и другой, «мягкий» способ транслирования ставок, суть которого заключается в том, что соотнесение реальной ставки к той или иной абстрактной определяется случайным образом, причем, чем ближе реальная ставка к абстрактной, тем вероятность соотнесения выше. При построении покерного агента, ориентированного на игру с людьми, зачастую дискретизация ставок осуществляется путем анализа реальных игровых раздач, после чего каждому игровому состоянию приписывается своя схема дискретизации ставок. Тем не менее, этот подход по прежнему уязвим после выявления схемы построения абстракции. Дискретизация не является единственным способом представления ставок в покере. Возможен также и подход, который заключен в том, что бот стремится дополнить банк на строго определенную сумму, чтобы привести банк в соответствие со значением, заложенным в абстракции. Очевидно, что такой трюк сразу разоблачает бота и при этом не лишен серьезных недостатков - например, незначительным превышением заложенного размера банка можно вынудить бота сбросить карты. В последнее время получил развитие способ построения абстракций ставок в покере, при котором вводится еще один игровой агент, принимающий решение о том, какую ставку нужно совершить в конкретной игровой ситуации. Вместо всех возможных ставок в каждом игровом состоянии игрокам разрешается совершать лишь три: минимальную ставку (L), максимальную ставку (H) или же уйти в ва-банк. При этом решение о том, какую из двух ставок L или H нужно сделать принимает новый игровой агент. Очевидным преимуществом такого подхода является сложность эксплуатации бота размерами ставок, но при этом появляются вопросы, связанные с обучением нововведенного агента, нахождением конкретных значений для ставок L и H, организацией представления абстракции в целом. Иногда встречаются абстракции, в которых ограничено количество ставок на кругах торговли или же полностью отсутствует история о предыдущих кругах. Но на сегодняшний день такие подходы особой популярностью не пользуются, так как они порождают слабых покерных агентов. Напомним, что эквивалентными комбинациями называются такие наборы карточных комбинаций, в которых заменой мастей можно получить любую другую комбинацию из этого же набора. Совершенно очевидно, что эквивалентные комбинации содержат в себе одну и ту же стратегическую информацию. В качестве примера эквивалентного класса можно привести комбинацию AKx|TxJy8x2x на третьем кругу торговли, где AKx — карманные карты игрока, TxJy8x2x — карты на доске в порядке их появления, а x и y (x!=y, разумеется) могут принимать одно из четырех значений мастей {c,s,h,d}. Таким образом, класс AKx|TxJy8x2x содержит в себе 12 абсолютно одинаковых комбинаций {AKc|TcJs8c2c, AKs|TsJc8s2s, ...}. Условимся называть комбинацию, записанную в виде AKx|TxJy8x2x, "рукой игрока", хотя это и идет в разрез с общепринятым определением. Тем не менее, термин "рука" как нельзя точно подходит к определению частной информации о картах, полностью доступную только одному игроку. Даже после приведения рук к эквивалентным классам их слишком много для непосредственного рассмотрения, поэтому необходимо прибегнуть к группировке этих эквивалентных классов в новые, абстрактные классы. Разумеется, теперь эта процедура будет происходить с некоторой потерей информации об исходных эквивалентных классах. Для примера такой группировки рук, рассмотрим префлоп, поскольку на нем всего 169 неэквивалентных карманных пар. Уже начиная с флопа анализ "в лоб" карточных комбинаций является весьма утомительным и неблагодарным делом, поэтому приходится прибегать к другим способам анализа и разбиения рук на классы. Речь пойдет о числовых характеристиках карточных комбинаций, которые позволяют осуществить группировку карточных комбинаций по выделенным свойствам. Одной из самых первых использованных характеристик стал ранг руки (Hand Strength, HS), который определяется как доля всех возможных готовых комбинаций у оппонента, уступающих по силе готовой комбинации у игрока. Например, HS(AKx|TxJy8x2x)=1.0 так как имеется уже готовый старший флеш, а вот HS(AxKy|TzJx82z)~0.43, что неудивительно, так как любая пара карт с мастью z у оппонента дает ему преимущество. Первое, что бросается в глаза при анализе HS, это то, что этот показатель ничего не говорит о том, как поведет себя рука игрока на последующих кругах торговли, не говоря уже о том, что и предыстория раздачи никак не отражается на значении этого показателя. С целью восполнить недостающую информацию, начиная с флопа, вводятся еще два показателя PPot (Positive Potential) и NPot (Negative Potential). Первый из этих показателей PPot представляет собой вероятность того, что текущая рука игрока улучшится после появления новой карты на столе. Под улучшением подразумевается, что у оппонента не появится новых готовых комбинаций, которые окажутся старше готовой комбинации игрока. Аналогично и Npot — вероятность того, что рука игрока ухудшится с новой картой на столе. Для рассмотренного выше примера PPot(AKx|TxJy8x2x)=0 и NPot(AKx|TxJy8x2x)~0, что неудивительно, так как оппоненту приходится рассчитывать только на фулл-хаус при наличии у него готовых двух пар. Для второго случая PPot(AxKy|TzJx8z2z)~0.15 и NPot(AxKy|TzJx8z2z)~0.22 и мы видим, что NPot достаточно высок за счет того, что оппонент может добрать недостающую карту с мастью х, чтобы собрать флэш. Квадрат ожидаемого ранга руки E[HS2] позволяет получить более качественную абстракцию, поскольку такой показатель дает больше преимуществ сильным рукам игрока и разграничивает их с руками, у которых высокий потенциал (PPot). Логично, ведь руки с одинаковым рангом, но с разным потенциалом, должны стратегически по разному разыгрываться. Три показателя {HS, PPot, NPot} позволяют точнее охарактеризовать руку игрока, но их одновременное рассмотрение может оказаться несколько неудобным делом, поэтом часто прибегают к вычислению эффективного ранга руки (effective hand strength, EHS), который задается по одной из двух формул: EHS1 = HS × (1 − NPot) + (1 − HS) * PPot или EHS2 = HS + (1 − HS) * PPot Разница между двумя формулами заключается в том, что EHS2 дает более «оптимистичный» прогноз, что бывает необходимо для проработки более агрессивной игры. Еще один показатель - это шанс выигрыша руки или ожидаемый ранг руки на вскрытии (expected hand strength, E[HS]). Как ясно из названия этой характеристики, она представляет собой долю случаев, когда доиграв до вскрытия текущий игрок получит комбинацию старше возможной комбинации оппонента. Плюс этого показателя заключен в том, что он уже включает в себя информацию о динамике руки на всех последующих кругах торговли вплоть до вскрытия, в отличие от NPot и PPot, прогнозирующих лишь на одну улицу вперед. В примерах выше, против случайной руки оппонента эти показатели имеют следующие значения: E[HS](AKx|TxJy82x)~0.99 и E[HS](AxKy|TzJx82z)~0.42 У всех вышеописанных показателей есть ряд общих недостатков. Первый из них, самый заметный для опытных игроков в покер, заключен в том, что эти показатели рассчитываются при предположении наличия любой руки у оппонента. Смысл в том, что в покере очень малый процент рук доходит до вскрытия, что означает, что при расчете HS, PPot, NPot или E[HS] мы ставим оппоненту все возможные карманные карты, которых у него может никогда и не быть. Это в свою очередь делает оценки таких показателей непредсказуемо завышенными. Чтобы как-то исправить этот недостаток, часто применяют взвешивание рук, возможных у оппонента в зависимости от игровой ситуации. В частности, такое взвешивание может быть осуществлено с помощью моделирования оппонента. Второй недостаток кроется в том, что применение этих показателей все же приводит к потере информации о руке игрока, хотя это не так очевидно. В первую очередь это касается «памяти» руки, а именно информации о том, в какой последовательности карты появлялись на доске. Например, очевидно, что HS(AKx|QJT5x)=HS(AKx|JT5Qx), где в первом случае у игрока на флопе уже была готовая комбинация стрит-флеша, а во втором — нет. Более того, зачастую руки, требующие разных стратегических решений, имеют близкие между собой значения числовых показателей, что может привести (и приводит) к ошибкам при обучению машины игре в покер. Дальнейшие манипуляции с числовыми показателями с целью построения абстракции, сводятся к группировке различных категорий рук в классы по определенным значениям этих показателей. Например, если мы желаем иметь всего пять классов на каждом кругу торговли, то мы можем сгруппировать все руки по следующим значениям E[HS]: [0.0 – 0.2] [0.2 – 0.4] [0.4 – 0.6] [0.6 – 0.8] [0.8 – 1.0] Теперь абстрактная игра в покер будет манипулировать пятью классами рук, а не исходными карточными комбинациями. Для того, чтобы такая абстрактная игра была полной, необходимо также рассчитать вероятности перехода между классами на смене кругов торговли и задать матрицу выплат, в которых указана «прибыльность» одного абстрактного класса рук по отношению к другому. Но этот пример разбиения рук по классам с помощью пяти предопределенных интервалов значений E[HS] имеет один недостаток — руки по классам распределяются неравномерно — в какой то диапазон значений E[HS] попадет всего пара рук, а в остальных будет перенаселение. Чтобы исправить эту ситуацию, используют разные приемы: от ручного редактирования классов, вычисления квантилей, разбиения на вложенные классы, k-means и другие. ================================================================== В академической литературе обычно не используют понятие эквити (т.к. все-таки по определению оно зависит от размера банка), а вместо этого используют понятие силы руки HS (Hand Strength). Далее я тоже не буду использовать понятие эквити – для меня привычнее показатель HS Сила HS руки показывает вероятность нашей победы в текущем раунде. HS на ривере – это то же самое, что большинство из нас понимают под шоудаун эквити. Следующая функция вычисляет HS для заданных карманных карт c1c2, карт стола board и взвешенного спектра карт оппонента wt: double CalcHandStrength(Hand const &board, Card c1, Card c2, WeightTable const &wt) { int i, j; int opprank; double wins = 0, ties = 0, loses = 0; Card o1, o2; double weight; char impossible[Card::NUM_CARDS]; int ourrank = Evaluate(board, c1, c2); memset(impossible, 0, sizeof(impossible)); impossible[c1.GetIndex()] = 1; impossible[c2.GetIndex()] = 1; for (i = 1; i <= board.GetSize(); i++) impossible[board.GetCard(i).GetIndex()] = true; for (i = 0; i < Card::NUM_CARDS; i++) for (j = i + 1; j < Card::NUM_CARDS; j++) { if (impossible[i] || impossible[j]) continue; o1.SetIndex(i); o2.SetIndex(j); opprank = Evaluate(board, o1, o2); weight = wt.GetHandWeight(o1, o2); if (ourrank >= opprank) wins += weight; else if (ourrank == opprank) ties += weight; else // ourrank < opprank loses += weight; } return (wins + ties / 2) / (wins + loses + ties); } Как видно из кода, вычисление заключается в переборе всех карт оппонента и сравнения очередной получившейся у него руки с учетом карт стола с нашей текущей рукой. вычисленное значение HS показывает наши шансы на победу в текущем раунде, т.е. без учета тех карт, которые могут выйти на стол, поэтому UofA вводит еще одно понятие – EHS (Effective Hand Strength) – эффективную силу руки, которая вычисляется по формуле: EHS = HS * (1 – NPot) + (1 – HS) * PPot. EHS уже учитывает те карты, которые могут выйти на стол (одну или две в зависимости от раунда) за счет того, что в нее «замешаны» показатели NPot и PPot – соответственно, отрицательный и положительный потенциалы нашей руки. Эти показатели для заданных карманных карт c1c2, карт стола board и взвешенного спектра оппонента wt вычисляет следующая функция: void CalcHandPotential(Hand const &board, Potential &pot, Card c1, Card c2, bool fullLookahead, WeightTable const &wt) { enum { AHEAD, TIED, BEHIND }; double HP[3][3]; double HPTotal[3]; Hand comm = board; int i, j, k, l, index; double weight; for (i = 0 ; i < 3; i++) { for (j = 0; j < 3; j++) HP[i][j] = 0; HPTotal[i] = 0; } bool impossible[Card::NUM_CARDS]; memset(impossible, 0, sizeof(impossible)); impossible[c1.GetIndex()] = 1; impossible[c2.GetIndex()] = 1; for (i = 1; i <= comm.GetSize(); i++) impossible[comm.GetCard(i).GetIndex()] = 1; int ourrank5 = Evaluate(comm, c1, c2); int opprank, ourrank7; Card o1, o2; ASSERT(comm.GetSize() == 3 || comm.GetSize() == 4); fullLookahead = (comm.GetSize() == 3 && fullLookahead); for (i = 0; i < Card::NUM_CARDS; i++) for (j = i + 1; j < Card::NUM_CARDS; j++) { if (impossible[i] || impossible[j]) continue; impossible[i] = 1; impossible[j] = 1; o1.SetIndex(i); o2.SetIndex(j); opprank = Evaluate(comm, o1, o2); if (ourrank5 > opprank) index = AHEAD; else if (ourrank5 == opprank) index = TIED; else index = BEHIND; weight = wt.GetHandWeight(o1, o2); HPTotal[index] += weight; for (k = 0; k < Card::NUM_CARDS; k++) { if (impossible[k]) continue; impossible[k] = 1; comm.AddCard(k); if (fullLookahead) { for (l = k + 1; l < Card::NUM_CARDS; l++) { if (impossible[l]) continue; comm.AddCard(l); ourrank7 = Evaluate(comm, c1, c2); opprank = Evaluate(comm, o1, o2); if (ourrank7 > opprank) HP[index][AHEAD] += weight; else if (ourrank7 == opprank) HP[index][TIED] += weight; else HP[index][BEHIND] += weight; comm.RemoveCard(); } } else { ourrank7 = Evaluate(comm, c1, c2); opprank = Evaluate(comm, o1, o2); if (ourrank7 > opprank) HP[index][AHEAD] += weight; else if (ourrank7 == opprank) HP[index][TIED] += weight; else HP[index][BEHIND] += weight; } comm.RemoveCard(); impossible[k] = 0; } impossible[o1.GetIndex()] = 0; impossible[o2.GetIndex()] = 0; } int mult = (fullLookahead ? 990 : (Card::NUM_CARDS - 3 - 2 * 2)); double den1 = mult * (HPTotal[BEHIND] + (double)HPTotal[TIED] / 2); double den2 = mult * (HPTotal[AHEAD] + (double)HPTotal[TIED] / 2); if (den1 > 0) pot.ppot = (HP[BEHIND][AHEAD] + (double)HP[BEHIND][TIED] / 2 + (double)HP[TIED][AHEAD] / 2) / den1; else pot.ppot = 0; if (den2 > 0) pot.npot = (HP[AHEAD][BEHIND] + (double)HP[AHEAD][TIED] / 2 + (double)HP[TIED][BEHIND] / 2) / den2; else pot.npot = 0; } Положительный потенциал PPot показывает шансы на то, что мы, находясь в текущем раунде позади, в следующем раунде (или к шоудану, если fullLookahead == true) улучшимся и победим. Отрицательный потенциал NPot показывает шансы на то, что мы, находясь в текущем раунде впереди, в следующем раунде (или к шоудауну) станем хуже и проиграем. Другими словами PPot – это вероятность того, что «переедем» мы, а NPot – это вероятность того, что «переедут» нас. Когда показатели HS, PPot и NPot вычислены, мы можем вычислить эффективную силу руки EHS. EHS – это и есть то значение, которое вычисляет любой эквилятор. Спрашивается, для чего все это делать через… PPot и NPot? Нельзя ли просто перебрать все карты оппа и «прогнать» их до шоудауна? Можно! Именно так и делает эквилятор. Однако при этом теряется очень много полезной информации. PPot, например, показывает силу нашего дро. Если PPot >= potOdds, то мы должны колировать по шансам банка. Кроме того, оценивая PPot, можно выбирать разные стратегии розыгрыша. Так, если PPot >= 0.2, то мы знаем, что у нас какое-то сильное дро, мы уже можем делать бет, чек-рейз и пр. Нам не нужно «вычислять гатшот», вычислять комбинированные дро и т.п. Вместо этого у нас появляется соответствующая математическая характеристика, что очень важно. NPot, наоборот, показывает слабость нашей готовой руки, обычно NPot исключают при вычислении EHS. Т.е. EHS = HS + (1 – HS) * PPot. Такой шаг по заверениям UofA поощряет агрессивную игру вообще и, в частности, при защите готовой руки. Я использую NPot для анализа слабости готовой руки. Это позволяет выделить руки с приличным EHS, но являющиеся при этом чрезвычайно уязвимыми. Такие руки нужно выкидывать на флопе. И, благодаря показателю NPot, бот очень просто делает сложные фолды :) и вообще начинает шикарно играть флоп в стиле Склански (либо рейз, либо пас). Взвешенный спектр в функции CalcHandStrength и CalcHandPotential передается через таблицу весов wt, в которой каждой из 1326 возможных рук оппонента поставлен в соответствие вес - вероятность того, что оппонент разыграл бы эту руку так, как мы увидели. Т.е. сумма весов в таблице НЕ равна 1. Напротив, вес каждой руки - это число от 0 до 1. Еще один момент, касающийся вычислений против нескольких оппонентов. UofA делает просто - они вычисляют HS (не EHS!) против каждого оппа (используя для каждого оппа его таблицу весов), перемножают полученные значения, получая HSn. При этом утверждается, что такое перемножение допустимо, т.к. вносит небольшую погрешность. Затем формируют единую таблицу весов всех оппов, которая называется field array и по этой единой таблице вычисляют PPot и NPot, передавая ее в функцию CalcHandPotential в качестве аргумента wt. Затем вычисляют EHSn = HSn + (1 – HSn) * PPot Небольшая неточность. Следующий фрагмент функции CalcHandStrength if (ourrank >= opprank) wins += weight; else if (ourrank == opprank) ties += weight; else // ourrank < opprank loses += weight; должен выглядеть так: if (ourrank > opprank) wins += weight; else if (ourrank == opprank) ties += weight; else // ourrank < opprank loses += weight; Просто у меня ничья трактуется в мою пользу, а у ребят из UofA считается отдельно ну так ничья и должна считаться отдельно ничья это не победа, это деление пота то есть если за победу ты плюсуешь 1, то за ничью максимум 0,5 Но Я же не эквилятор пишу. Я приводил уже пример для чего я так сделал. Борд: KQJT2, у меня туз. Бот считает HS и, пологая, что у оппа также туз, дает мне HS меньше 1 (0.98). За счет того, что там много ничьих в переборе. В результате бот уже скорее всего не сделает 3bet и cap в ответ на рейзы от оппа. А он ужратый просто и играет так с девяткой - вот, когда такое своими глазами увидишь, поймешь. А у тебя идет вычисление силы руки против спектра оппонента даже если на руках абсолютный натс? Я "абсолютный натс" определяю по значению силы руки против невзвешенного спектра оппа. Если HS >= 0.985, то считаю что натс. Если я правильно понимаю, то для каждого действия оппа (fold, call, raise, 3bet и тд) в определенной ситуации нужна своя таблица. Если это так, то довольно много получиться таблиц. В каком формате их хранить? Я пробую создать таблицу на 169, используя для теста 1 и 0 для каждой руки. Но хранить 169 нулей в строке для каждого действия что-то не очень. Если хранить 169, как биты с переводом в число, то 22 байта получается. А как ты хранишь 1326 дробных значений? sizeof(float)*1326 = 5304. Пять килобайт, чего смеятся? Тут таблица для быстрого вычисления силы рук на несколько мегабайт будет. Не памятью надо заморачиваться, её хватит. Handranks - это таблица соответствия для быстрого вычисления ранга руки, сохраненная в файл. Эта таблица глобальная, загружается один раз и лежит в памяти без изменений. На каждого оппонента заводится отдельная таблица весов. Т.е. кол-во таблиц равняется кол-ву оппонентов. В процессе игры оппонент совершает те или иные действия в определенном игровом контексте. Увидев очередное действие очередного оппонента, мы пытаемся перераспределить веса в таблице так, чтобы они отражали его вероятный спектр рук, соотв. совершенному действию. Этот процесс называется перевзвешиванием (reweighting). Когда очередь хода доходит до нас мы вычисляем характеристики (HS, PPot, NPot) своей руки, используя обновленные таблицы оппонентов. Одним словом, таблица весов - это наше представление о спектре рук оппонента. Занимает она в памяти 1326 эл-тов типа double. Что приблизительно 10К. И по-другому никак. Суммарный размер всех таблиц в памяти при макс. 9 оппонентах составляет 90К. В таблице в каждом элементе хранится вес - т.е. число с плав. точкой от 0 до 1. Как ты его представишь - тебе решать: float или double или как число с фикс. точкой. Ну это уж точно не один бит на руку. Про таблицу весов где можно почитать? Смотря, что ты хочешь узнать. Если реализацию, то что тут читать? Матрица из 1326 эл-тов вот и все. Если про перевзвешивание, то готовые алгоритмов нигде нет, а общие сведения есть в топовых публикациях CPRG. Например: 1) Algorithms and Assessment in Computer Poker 2) Dealing with Imperfect Information in Poker подскажи про практическое применение NPot. На флопе против одного высчитали HS на флопе, PPot и NPot до ривера. Если PPot>30, то мы имеем неплохие шансы на усиление. А вот при каких показателях NPot, нужно делать сложные фолды? Ну у меня алгоритм примерно такой. Сначала я считаю PPot и NPot на один раунд вперед. Это обозначается PPot1 и NPot1. Затем я считаю NPot и PPot с полным перебором (актуально только на флопе). Это обозначается PPot2 и NPot2. Затем я считаю EHS в текущем раунде, используя только PPot1, т.е. NPot1 у меня никогда не используется. PPot2 также никогда не используется. NPot2 используется в случае, когда EHS довольна мала (<0.47). В такой ситуации, если NPot2 >= 0.28, то мы делаем фолд, не глядя даже на шансы банка. А почему PPot2 не используешь на флопе? С PPot1 только натсовое дро по шансам получается, которое не всегда будет, особенно на 6 max. Я сегодня играл: на флопе HS 0.41 EHS 0.61 PPot2 0.42 - руки готовой нет, но потенциал какой (рука Ah Kh на флопе Qh 6h 6c) Дело в том, что PPot2 на флопе показывает наш потенциал к шоудауну. Мы не всегда будем играть до шоудауна, так что использовать его неправильно. Поскольку я не делаю обход дерева решений, то игра строится инкрементно - от раунда к раунду, следовательно, и эффективную силу руки EHS я вычисляю для конкретного раунда. Кроме того именно PPot1, а не PPot2, сравнивается с шансами банка. Если мы считаем шансы банка по формуле potOdds = amountToCall / (pot + amountToCall) то коллировать по шансам выгодно, когда PPot1 >= potOdds и только так - сравнивать PPot2 >= potOdds не корректно. Обычно PPot1 составляет от 10 до 20%, если больше 20%, то у нас уже 8 или более аутов, т.е. можно уже атаковать самому или делать чек-рейз, а не чек-колл. В твоем примере PPot1 на флопе будет где-то 0.22, а на терне 0.21, что соответствует шансам 4 к 1. Т.е. коллировать тебе будет выгодно практически всегда с учетом банка сформированного на префлопе. Но в этой ситуации, поскольку PPot1 >= 0.2, мой бот уже будет сам атаковать. А такой пример, в игре двое. Префлоп агрессии не было, у нас Ah 2h флоп Kh 9h 7c, пот = 1, опп бет пол пота. Пот оддсы для кола 0,33 (0,5/1,5). Если считать одну улицу, то получается фолд, а если до ривера, то кол. Только OESD + флеш дро с натяжкой подходит на одну улицу. Получается нужно падать с натсовым флеш дро. Или нужно 3бетить и замазываться с неготовой рукой? Если опп ставит тебе не по шансам, то надо бросать - это аксиома. В FL других вариантов нет, но твоя ситуация там вряд ли возможна. Другой вопрос в том, что в NL все гораздо сложнее. В NL ты в твоей ситуации делаешь колл, руководствуясь, не прямыми, а подразумеваемыми шансами банка (implied odds). Точно так же как ты делаешь cold call с карманкой на префлопе, который "по науке" требует potOdds <= 0.04. Т.е. ты знаешь, что, если соберешь свой натс, то заберешь стек у оппа. Т.е. в NL нужно сравнивать PPot1 не с potOdds, а с impliedOdds. Я не большой спец по NL, так что не могу сказать как там вычислить impliedOdds, но смысл примерно такой. Теперь смотри, ну пусть ты решил использовать PPot2, в твоем примере опп на флопе дал пол банка, ты колл. Он на ривере еще тебе барель выстрелит, твои действия? Тоже колл, правильно? А теперь посчитай сколько ты в итоге заплатил? Т.е. ты пытаешь оправдать свой колл (кстати абсолютно корректный), но PPot2 тут ни причем. В FL, например, на флопе PPot2 можно использовать при игре по шансам на вскрытие showdownOdds. В этом случае showdownOdds = (amountToCall + SBSize * 4) / (pot + amountToCall + 2 * SBSize * 4). Затем вычисляется полная EHS с использованием PPot2 и NPot2 и EHS уже сравнивается с showdownOdds. Но я так не делаю. Мой бот в FL в твоей ситуации сделает рейз. А на терне бет. На ривере скорее всего тоже бет, даже если флэш не собрался, а вот если собрался то на ривере в 25% будет чек с целью рейза. Но это зависит от оппа. --- EHS(opt) = HS + (1 - HS) * PPot EHS(full) = HS * (1 - NPot) + (1 - HS) * PPot EHS(opt) не учитывает NPot, EHS(full) все учитывает - и поэтму не совпадают --- EHS всегда должен быть >0.5 чтобы продолжать играть? Или EHS>1/количество_оппонентов? EHS уже учитывает кол-во оппонентов. Как я уже писал, EHS вычисляется либо так: EHS(opt) = HSn + (1 - HSn) * PPot либо так: EHS(full) = HSn * (1 - NPot) + (1 - HSn) * PPot Так или иначе нужно знать HSn, которое вычисляется перемножением вероятностей: HSn = HS1 * HS2 * ... * HSN, где N - кол-во оппонентов. Т.е. ты проходишь по всем оппонентам и против каждого вычисляешь HS и перемножаешь с предыдущим значением (см. код ниже). Если бы у оппов был невзвешенный спектр, то HSn = pow(HS_против_любого, число_оппов). double HSn = 1; for (int i = 0; i < num_opps; i++) { HSn *= CalcHandStrength(board, my_hole1, my_hole2, opp[i].GetWeightTable()); } После того как значение HSn посчитано, оно вместе с потенциалами подставляется в одну из приведенных выше формул (я использую первую - оптимистичный вариант, поощряеет агрессию) и вычисляется EHS. Что касается исходного вопроса. Да, EHS >= 0.5 уже достаточно для колла. Возможно нужно ввести еще верхнее значение, при достижении EHS которого, нужно рейзить. Ну да, я так EHS и считаю. Просто часто против двух оппонентов при 0.40.33 можно играть. Еще вопрос по PPot. Какую карту можно считать аутом? Я сейчас считаю так. Если при выходе карты я переезжаю 50% лучших на текущем борде рук из спектра оппонента, то это хороший аут. В NPot включаю карты, которые улучшают 20% худших рук оппонента против моей. 50% и 20% - это я просто пальцем в небо ткнул. Интересный подход. Я не считаю ауты, а использую PPot как есть в 2х случаях: 1) для вычисления EHS; 2) для бета/рейза вне зависимости от EHS, если PPot1 >= 0.2. Вот я и не знаю как правильно PPot вычислять. В статьях написано, что PPot - это шансы на усиление против лучших рук. Это можно понимать, как шансы на усиление до натса. Или просто на получение сильной руки. Я использую второй вариант. У меня PPot=good_cards/cards_in_deck, где good_cards - это количество карт, подходящих под описанное выше условие. В связи с этим у меня может получаться слишком оптимистичный EHS. >>>> если PPot >= 0.2, то мы знаем, что у нас какое-то сильное дро Было бы здорово.. но... Суть в том, что PPot - величина, характеризующая сколько рук мы побьем на ривере, среди тех, которые сейчас бьют нас. Поэтому если у тебя на руках 92o то любая девятка и двойка на борде идут в пользу PPot, хотя на самом деле у тебя средняя или низкая пара, а вовсе не стрит. Поясню точнее. Вот цикл у тебя по всем возможным рукам, ты сравниваешь 92o (свои) против 95o, ясно что 92o хуже (на борде AKQ). Тогда как при эмуляции борда если выпала 2, то рука 95o стала худшей, то есть попала в определение PPot. И таких рук как 95o просто тьма. Я тоже задумывался об использовании PPot как показателя "дро", но вот эти мысли (и проверка на практике) меня остановили. Замечу, что PPot можно использовать таким показателем, только если HS у нас высокое (порядка 85 - 95). Тогда подобными ситуациями можно пренебречь. Поэтому я пришел к другому способу в тех же понятиях. Мы можем сравнить EHS и HS и точно знать, вверх или вниз пойдет динамика руки. Говоря про спектры оппонентов интересно как упорядочиваете его на определенном борде. Вот к примеру, у опп-а есть спектр, выпал флоп, и теперь если опп делает ставку, то мы распределяем его спектр, давая топовым рукам больше шанс, чем слабым. То есть на борде нужно посчитать EHS каждой руки, чтобы можно было сравнить их топовость. Но ведь на это тратится немало времени ЦП? Интересно как вы решаете такую ситуаию. Я например просто записал в файл все борды, и достаю уже упорядоченный спектр по которому сортирую остальные. ========================================= Кто как считает ауты? Слышал что надо вычислить эквити против спектра оппонента при выходе определенной карты на терне. Допустим карта терна дает мне флеш. Эквити мой 80%. Это мой аут. Выходит другая карта и эквити моё 25%. Это не мой аут. Так перебираются все карты терна и считаются те из них, эквити против которых больше 50%. Против скольких аутов эквити больше 50% - столько и аутов. Но тут кроется ошибка. А вот что за ошибка, я до сих пор не могу внятно сформулировать. --- Аут - это карта, которая может дать минимум ТОП пару на следущей улице, эквити тут не при чем - оно лишь отражает то, что при выходе аута вероятность выигрыша повышается, но не дает ответа - что это - аут или нет. Плюс еще есть бэкдорные ауты которые есть только на флопе. Но это все работает только если мы не кладем оппу руку больше, чем топ-пара У тебя свое оригинальное понимание аута. Мне Рой Раундер как то ближе. Особенно вот это место: "... Ауты – это ТОЛЬКО те карты в колоде, которые дают вам выигрышную руку. И вопрос превращается в следующий: как я могу действительно знать, какая рука будет выигрышной? И ответ будет – никак. ..." топ-пара != лучшая рука. Ладно, тогда по-другому вопрос: как вы считает потенциал руки без шоудаун велью? Если есть шоудаун велью, то тут все просто. Эквити. ответ тот же, но по другому - кладем оппу руку от топ-пары и смотрим эквити. Хотя, по моему мнению, сложнее опредлить потенциал руки с шоудаун велью чем без него. Когда рука с шоудаун велью эквити слабо отражает потенциал почему по твоему это легче? Я понял. Мы мыслим по-разному. Тебя не устроит мой ответ. Да, ауты для ботов - это зло :) Если реализовать просчет EV линий. Думаю - правильно реализованный расчет EV это секрет успеха. Одного эквити не хватит - вот поэтому оно и доступно в паблике, а EV очень так ограничено прямо скажем. Как иногда пишут "этот параметр можно посчитать только приблизительно" итд итп так и хочется сказать "не надо мне заливать про приблизительно: формулы пишите - все давно кем-то посчитано" ну там имеется ввиду что чтобы посчитать ЕV нужно знать спектры оппонента в зависимости от его действий. А поскольку спектры - величина приблизительная, то и EV также величина приблизительная. А форумы да, уже где-то высчитаны. Нужно считать не ауты, а еv против диапазона противника (противников). Например, NL-6MAX, мы на СБ, УТГ открывает 16% рук, постфлоп играет, фолдит на 3бет 80%. Вопрос - с какими и как часто нужно его 3бетить? Ev этого 3бета? Всё это можно посчитать... Или NL HU. Опп стилит 65% рук, 4бетит 8%. Какой наш диапазон 3бета? Какой наш диапазон 5бета? Еv... Обычно при оценке руки используют понятия положительного (PPot) и отрицального (NPot) потенциалов. PPot = вероятность выигрыша руки, которая сейчас позади NPot = вероятность проигрыша руки, которая сейчас впереди Потенциал рассчитывается только на флопе и терне. Если мы считаем относительно всех возможных комбинаций оппонента, то на флопе мы перебираем 1081 комбинацию и на терне 990 комбинаций. Для каждой комбинации, которую мы назначем оппоненту на флопе мы перебираем все 2х карточные комбинации терна и ривера. Для каждой комбинации, которую мы назначем оппоненту на терне мы перебираем все карты ривера. Это дает 1070190 итераций на флопе (1081 * 45 * 44 / 2) и 43560 итераций на терне (990 * 44). В каждой итерации мы отмечаем случаи, когда наша рука была впереди но проиграла. И случаи, когда наша рука была позади, но к шоудауну выиграла. Потом вычисляем доли первого и второго счетчиков относительно общего числа итераций. Получаем соответсвенно NPot и PPot. PPot - это математическая форма кол-ва аутов, т.е. прямых потенциальных шансов (шансов на то, что вы переедите оппонента, если в данный момент вы являетесь андердогом) NPot - это математическая выражение обратных потенциальных шансов (шансов переезда вас оппонентом, если в данный момент вы являетесь фаворитом) Это имеет самое прямое отношение к подсчету аутов. Собственно это и есть мат. подсчет кол-ва аутов. Убедиться в этом можно, прочитав, например, Dealing with Imperfect Imformation in Poker - классический труд по покерному ИИ. Расчет эквити (иначе силы руки), например на флопе, начинается в текущем состоянии стола, т.е. без перебора всех возможных карт терна и ривера. Для этого нам нужно только перебрать все возможные комбинации оппонента (на флопе у него их 1081). Таким образом мы получаем мгновенную силу руки HS. Для учета возможностей роста и переезда после этого считаются потенциалы PPot и NPot. Это доп. итерации по перебору карт терна и ривера (на флопе их 44 * 45 / 2 = 990). Когда потенциалы посчитаны, мы можем рассчитать полную силу руки по формуле: HS' = HS + (1 - HS) * PPot - HS * NPot Что касается ничьей, то она приравнивается к нашей победе, что дает нам более оптимистичные цифры. --- >>> Для этого нам нужно только перебрать все возможные комбинации оппонента (на флопе у него их 1081). Вот это и есть рамдомный спектр оппа, ставить это любому игроку думаю ошибка >>> Для учета возможностей роста и переезда после этого считаются потенциалы PPot и NPot. опять все делаеться по рамдомному спектру с учетом всех возможных карт да ? >>> Что касается ничьей, то она приравнивается к нашей победе, что дает нам более оптимистичные цифры. А вот это точно неправильно ничья это деление пота по полам и она не приравниваеться к победе --- Для пояснения приведу пример. Пусть у нас AdQc на флопе 3h 4c Jh. Перебрав все 1081 комбинацию оппонента мы увидим, что существует 444 руки, кот. лучше нашей, 9 рук, кот. дают нам ничью и 628 рук, кот. хуже нашей, что дает нам HS = (628+9) / 1081 = 0.589 или ~59% на НЕМЕДЛЕННУЮ победу. Теперь, переберем все оставшиеся карты терна и ривера для каждой из 1081 комбинации оппонента. Получим 91981 случай, когда оппонент имел лучшую руку, но мы его переехали к шоудауну. 1036 случаев, когда мы закончили вничью. И 346543 случая, когда оппонент имел худшую руку, но переехал нас к шоудауну. PPot = (91981+1036)/(91981+1036+346543) = 0.211 или ~21% на победу, К ШОУДАУНУ, если мы считаем, что на флопе наша рука хуже руки оппонента. Грубо разделив эту вероятность на терн и ривер, получим где-то 11% и 10% соответственно, это и есть наши "ауты". Математически будет верным делать колл на флопе, если банк предлагает нам шансы 8-к-1 (~11%) или лучше. Ты прав, все делается без учета весов карт оппонента - это называется uniform weighting. Взвешивание опущено для облегчения понимания сути, тем не менее принцип именно такой. Если использовать веса, то к счетчикам на каждой итерации добавляется не единица, а вес очередной комбинации оппонента. Если не приравнивать ничью к победе, то по цифрам у нас не будет получаться натс, например, с рукой Ax на разноцветном столе KQJTx, потому что математика будет допускать, что туз есть и у оппонента. Но ведь в данной ситуации человек же так не думает, когда играет. Человек считает, что он выиграл и нет разницы туз у оппа или нет туза --- >>> Ты прав, все делается без учета весов карт оппонента - это называется uniform weighting если бы это все просто было - прикинуть против неизвестного оппа где мы находися, то да, согласен - это имеет право на жизнь >>> Если не приравнивать ничью к победе, то по цифрам у нас не будет получаться натс, например, с рукой Ax на разноцветном столе KQJTx, потому что математика будет допускать, что туз есть и у оппонента. Но ведь в данной ситуации человек же так не думает, когда играет. Человек считает, что он выиграл тут, я думаю, не важно, что у нас будет не 100% эквити, а важно то, что нет рук, которые нас могут побить - что является характеристикой натса ====================================================================================