Как сделать игру дурак

Игра (не) для дураков. Пишем AI для «Дурака» (часть 1)

Думаю, ни для кого не секрет, что «Дурак» (далее это слово будет написано с маленькой буквы и без кавычек) — это самая популярная карточная игра в России и странах бывшего СССР (хотя и почти неизвестная за его пределами). Несмотря на свое название и довольно несложные правила, выигрыш в ней все-таки зависит больше от мастерства игрока, чем от случайного расклада карт (в английской терминологии игры того и другого типов называются соответственно game of skill и game of chance. Так вот — дурак в большей степени game of skill).

Целью статьи является написание простого ИИ для игры. Под словом «простого» подразумевается следующее:

(Строго говоря, первый пункт уже не дает права такому ИИ называться искусственным интеллектом per se, а лишь псевдо-ИИ. Но такая терминология в разработке игр устоялась, поэтому ее мы менять не будем.)

Правила игры, думаю, известны всем, поэтому лишний раз их напоминать не буду. Тем, кто хочет свериться, советую обратиться в Википедию, там довольно хорошая статья на эту тему.

Итак, начнем. Очевидно, что в дураке чем старше карта, тем выгоднее иметь ее в руке. Поэтому, построим алгоритм на классической оценке силы руки и принятии решения (например, о подкидывании той или иной карты) на основе этой оценки. Припишем картам значения, например так:

(Используем числа, кратные 100 для того, чтобы избавиться в расчетах от floating-point и оперировать только целыми числами. Для чего нужны отрицательные оценки, увидим ниже в статье.)

В коде (все примеры кода в статье будут на Kotlin) это выглядит примерно так (функция relativaCardValue() возвращает ту самую оценку, а RANK_MULTIPLIER — это как раз коэффициент, равный 100):

Увы, это еще не все. Важно также учесть следующие правила оценки:

$$display$$\clubsuit 2 \spadesuit 2 \diamondsuit Q \heartsuit Q \clubsuit Q \spadesuit Q$$display$$ практически идеальна (конечно, если соперник не пойдет под вас королями или тузами): вы отобьетесь дамами, после чего повесите сопернику погоны вручите ему пару двоек.

а вот много карт одной (конечно же, некозырной) масти, наоборот, иметь невыгодно — они будут «мешать» друг другу. Например, рука

$$display$$\spadesuit 5 \spadesuit J \spadesuit A \diamondsuit 6 \diamondsuit 9 \diamondsuit K$$display$$ очень неудачна — даже если соперник не «выбьет» у вас первым ходом козыря и пойдет картой пиковой масти, то все прочие подкинутые карты будут других мастей, и на них придется отдавать козыри. Кроме того, с большой вероятностью останется невостребованной пятерка пик — все козыри у вас достоинством выше пятерки, поэтому ни при каких обстоятельствах (если, конечно же, изначально не зашли картой младше) вам не удастся покрыть ею какую-нибудь другую карту — вероятность взять очень высока. С другой стороны, заменим валета пик десяткой треф, а козырную шестерку — тройкой:

$$display$$\spadesuit 5 \clubsuit 10 \spadesuit A \diamondsuit 3 \diamondsuit 9 \diamondsuit K$$display$$ Несмотря на то, что мы заменили карты на более младшие, такая рука значительно лучше — во-первых, на трефовую масть не придется отдавать козыря (и можно будет с большей вероятностью использовать пикового туза), а во-вторых, если вы побьете какую-то карту вашей козырной тройкой, есть вероятность того, что кто-то кинет вам тройку пик (ибо особого смысла держать такую карту, как правило, нет), и вы «отоваритесь» пятеркой.

Для реализации этих стратегий модифицируем наш алгоритм: Здесь мы считаем количество карт каждой масти и достоинства.

… здесь добавляем бонусы за них (вызов Math.max нужен для того, чтобы не начислять отрицательные бонусы за младшие карты — потому что в данном случае это тоже выгодно).

… а тут, наоборот, штрафуем за несбалансированную по мастям руку (значение UNBALANCED_HAND_PENALTY опытным путем установлено как 200):

