Tuesday 1 December 2009

Playing Cards with Haskell

As my first attempt at writing a reasonably complex lump of code in Haskell, I thought I'd try and model a card game called Klondike Solitaire. Apparently Solitaire is complex enough that:

... the odds of winning a standard game of non-Thoughtful Klondike are currently unknown. It has been said that the inability for theoreticians to calculate these odds is "one of the embarrassments of applied mathematics

The first step is to try and model the playing cards themselves. Cards have both a Suit and a Value.

data Suit = Clubs
| Diamonds
| Hearts
| Spades
deriving (Eq,Enum,Show,Bounded)

data Value = Ace
| Two
| Three
| Four
| Five
| Six
| Seven
| Eight
| Nine
| Ten
| Jack
| Queen
| King
deriving (Eq,Enum,Show,Bounded)

data Card = Card Value Suit
deriving (Eq,Show)

Suit and Value both derive Eq (for equality), Show (for printing), Enum (for successor and predecessor functions) and Bounded (because they are within bounds). We can now define a complete deck of cards with a simple list comprehension.

allCards :: [Card]
allCards = [Card x y | x <- [Ace .. King], y <- [Clubs .. Spades]]

Next there are a bunch of helper functions which seem like they'll be generally useful.

color :: Card -> Color
color (Card _ s) | s == Clubs || s == Spades = Black
| otherwise = Red

value :: Card -> Value
value (Card x _) = x

-- |Are the two cards alternate colours?
alternateColors :: Card -> Card -> Bool
alternateColors a b = color a /= color b

-- |Does the second card follow the first?
follows :: Card -> Card -> Bool
follows (Card King _) _ = False
follows (Card v1 _) (Card v2 _) = succ v1 == v2

succ gives the successor of a given function. I don't think I can define Card as an instance of Enum because, depending on the context, the successor could be one of multiple values (e.g. the successor to Card Ace Spades might be a Card Two Spades or a Card Two Hearts depending on the context.

The -- | syntax is an indicator for the Haddock documentation tool so that you can associate the comment with the declaration.

Next time, I'll look at defining the types for Klondike Solitaire!