![]() |
Алгоритм подсчета покерной руки
Вложений: 3
Доброго времени суток!
Вот добрался я и до этого форума :) Сразу скажу, что программист я никакой, и весь мой опыт программирования сводится к нескольким заданиям в рамках курса VB.NET в универе. Курс предназначен для людей, не имеющих опыта программирования, и знакомит с элементарными понятиями. Поскольку курс преподается на англ. языке, я знаю только английскую терминологию. Предположу, что array - массив, a data structure - структура данных. Финальный проект курса формулируется так. Надо сгенерировать колоду карт на 52 листа. Сначала нужно создать структуру данных, у которой два члена: face (номинал карты) и suit (масть). Затем нужно сформировать массив, который и будет наполнен 52 картами. Затем нужно сгенерировать 5 случайных уникальных номеров от 0 до 51 (или от 1 до 52) и на основе этих номеров сформировать покерную руку. Ну и самое главное - определить покерную комбиниацию, которая получится в рез-те. Покерные комбинации такие Flush (все одной масти) Четверка (четыре карды одного номинала) Full house (3+2) Тройка Две пары Пара Ничего Как видите, вариант упрощенный - straight (стрит) подсчитывать не нужно (курс-то для начинающих ;-) Ну и вывести надо 5 карт и название комбинации. Никаких картинок карточной колоды, все в текстовом виде. Затрудненения у меня вызвали пять уникальных случайных кард и алгоритм подсчета покерной руки. С пятью уникальными картами я вроде разобрался относительно легко. Прикрепленный файл Если я что-то упустил, то буду признателен, если вы укажете на ошибки. С подсчетом покерных комбинаций было сложнее. В конечном итоге, я его определил, но хотелось бы услышать и ваше мнение. Возможно, я что-то где-то упустил, или можно все сильно упростить. Основная идея: в руке не может быть более двух различных совпадающих номиналов карт. Т.е. если есть два туза и два короля, то пятой карте пары уже точно не будет. Значит надо подсчитать сколько раз совпали два различных номинала. Прикрепленный файл Ну а дальше уже детали. По порядку проверяем следующие условия:
Вот так я себе это представил, и вроде даже работает. Буду признателен, если вы проверите алгоритм на наличие ошибок или предложите более оптимальный вариант. Может где-то есть лишние шаги, которые можно убрать, упростив код. Спасибо за внимание. P.S. Полный код проекта прикреплен. |
Немного путанно или я старый уже, мозги усохли :] Можно уточнить условия?
>> Затем нужно сгенерировать 5 случайных уникальных номеров от 0 до 51 (или от 1 до 52) и на основе этих номеров сформировать покерную руку Что такое "сформировать покерную руку"? Нужно просто рандомом (следя за неповторяющимися номерами) выбрать 5 чисел от 1 до 52? И всё? Так? Т.е. "сдать пять карт"? >> Ну и самое главное - определить покерную комбиниацию, которая получится в рез-те. Опять непонятно. Честно, я не очень понимаю. Нужно просто сказать, какая комбинация карт получилась из сданных пяти? Так? Т.е. никаких подсчётов вероятностей, никаких"взять ещё три" и "сколько сдать карт, чтобы получить флэш" и т.д.? |
Тасование колоды:
Цитата:
1. Инициализируем массив, от 1 до 52, Перемешиваем массив - RandomN() 2. Берём его первые 5 элементов. |
Масть - номер карты (0-51) mod 4 (остаток от деления) 0 - пики... 3 - бубны
Номинал - номер карты (0-51) / 4 (целочисленное деление) 0 - двойка ... 12 - туз |
Проверку на флеш делаем очень быструю, без массивов.
Флеш - это когда K[0]%4 == k[1]%4 == k[2]%4 == k[3]%4 == k[4]%4 % - остаток от деления == - равно Сорри за мой С :-Р Поверки по номиналам даём с использованием промежуточного массива. 1. Делаем пустой масив номиналов S размером от 0(двойка) до 12 (туз), итого 13 элементов. 2. Инициализируем его нулями - это счётчики встреч 3. Для каждой карты K из массива k[0..4](пять карт) увеличиваем счётчик номинала. для i от 0 до 4 счётчик[K[i]/4] ++ / - целочисленое деление ++ - увеличение на 1. Далее сортируем массив S (счётчиков номинала по возрастанию) и проверяем результат: 1. если первый элемент стал 4 - "каре". 2. если первый элемент стал 3: 2а. если второй элемент равен 2 - "полный дом". 2б. если второй элемент равен 0 или 1 - "тройка" 3. если первый елемент равен 2: 3а. если второй элемент равен 2 - "две пары". 3б. если второй элемент равен 0 или 1 - "пара" Комбинации "туз" и "стрит-флеш" тоже легко вычисляются. Осталось только расписать "сортируем массив счётчиков" - это простая пузырьковая сортировка, например. Или чего-нибудь продвинутое :] |
Сортировка (два-три метода)
http://www.relib.com/articles/article.asp?id=197 |
hasherfrog
LOL Цитата:
Цитата:
Насчет тасования колоды я понял идею, но не слишком понял реализацию. Что делает следующий код? Код:
for i := N downto 2 do begin А для подсчета совпадений номиналов твой вариант действительно проще. Общая идея, как я вижу, такая же: отталкиваемся от факта, что у нас не более двух различных совпадающих номиналов карт. Однако исполнение намного проще. Мне как-то в голову не пришло использовать целочисленое деление :confused: Я сделал так. Допустим, у меня уже есть массив Hand(4), 5 элементов которого являются случайными числами от 0 до 51. Теперь создается массив HandFaces(12) для номиналов Код:
Dim HandFaces(12) As Integer Код:
For i = 0 To 4 |
>> Правильно?
Ну да :] Кстати... Я только сейчас увидел, что дал кусок про тасовку карт на паскале... :-P LOL, даже ROTFLMAO По-русски :] это будет так: Код:
Sub shuffle() |
hasherfrog
Да я уже разобрался как отсортировать массив в обратном порядке. На самом деле мне это не нужно, без разницы какой элемент использовать для подсчета сопвадений. Кстати, хорошая идея с Цитата:
Спасибо за пояснения по тасовке колоды. Я понял теперь :) Ты с VB на Си, a потом на паскаль прыгаешь, совсем меня запутал. Я же сказал, что я начинающий ;) На самом деле, я вижу преимущества такого метода вообще, т.к. если надо сдать две руки, то берутся просто следующие пять элементов массива. В моем же случае с одной единственной рукой я, пожалуй, оставлю свой код. Нет нужды в создании массива на 52 элемента, если нужно всего 5. Логично? Признателен за ответы :up: |
У-ё.
Неправильно я расписал тасовку. Так тужился, вспоминая васик... :] Переупростил код и потерял смысл использования -1. Надо так: Код:
'тасовка Стоит только начать... ;] Удачи при сдаче. |
hasherfrog
В общем, преподаватель сказал, что все очень хорошо и добавлять ничего не надо. Конечно, оговорился, что при финальной проверке может что-то и всплывет :) Ладно, я тут подумал как подсчитывать 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й элемент массива - десятка. Просто сортируем массив и проверяем совпадение номиналов. Единственное, что три раза один и тот же код сортировки и сравнения номиналов вставлять не хочется, надо как-то это иначе оформить. Но идея такая. |
Vadikan
Эммм. Я бы исходил из того, что стрит (правильно я интерпретирую straight?) - это пять _разных(1) по номиналу, но _последовательных(2) карт. 1. Т.о. предварительную проверку на стрит можно делать уже после определения всех остальных комбинаций. Стрит получится, только если нет даже пары. Ведь все карты должны быть _разные по номиналу. Единственное, что нельзя учитывать - это флеш. Поэтому проверку на флеш стоит делать уже _после проверки на стрит (и до проверки на стрит-флеш aka флеш-рояль, емнип). 2. "Последовательность" можно определить двумя способами. 2а. Сданные карты должны дать в массиве счётчиков единицы в пяти последовательных ячейках (до сортировки по номиналу - то есть проверку на стрит надо делать самой-самой первой). Проверка такая: для i=0 to (12 - 5) если S[i]==S[i+1]==S[i+2]==S[i+3]==S[i+4] (и, кстати, == 1), то стрит. 2б. Можно опираться на номинал. Взять промежуточный массив из 5 элементов, заполнить его номиналами карт (K/4). Отсортировать по увеличению. И проверить, если будет T[0]==T[1]+1==T[2]+2==T[3]+3==T[4]+4, то стрит. Единственное, что я не помню... Туз-король-дама-валет-десять - это стрит. А вот считается ли туз-двойка-тройка-4-5 за стрит??? Ентих покеров, афаик, штук десять разновидностей. В какой ваш перпод играет??? P.S. Ну а стрит-флеш, это когда есть стрит и есть флеш :] OOOPS. Заметил несуразность. Надо делать проверку до сортировки. И тут же - надо делать проверку после сортировки :] Выходит так, что придётся делать не сильно оптимизированную программу. Либо немного запутанную по логике. |
Т.е. вариант в п. 2а. подходит без учёта п 1. - заполняем массив номиналов, прогоняем 2а, сортируем, дальше проверяем остальные комбинации.
Либо заполняем массив номиналов, сортируем, проверяем остальные комбинации, затем, если ничего нет, прогоняем 2б. |
hasherfrog
Цитата:
По пункту 2а. Да вроде я таки делаю. Цитата:
По пункту 2б. Еще один промежуточный массив не очень хочется делать. Это уже третий получается... Цитата:
|
Vadikan
Спешил я, невнимательно прочёл :] У тебя же всё и расписано по полочкам, LOL. Меня сбило с толку IndexOf, я сразу полез делать своё самостийное решение :] Таки да, всё правильно, так и делай. |
hasherfrog
Да, мне тоже показалось, что ты не столько мое решение проверял, сколько свое продвигал ;) Я уже сделал: алгоритм сравнения повторющихся номиналов запихал в subroutine, и все работает как надо. Со straight-flush я не стал заморачиваться, и так уже сделано больше, чем требовалось в проекте. Бонусных очков все равно не предусмотрено... |
Зашел сейчас на страничку курса почитать письма сокурсников. В день сдачи проекта сразу несколько человек писали письма с вопросами о том, как им оценить покерную руку. Учитывая то, что как сгенерировать колоду фактически об'яснялось в лекциях, то непонятно чем люди занимались раньше :) Но речь не об этом. Преподаватель ответил сразу всем студентам, а не только тем, кто задавал вопрос. Он об'яснил, что надо считать совпадения номиналов, и сказал, что обычно студенты пишут очень болшие блоки IF/ELSE (бедные преподы потом в них разбираются ;-), но поскольку для вычисления "Пары" понадобится проверка десяти условий, то проблему подсчета совпадений номиналов он бы решил так:
Код:
For i = 0 to 4 |
Vadikan
имхо этот кусок кода определит только то, что на руке есть что-то "не меньше пары", а вот что именно... тут подумать надо а с преподом согласен, циклы всегда лучше/универсальнее (вдруг кому-то в голову придет сделать 6-карточный покер :)) |
aESThete
Мда... я вот сейчас посмотрел внимательнее... Не удивительно, что мне такое в голову не пришло :) Как ни крути, но придется работать с двумя числами, представляющимии совпадения номиналов. Я-то изначально два счетчика делал (второй прикрепленный файл), хотя там наверное можно было упростить как-то. |
Vadikan
счетчиков д.б. не два, а массив счетчиков (можно плавающий, можно по кол-ву номиналов...) тогда достигаем нужной гибкости: 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 это и красиво, и будет работать при любом кол-ве карт в руке, т.е. покажет все пары, тройки и каре... :) |
Цитата:
Спасибо за идею. Все равно, слишком много переменных вводится, тогда уж лучше массив... |
Цитата:
вводить массив не вижу смысла... ок, рассмотрим гипотетический случай с MaxSuit мастями, MaxFaces количеством карт в каждой масти и MaxCardsOnHand картами в руках игрока, тогда Цитата:
но это только одинаковые значения карт. опять же можно ввести джокеров и даже смотреть их старшинство (b.e. "пара дам с красным джокером" бьет "пару дам с синим") стриты, флешрояли и иже с ними рассмотрим в следующей главе :) |
aESThete
Сорри, я что-то не получил уведомления о последнем сообщении. Код:
if combinations(i) > 0 then Между тем, я получил за проект 100% с комментарием "Очень хороший проект" и в целом за курс А+, что является максимально возможно оценкой. Спасибо всем, кто принял участие в обсуждении :) |
поздравляю!
Код:
if combinations(i) > 0 then ' уже писал в предыдущем посте: combinations(2) - количество пар, combinations(3) - количество троек нуууу ооооочень красиво получается но есть подозрение, что ваш препод тогда или поседеет раньше времени, или в дурку попадет :lol: |
Жалко что не на си))) было бы в сто раз понятнее, код хороший, я вот хочу сделать калькулятор шансов (подсчет аутов), велосипед (покерную руку) делать не очень хотелось.
|
Время: 23:00. |
Время: 23:00.
© OSzone.net 2001-