Избегайте невозможных состояний с моделью данных для карточной игры в Haskell - PullRequest
2 голосов
/ 13 февраля 2020

Я пытаюсь реализовать карточную игру Jass в Haskell и хотел бы сделать состояние игры как можно более явным или избегать невозможных состояний с типами.

В стандартном варианте участвуют четыре игрока. Игроки друг против друга образуют команду. В игре раунды играются до тех пор, пока одна команда не наберет целевой счет. В начале каждого раунда каждый игрок получает по 9 карт, один игрок выбирает козырь и ведет первый трюк. В фокусе каждый игрок должен разыграть одну карту. Карты сравниваются относительно главной масти и козыря, и игрок с самой высокой картой выигрывает. Очки трюков добавляются к счету команды победителя. Победитель также ведет на следующий трюк. В раунде играются трюки, пока у каждого игрока не осталось карт. Существует более подробное объяснение википедии и немецкое объяснение jassa.at .

Моя модель данных для игрока выглядит примерно так

data Player = Player
    { playerID :: PlayerID
    , name :: String
    , cards :: Set Card
    , sendMessage :: Message -> IO ()
    , receiveAction :: IO Action
    }

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

  1. Я хотел бы представить игру как конечный автомат с чистой функцией обновления, однако в большинстве штатов допустимо только одно действие только от одного игрока, и поэтому я думаю, что в функции обновления большая часть просто обрабатывает недопустимые входные данные.

Я не знаю, что будет лучший способ представить эти части игрового состояния.

Игроки.

  • Моей первой идеей было использовать простой список и каждый раз вращать этот список, чтобы текущий игрок всегда был главой. Это легко сделать, но я также думаю, что легко что-то сделать go неправильно, например, что если список пуст, или есть только один игрок и так далее ...
  • Использовать массив для игроки и использовать индекс для текущего игрока. Преимущество в том, что для перехода к следующему игроку мне просто нужно увеличить индекс. Где-то я должен использовать мод для циклического массива, но это не проблема. Я также попробовал это с XDataKinds, чтобы размер массива соответствовал уровню типа, и поэтому функция nextPlayer может управлять модом. Это также имеет то преимущество, что для команд я могу использовать только четные и нечетные индексы игроков. Но при этом я должен хранить дополнительную карту из playerID или индексировать карты игроков. Так что теперь массив и карта могут выйти из-под контроля c.

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

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

1 Ответ

4 голосов
/ 14 февраля 2020

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

Я начинаю с некоторые способ решить проблему, а затем узнать, как я go, каковы болевые точки. Какие ошибки я на самом деле склонен делать? В вашем случае я бы реализовал игру (или ее часть) каким-то образом, который смог бы выяснить, и тогда из этого опыта я буду знать, как сделать это лучше.

cycle :: [a] -> [a] берет список и повторяет это навсегда. Вы можете сделать это для своих игроков и навсегда занять главу списка.

Для непустых списков есть Data.List.NonEmpty.

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...