Наконец, учтем еще такую банальную вещь, как количество карт в руке. В самом деле, иметь в начале игры 12 хороших карт очень даже неплохо (тем более, что кинуть все равно смогут не больше 6), а вот в конце игры, когда помимо вас остался только соперник с 2 картами, это совсем не так.

Резюмируем — в полном виде функция оценки выглядит так:

Итак, у нас готова функция оценки. В следующей части планируется описать более интересную задачу — принятие решений на основе такой оценки.

Источник

Карточная игра «Дурак» на двух M5Stack

Цель урока

Сегодня мы напишем сетевую карточную игру на двух игроков. Какую игру написать? Давайте напишем популярную карточную игру «Дурак», цель которой — избавиться от всех карт. Подробнее о правилах Вы можете узнать здесь.

Как сделать игру дурак. Смотреть фото Как сделать игру дурак. Смотреть картинку Как сделать игру дурак. Картинка про Как сделать игру дурак. Фото Как сделать игру дурак

Немного теории

Последовательный порт — сленговое название интерфейса стандарта RS-232, которым массово оснащались персональные компьютеры, техника специального назначения, в том числе и принтеры. Порт называется «последовательным», так как информация через него передаётся по одному биту, последовательно бит за битом. Обычные персональные компьютеры снабжены RS-232, а микроконтроллеры и M5STACK в том числе использует TTL.

Как сделать игру дурак. Смотреть фото Как сделать игру дурак. Смотреть картинку Как сделать игру дурак. Картинка про Как сделать игру дурак. Фото Как сделать игру дурак
Рисунок 2. Разница напряжений между RS-232 и TTL

Для обмена данными через последовательный порт M5STACK используют цифровые порты ввод/вывода R0, R2 (RX) и T0, T2 (TX) (рис. 3), а также USB порт. Важно учитывать, что если вы используете функции для работы с последовательным портом, то нельзя одновременно с этим использовать порты 0 и 1 для других целей.

Как сделать игру дурак. Смотреть фото Как сделать игру дурак. Смотреть картинку Как сделать игру дурак. Картинка про Как сделать игру дурак. Фото Как сделать игру дурак
Рисунок 3. Правило подключения двух устройств через последовательный порт

Для работы с данным портом используют стандартный набор функций Serial из Arduino IDE https://www.arduino.cc/reference/en/language/functions/communication/serial/

Ознакомьтесь с понятием таблицы ASCII, с помощью которой кодируются символы в вычислительной технике: https://en.wikipedia.org/wiki/ASCII

Перечень компонентов для урока

Начнём!

Шаг 1. Приступим с самого главного — логотип

Давайте нарисуем логотип игры, который будет появляться на дисплее устройства при запуске игры. Для этого используем любой редактор, например MS Office Word и Paint (рис. 4).

Как сделать игру дурак. Смотреть фото Как сделать игру дурак. Смотреть картинку Как сделать игру дурак. Картинка про Как сделать игру дурак. Фото Как сделать игру дурак
Рисунок 4. Рисуем логотип игры в MS Office Word и Paint 🙂

Далее при помощи приложения из урока 1.2.1 http://forum.m5stack.com/topic/49/lesson-1-2-1-lcd-how-to-create-image-array конвертируем изображение в массив пикселей и получим файл logo.c, который подключим к скетчу.

Шаг 2. Создадим структуру колоды карт

По правилам игры используется колода, содержащая только 36 карт. По 6 карт выдается каждому игроку. Игровое поле рассчитано на 12 карт. Игрок может взять 10 карт, после чего проиграет. В игре карта имеет своё значение и масть, например 7. В программе карта имеет своё место: (в руках у игрока — поле игрока; в игровом поле; в использованной колоде; в неиспользованной колоде) и назначение: свободная карта, недоступная карта, карта козырная, карта игрока, карта противника. Сделаем структуру, которая содержит порядковые номера параметров. Колода карт представляет собой массив структур (рис. 5).

Как сделать игру дурак. Смотреть фото Как сделать игру дурак. Смотреть картинку Как сделать игру дурак. Картинка про Как сделать игру дурак. Фото Как сделать игру дурак
Рисунок 5. Структура карты и массив колоды карт

