I am trying to implement the Jass card game in Haskell and would like to make the game state as explicit as possible or avoiding impossible states with types.
The standard variant involves four players. The players opposite each other form a team. In a game rounds are played until one team reaches the target score. At the start of each round each player is dealt with 9 cards one player gets to choose the trump, and lead the first trick. In a trick every player has to play one card. The cards are compared with respect to the leading suit and trump and the player with the highest card wins the trick. The points of the tricks are added to the score of the winner's team. The winner also is lead on the next trick. In a round tricks are played until each player has no cards left. There is a more detailed explanation on wikipedia and a german explanation on jassa.at.
My data model for the player looks something like this
data Player = Player
{ playerID :: PlayerID
, name :: String
, cards :: Set Card
, sendMessage :: Message -> IO ()
, receiveAction :: IO Action
}
this way I can just use different send and receive functions for like command line or network players.
I don't know what would be the best way to represent these parts of the game state.
The Players.
Trick
I am not sure if i should store a list of played cards and who played them or just a record with the lead, highest card, winner and the value of the played cards and keep track of all played cards in a writer monad.
Rounds and Tricks
This is kinda the same for both, should i store a list with all played rounds and tricks or only the current round and trick and save the sum of the points from the previous rounds/tricks
There is temptation to have the type checker prove your program is perfect. However, that will become an endless pursuit. There really are not the hours in the day to spend teaching a computer how to be sure what you're sure is sure is for sure for every surety.
What I do is start with some way of solving the problem and then learn as I go what the pain points are. Which mistakes am I actually prone to making? In your case, I would implement the game (or part of) in some way I can figure out, and then from that experience I will know how to do it better.
cycle :: [a] -> [a] takes a list and repeats it forever. You can do this for your players and take the head of the list forever.
For non-empty lists there is Data.List.NonEmpty.
A way to only construct valid game states is to define an abstract data type. Rather than exporting the data constructors for a type, you only export functions of your own definition which can construct the type. That way, you can do whatever (runtime) checking or fixing you want.
Another tool is unit testing. Encoding propositions as types is difficult, especially in Haskell, and so the vast majority will not be. Instead, you can use property-based unit testing to recover a modicum of assurance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With