-- | A module containing some simple data definitions for a deck of playing cards.
module Numeric.Probability.Game.PlayingCards (PlayingCard(..),Suit(..),Rank(..),AceLowRank(..),deck,
  sameSuit, sameRank) where

import Data.Function (on)

import Numeric.Probability.Game.Cards

-- | The rank of playing cards.  The ranking is specified ace-high, as this is
-- how many games operate.  If you wish to have an ace-low ordering you can use
-- the 'AceLowRank' newtype.
data Rank = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack
  | Queen | King | Ace
  deriving (Bounded, Enum, Eq, Ord, Show, Read)

-- | A wrapper for 'Rank' where the Ord, Enum and Bounded instances are adjusted
-- to list Ace as the lowest item rather than the highest.
newtype AceLowRank = AceLow {alRank :: Rank}
  deriving (Eq, Show, Read)

instance Bounded AceLowRank where
  minBound = AceLow Ace
  maxBound = AceLow King

instance Enum AceLowRank where
  fromEnum (AceLow Ace) = 0
  fromEnum (AceLow x) = fromEnum x + 1
  toEnum 0 = AceLow Ace
  toEnum n = AceLow $ toEnum (n - 1)

instance Ord AceLowRank where
  compare = compare `on` fromEnum

-- | The standard four suits of playing cards.  The ordering on them is arbitrary
-- (alphabetical, in fact).
data Suit = Clubs | Diamonds | Hearts | Spades
  deriving (Eq, Ord, Show, Read)

-- | A playing card with a rank and suit.  The ordering on them is arbitrary (by
-- rank then by suit).
data PlayingCard = PlayingCard {rank :: Rank, suit :: Suit}
  deriving (Eq, Ord, Show, Read)

-- | The standard full deck of 52 playing cards.
deck :: Cards PlayingCard
deck = makeCards [PlayingCard r s | r <- [minBound .. maxBound], s <- [Clubs,Diamonds,Hearts,Spades]]

-- | Checks if the two cards have the same suit.
sameSuit :: PlayingCard -> PlayingCard -> Bool
sameSuit = (==) `on` suit

-- | Checks if the two cards have the same rank.
sameRank :: PlayingCard -> PlayingCard -> Bool
sameRank = (==) `on` rank