define playerAmount 6

Шаг 3. Напишем правила игры

Атака. Случай 1. Если в игровом поле нет ни одной карты, тогда игрок, который получил первое место в очереди может кинуть совершенно любую карту любой масти (рис. 5.1):

Как сделать игру дурак. Смотреть фото Как сделать игру дурак. Смотреть картинку Как сделать игру дурак. Картинка про Как сделать игру дурак. Фото Как сделать игру дурак
Рисунок 5.1. Можно сделать ход любой картой

Атака. Случай 2. Если соперник сделал ход на игрока, то игрок может отбиться картой такой же масти, но большего значения (рис. 5.2) или козырной картой любого значения при условии того, что последняя карта соперника в игровом поле не является козырной:

Как сделать игру дурак. Смотреть фото Как сделать игру дурак. Смотреть картинку Как сделать игру дурак. Картинка про Как сделать игру дурак. Фото Как сделать игру дурак
Рисунок 5.1. Соперник должен отбить карту игрока

Атака. Случай 3. Когда соперник отобьёт карту, то количество шагов станет равным и игрок сможет подкинуть ещё карты такого же значения, что имеется в игровом поле:

Бито/взять. Случай 1. Если у игроков одинаковое количество шагов, то игрок может отправить карты в отбитую колоду:

Бито/взять. Случай 2. Если у соперник сделал больше ходов, чем игрок, то игрок может забрать все карты с игрового поля себе:

Шаг 4. Нарисуем карты

Благодаря прекрасным стандартным функциям для работы со встроенным дисплеем из библиотеки M5STACK, рисование карт займет доли секунд и практически не займёт памяти устройства.

Лайфхак для пользователей Windows: попробуйте зайти в любой текстовый редактор и зажать на клавиатуре клавишу Alt и набрать одну из цифр от 3 до 6, после этого отпустить нажатые клавиши.

Шаг 5. Напишем анимацию движения карт

Шаг 6. Нарисуем игровой стол и меню

Шаг 7. Обновление графики

Дополнительные функции (например, drawPlayerId()) Вы можете посмотреть в полном скетче или написать собственные гораздо лучше 😉

Шаг 8. Напишем функции для приёма/отправки данных через последовательный порт

Как сделать игру дурак. Смотреть фото Как сделать игру дурак. Смотреть картинку Как сделать игру дурак. Картинка про Как сделать игру дурак. Фото Как сделать игру дурак
Рисунок 5.3

Функция отправки строки добавляет в конец строки полученной в качестве аргумента символ новой строки и отправляет её в последовательный порт. Потом пытается принять строку, содержащую в себе символ конца сеанса передачи данных. Если символ пришёл, то функция вернёт true иначе false.

Функция приёма строки в течении timeout (3000 миллисекунд) пытается принять строку, при этом очищая от мусора в начале до символа начала пакета. В случае неудачи возвращает пустую строку.

Шаг 9. Сделаем обработку входящих пакетов данных

Сделаем так, что любая информация, передаваемая между устройствами упаковывалась в специальные пакеты (рис. 6). Напишем функцию, которая принимает, распаковывает и выполняет пакеты. А также напишем вспомогательную функцию (parseString), которая позволит извлекать определенный участок из строки, заключенный между специальных знаков разделителей (похожа на метод Split из JS).

Как сделать игру дурак. Смотреть фото Как сделать игру дурак. Смотреть картинку Как сделать игру дурак. Картинка про Как сделать игру дурак. Фото Как сделать игру дурак
Рисунок 6. Структура пакета данных

Пример использования функции parseString:

Шаг 10. Реализуем систему задающую номер игрока

При включении устройства и соединении кабеля, каждое из устройств выдерживает случайный интервал времени, при этом слушает входящие пакеты, и, отсылает пакет «Я здесь!» (рис. 7). По-умолчанию оба устройства являются первыми игроками. То устройство, которое первым примет послание «Я здесь!» сразу назначит себе номер игрока 2.

