{-# LANGUAGE OverloadedStrings #-} module Skat.Bidding ( biddingScore, Game(..), Modifier(..), isHand, getTrump, Result(..), getResults ) where import Data.Aeson hiding (Null, Result) import Skat.Card import Data.List (sortOn) import Data.Ord (Down(..)) import Control.Monad import Skat.Pile -- | different game types data Game = Colour Colour Modifier | Grand Modifier | Null | NullHand | NullOuvert | NullOuvertHand deriving (Show, Eq) instance ToJSON Game where toJSON (Grand mod) = object ["game" .= ("grand" :: String), "modifier" .= show mod] toJSON (Colour col mod) = object ["game" .= ("colour" :: String), "modifier" .= show mod, "colour" .= show col] toJSON Null = object ["game" .= ("null" :: String)] toJSON NullHand = object ["game" .= ("nullhand" :: String)] toJSON NullOuvert = object ["game" .= ("nullouvert" :: String)] toJSON NullOuvertHand = object ["game" .= ("nullouverthand" :: String)] instance FromJSON Game where parseJSON = withObject "Game" $ \v -> do gamekind <- v .: "game" case (gamekind :: String) of "colour" -> do col <- v .: "colour" mod <- v .: "modifier" return $ Colour (read col) mod "grand" -> do mod <- v .: "modifier" return $ Grand mod "null" -> return Null "nullhand" -> return NullHand "nullouvert" -> return NullOuvert "nullouverthand" -> return NullOuvertHand _ -> mzero -- | modifiers for grand and colour games data Modifier = Einfach | Schneider | Schwarz | Hand | HandSchneider | HandSchneiderAngesagt | HandSchneiderSchwarz | HandSchneiderAngesagtSchwarz | HandSchwarzAngesagt | Ouvert deriving (Show, Eq) instance FromJSON Modifier where parseJSON = withObject "Modifier" $ \v -> do hnd <- v .: "hand" if hnd then do schneider <- v .:? "schneider" .!= False schwarz <- v .:? "schwarz" .!= False ouvert <- v .:? "ouvert" .!= False case (schneider, schwarz, ouvert) of (_, _, True) -> return Ouvert (True, False, _) -> return HandSchneiderAngesagt (_, True, _) -> return HandSchwarzAngesagt _ -> return Hand else return Einfach isHand :: Modifier -> Bool isHand Einfach = False isHand Schneider = False isHand Schwarz = False isHand _ = True -- | calculate the value of a game with given cards biddingScore :: HasCard c => Game -> [c] -> Int biddingScore game@(Grand mod) cards = (spitzen game cards + modifierFactor mod) * 24 biddingScore game@(Colour Clubs mod) cards = (spitzen game cards + modifierFactor mod) * 12 biddingScore game@(Colour Spades mod) cards = (spitzen game cards + modifierFactor mod) * 11 biddingScore game@(Colour Hearts mod) cards = (spitzen game cards + modifierFactor mod) * 10 biddingScore game@(Colour Diamonds mod) cards = (spitzen game cards + modifierFactor mod) * 9 biddingScore Null _ = 23 biddingScore NullHand _ = 35 biddingScore NullOuvert _ = 46 biddingScore NullOuvertHand _ = 59 -- | calculate the modifier based on the game kind modifierFactor :: Modifier -> Int modifierFactor Einfach = 1 modifierFactor Schneider = 2 modifierFactor Schwarz = 3 modifierFactor Hand = 2 modifierFactor HandSchneider = 3 modifierFactor HandSchneiderAngesagt = 4 modifierFactor HandSchneiderSchwarz = 4 modifierFactor HandSchneiderAngesagtSchwarz = 5 modifierFactor HandSchwarzAngesagt = 6 modifierFactor Ouvert = 7 -- | get all available trumps for a given game allTrumps :: Game -> [Card] allTrumps (Grand _) = jacks allTrumps (Colour col _) = jacks ++ [Card t col | t <- [Ace,Ten .. Seven] ] jacks :: [Card] jacks = [ Card Jack Clubs, Card Jack Spades, Card Jack Hearts, Card Jack Diamonds ] -- | calculate the spitzen count spitzen :: HasCard c => Game -> [c] -> Int spitzen game cards | null trumps = length $ allTrumps game | mit = foldl (\val (a, o) -> if a == o then val + 1 else val) 0 zipped | otherwise = findOhne (allTrumps game) 0 where trumps = getTrumps game cards zipped = zip (allTrumps game) trumps mit = Card Jack Clubs == head trumps findOhne [] acc = acc findOhne (c:cs) acc = if c /= highest then findOhne cs (acc+1) else acc highest = head trumps -- | get all trumps for a given game out of a hand of cards getTrumps :: HasCard c => Game -> [c] -> [Card] getTrumps (Grand _) cards = sortOn Down $ filter (isTrump Jacks) $ map toCard cards getTrumps (Colour col _) cards = sortOn Down $ filter (isTrump $ TrumpColour col) $ map toCard cards getTrumps _ _ = [] -- | get trump for a given game getTrump :: Game -> Trump getTrump (Colour col _) = TrumpColour col getTrump (Grand _) = Jacks getTrump _ = None data Result = Result { resultGame :: Game , resultScore :: Int , resultSinglePoints :: Int , resultTeamPoints :: Int } deriving (Show, Eq) instance ToJSON Result where toJSON (Result game points sgl tm) = object ["game" .= game, "points" .= points, "single" .= sgl, "team" .= tm] isSchwarz :: Team -> Piles -> Bool isSchwarz tm = null . wonCards tm hasWon :: Game -> Piles -> (Bool, Game) hasWon Null ps = (Single `isSchwarz` ps, Null) hasWon NullHand ps = (Single `isSchwarz` ps, NullHand) hasWon NullOuvert ps = (Single `isSchwarz` ps, NullOuvert) hasWon NullOuvertHand ps = (Single `isSchwarz` ps, NullOuvertHand) hasWon (Colour col mod) ps = let (b, mod') = meetsCall mod ps in (b, Colour col mod') hasWon (Grand mod) ps = let (b, mod') = meetsCall mod ps in (b, Grand mod') meetsCall :: Modifier -> Piles -> (Bool, Modifier) meetsCall Hand ps = case wonByPoints ps of (b, Schneider) -> (b, HandSchneider) (b, Schwarz) -> (b, HandSchneiderSchwarz) (b, Einfach) -> (b, Hand) meetsCall HandSchneiderAngesagt ps = case wonByPoints ps of (b, Schneider) -> (b, HandSchneiderAngesagt) (b, Schwarz) -> (b, HandSchneiderAngesagtSchwarz) (b, Einfach) -> (False, HandSchneiderAngesagt) meetsCall HandSchwarzAngesagt ps = case wonByPoints ps of (b, Schneider) -> (False, HandSchwarzAngesagt) (b, Schwarz) -> (b, HandSchwarzAngesagt) (b, Einfach) -> (False, HandSchwarzAngesagt) meetsCall _ ps = wonByPoints ps wonByPoints :: Piles -> (Bool, Modifier) wonByPoints ps | Team `isSchwarz` ps = (True, Schwarz) | sgl >= 90 = (True, Schneider) | Single `isSchwarz` ps = (False, Schwarz) | sgl <= 30 = (False, Schneider) | otherwise = (sgl > 60, Einfach) where (sgl, _) = count ps :: (Int, Int) -- | get result of game getResults :: Game -> Hand -> Piles -> Piles -> Result getResults game sglPlayer before after = Result afterGame score sglPoints teamPoints where (won, afterGame) = hasWon game after hand = skatCards before ++ (map toCard $ handCards sglPlayer before) (sglPoints, teamPoints) = count after gameScore = biddingScore afterGame hand score = if won then gameScore else (-2) * gameScore