Моделирование войны в карточных играх на C #, часть 2 - Код

ПРИМЕЧАНИЕ. Это вторая часть серии из трех частей, демонстрирующей, как мы можем смоделировать карточную игру War как программу на C #. Часть 1 здесь. Возможно, вы захотите использовать образец проекта на GitHub, чтобы следить за этим сообщением. Также ознакомьтесь с другими моими постами из серии «Практика моделирования»!

Теперь, когда у нас есть наши Объекты, наблюдения и другие правила, пора приступить к их созданию!

Карта

Начнем с самого простого объекта в этой модели: самой карты.

В нашей модели каждая карта имеет три свойства:

  1. Костюм (червы, трефы, бубны или пики)
  2. Ценность
  3. Отображаемое имя (например, «10D» для 10 бубен, «AC» для треф-туза и т. Д.)

В итоге я использовал перечисление C # для представления масти карт, поэтому вот это перечисление плюс объект Card:

Игрок

Теперь давайте начнем с очень простого определения объекта Player. В нашей модели игрок имеет следующие свойства:

  1. Имя
  2. Коллекция карт (это колода игрока).

Итак, наш объект игрока выглядит так:

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

Колода карт

Вот первый секрет нашей модели: на самом деле нет объекта под названием Deck. Скорее, мы реализуем колоду карт, используя встроенный объект Queue <>в C #. В этой коллекции есть почти все функции, необходимые для создания колоды карт, так почему бы не использовать ее?

Ключевое слово: почти. Нам нужен еще один метод, чтобы упростить наш код: метод, который может «ставить в очередь» или помещать в коллекцию коллекцию карточек, а не только одну. Вот этот метод расширения:

Колода карт

Несмотря на то, что объекта Deck нет, нам все равно нужно будет создать стандартную колоду игральных карт из 52 карт и перетасовать ее.

Стандартная колода состоит из 13 карт каждой из четырех мастей: туз, карты с номерами 2-10, валет, дама и король.

Вот статический класс DeckCreator, который даст нам стандартную колоду карт:

Обратите внимание на необычное для объявления. Мы собираемся сделать 13 карт в каждой масти, но каждая карта будет иметь значение от 2 до 14. Это связано с тем, что в этой игре тузы высоки (выше, чем короли), и мы хотим легко сравнивать значения.

Также обратите внимание на метод Shuffle (), который мы еще не реализовали. Ранее я писал сообщение об алгоритме тасования карт Фишера-Йейтса, и это то, что мы собираемся использовать для тасования карт.

Наконец, нам нужно обработать метод GetShortName (). Мы не хотим отображать «Пиковый туз» каждый раз, когда появляется эта карта, скорее мы хотим отображать короткое название «AS» (или «5C» для 5 треф, или «KD» для бубнового короля и скоро). Итак, нам нужен способ присвоить это короткое имя каждой карточке, например:

С картой, игроком и «колодой» мы наконец можем создать последний и самый сложный объект в нашей модели: саму игру.

Игра

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

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

Помните, что наша модель делает колоду свойством объекта Player, поэтому она не будет объявлена ​​в объекте Game.

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

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

Первый шаг относительно прост, поэтому давайте закодируем его:

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

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

Вот этот метод проверки в конце игры в объекте Game:

Теперь нам предстоит разобраться с самым сложным из трех шагов: розыгрышем хода.

Разыгрывание хода

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

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

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

Решение: в части 1 я упоминал, что «официальные» правила войны не говорят, что произойдет, если у игрока закончатся карты во время войны. В этой модели я делаю этот сценарий немедленным концом игры, поскольку он упрощает систему в целом.

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

Имея все это в виду, вот код:

Решение: в части 1 мы обсуждали, что война в реальной жизни не является детерминированной, а это означает, что результат не может быть известен после перетасовки карт, потому что они будут добавлены в колоды игроков в случайном порядке. Наша модель почти случайно сделала войну детерминированной; карты всегда добавляются в колоды игроков в известном порядке. Следовательно, при желании мы могли бы «знать» после раздачи, какой игрок выиграет. Вам, уважаемые читатели, решать, что делать с этой информацией.

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

Не забудьте проверить образец проекта на GitHub !. Как всегда, приветствуются конструктивная критика и советы по улучшению этого проекта. Поделитесь своими мыслями в комментариях!