Как сделать игру дурак. Смотреть фото Как сделать игру дурак. Смотреть картинку Как сделать игру дурак. Картинка про Как сделать игру дурак. Фото Как сделать игру дурак
Рисунок 7. Принцип работы рукопожатия для получения номера игрока

void handshakePlayerId() <
long tpid = random(10, 1001) * 10;
long previousMillis = millis();
while (!serialRecivePacket())
<
unsigned long currentMillis = millis();
if (currentMillis — previousMillis > tpid) break;
>
while (!serialSendPlayerTId());
>

Шаг 11. Упакуем и распакуем пакет с картой

Дополнительные функции (например, getTrumpCard()) Вы можете посмотреть в полном скетче или написать собственные гораздо лучше 😉

Шаг 12. Напишем функцию синхронизации

Функция синхронизации по факту является ключевой в данном проекте. Целью функции синхронизации является реагирование при действиях игроков и обмен игровой информацией. Функция вызывается в ручном и автоматическом режиме из цикла loop(). Когда очередь игрока функция работает в ручном режиме и выполняется только после действий игрока. В то время эта же функция работает в автоматическом режиме на устройстве соперника.

Шаг 13. Кто же выиграл?

Шаг 14. Соберём весь код воедино и загрузим его в устройство

Внимание: это мой первый проект игры, поэтому приведенный код является beta-версией, который может содержать баги и ошибки. Спасибо за понимание!

Скачать скетч для Arduino IDE можно внизу урока в разделе Загрузки.

Шаг 15. Запуск

Соединим устройства при помощи последовательного порта (рис. 5.1). И нажмём красные кнопки на обоих устройствах (рис. 8). Попробуем сыграть! (рис. 8.1).

Как сделать игру дурак. Смотреть фото Как сделать игру дурак. Смотреть картинку Как сделать игру дурак. Картинка про Как сделать игру дурак. Фото Как сделать игру дурак
Рисунок 8. Первый запуск игры

Как сделать игру дурак. Смотреть фото Как сделать игру дурак. Смотреть картинку Как сделать игру дурак. Картинка про Как сделать игру дурак. Фото Как сделать игру дурак
Рисунок 8.1. Играем!

Источник

Как научиться играть в дурака

Как сделать игру дурак. Смотреть фото Как сделать игру дурак. Смотреть картинку Как сделать игру дурак. Картинка про Как сделать игру дурак. Фото Как сделать игру дуракИгра в дурака неизменно остается одной из самых популярных карточных игр. В нее играют практически все, независимо от пола, социального статуса и даже возраста. Это хороший способ скоротать время и провести интересно вечер с друзьями. Научиться играть в дурака не сложно. Общие правила довольно просты к пониманию, однако можно попробовать научиться не только делать основные ходы, но и вырабатывать свою стратегию. Благодаря этому можно всегда выигрывать в карты. В любом случае начинайте изучение игры с общих правил.

Общие правила

Играть в дурака могут от 2 до 6 игроков. Обычно в игре используется колода, состоящая из 36 карт. Кто-то один должен раздать каждому игроку по шесть карт. Следующая карта при раздаче открывается и ложится под колоду. Это будет козырь в данной партии. Основная колода должна лежать рубашкой вверх. По мере необходимости из нее игроки будут брать карты. Цель игры состоит в том, чтобы избавиться от карт раньше, чем это сделают другие. Проигравшим считается тот игрок, который останется последним с картами в руках.

Начинает игру и ходит первым тот, у кого находится младший козырь. Вторую и последующие партии начинают ходить «из под дурака», т.е. ходит человек, который находится после того, кто проиграл в предыдущей партии. Ходы делаются по часовой стрелке. Ходить можно как одной, так и несколькими картами одного достоинства, несмотря на их масть. Атакуемый игрок должен побить их, накрыв старшей картой такой же масти. Если подходящей масти нет, то побить карты можно козырем.

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

Виды игры в дурака

Как сделать игру дурак. Смотреть фото Как сделать игру дурак. Смотреть картинку Как сделать игру дурак. Картинка про Как сделать игру дурак. Фото Как сделать игру дуракИгра в дурака имеет разные виды и варианты. Можно играть в простого, подкидного или переводного дурака. Правила в данном случае будут немного отличаться. Так при игре в подкидного дурака подкидывать карты может не только тот, кто ходил, но и остальные игроки. Если игроков много, то в начале игры можно договориться, будут ли подкидывать все игроки или же только крайние, сидящие по обе стороны от того, на кого был сделан ход.

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

Стратегия и тактика игры

Стратегия игры всегда основана на запоминании карт, а также ведении статистики игры. Кончено, не стоит исключать и теорию вероятности, а также психологию игры. Если рассматривать игру на двоих, то можно рассчитать, что из общего количества карт в колоде будет 9 козырных. У игроков оказывается полтора козыря в среднем на руках. Еще один лежит открытым в колоде. На протяжении всей игры нужно постараться посчитать, сколько козырей уходит и какие карты уже были разыграны. Благодаря этому можно спланировать выигрышные ходы.

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

Концовка игры

На самом деле играть в дурака не сложно. Уже после нескольких партий вы сможете разобраться и играть весьма неплохо. Если вы будете играть в компании друзей, они смогут более наглядно показать вам, как нужно действовать. На практике это осваивается быстрее.

Источник

Как сделать игру дурак. Смотреть фото Как сделать игру дурак. Смотреть картинку Как сделать игру дурак. Картинка про Как сделать игру дурак. Фото Как сделать игру дуракbottgg

При игре не более, чем в 2 игрока

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

1. Определения стиля игры соперника.

2. Создание своего контр-стиля игры.

Определение стиля игры соперника

Для того, чтобы определить в каком стиле играет соперник, для этого необходимо сыграть с ним несколько контрольных игр. Зачастую, встречается 4 основных стиля игры:

С данным вариантом игры меня познакомил один из пользователей моей программы, когда в алгоритме игры в дурака была данная брешь. Заключалась эта брешь в следующей тактике игры.

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

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

При игре в 3 игрока

В такой игре необходимо собирать козырные и парные карты. Для этого необходимо стараться отбиваться. Самое главное в такой игре это никогда не закидывать картами игрока, под которого Вы ходите. Это можно делать лишь только в конце игры, когда карты в колоде кончились и Вы видите, что этот игрок первым может выйти из игры. Объясняется это тем, что если закидать картами этого игрока, то следующий ход будет сделан под Вас следующим игроком и Вы не сможете еще раз скинуть ненужные карты и закидать игрока, который может к Вам зайти. Можно даже схитрить, взять карту, с которой к Вам зашли и тогда следующий игрок будет ходить к игроку, который к Вам должен будет ходить. У Вас появится шанс закидать картами этого игрока.

При игре в 4 игрока

В данной игре необходимо определиться играете Вы против всех или против двоих, то есть пара на пару. Если игрок, который сидит напротив Вас играет с Вами заодно, то играть станет намного легче. Обычно такую игру называют пара на пару. Игрок, который сидит слева от Вас играет в паре с игроком, который сидит справа от Вас, а Вы соответственно играете в паре с игроком, сидящим напротив Вас. В такой игре необходимо закидывать игроков слева и справа. Для этого рекомендуется к игроку слева в начале игры ходить с младших парных карт. Если таких нет, то выбрать младшую карту той масти, которой у Вас больше всего. Если и этого нет, то можно составить пару из младших карт, например: козырной шестерки и шестерки бубны.

Если Вам удалось завалить игрока справа от Вас и к Вам перешел ход, то ходить Вам желательно с тех карт, которых нет у игрока слева или справа. После первого круга игры уже можно предположить какие это карты. Например, в процессе отбоя игрок засветил карты 8, 10, валет, а игрок слева засветил 7, даму, в данном случае рекомендуется ходить с 6, 7, 9, так как шанс, что эти карты есть у Вашего напарника, велики.

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

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

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

При игре в 5 игроков и более

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

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

Если Вы собираетесь отбиваться. Рекомендуется отбиваться парными картами, так как у соперника меньше шансов подбросить Вам карту.

Если Вы собираетесь отбиваться. В этом случае, в Ваших картах не должно быть младших карт и более двух карт одной масти, за исключением козырных. Иначе Вы рискуете взять карты.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *