Skat Engine und AI auf Haskell Basis
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

198 lines
7.6KB

  1. {-# LANGUAGE OverloadedStrings #-}
  2. module Skat.Bidding (
  3. biddingScore, Game(..), Modifier(..), isHand, getTrump, Result(..),
  4. getResults
  5. ) where
  6. import Data.Aeson hiding (Null, Result)
  7. import Skat.Card
  8. import Data.List (sortOn)
  9. import Data.Ord (Down(..))
  10. import Control.Monad
  11. import Skat.Pile
  12. -- | different game types
  13. data Game = Colour Colour Modifier
  14. | Grand Modifier
  15. | Null
  16. | NullHand
  17. | NullOuvert
  18. | NullOuvertHand
  19. deriving (Show, Eq)
  20. instance ToJSON Game where
  21. toJSON (Grand mod) =
  22. object ["game" .= ("grand" :: String), "modifier" .= show mod]
  23. toJSON (Colour col mod) =
  24. object ["game" .= ("colour" :: String), "modifier" .= show mod, "colour" .= show col]
  25. toJSON Null = object ["game" .= ("null" :: String)]
  26. toJSON NullHand = object ["game" .= ("nullhand" :: String)]
  27. toJSON NullOuvert = object ["game" .= ("nullouvert" :: String)]
  28. toJSON NullOuvertHand = object ["game" .= ("nullouverthand" :: String)]
  29. instance FromJSON Game where
  30. parseJSON = withObject "Game" $ \v -> do
  31. gamekind <- v .: "game"
  32. case (gamekind :: String) of
  33. "colour" -> do
  34. col <- v .: "colour"
  35. mod <- v .: "modifier"
  36. return $ Colour (read col) mod
  37. "grand" -> do
  38. mod <- v .: "modifier"
  39. return $ Grand mod
  40. "null" -> return Null
  41. "nullhand" -> return NullHand
  42. "nullouvert" -> return NullOuvert
  43. "nullouverthand" -> return NullOuvertHand
  44. _ -> mzero
  45. -- | modifiers for grand and colour games
  46. data Modifier = Einfach
  47. | Schneider
  48. | Schwarz
  49. | Hand
  50. | HandSchneider
  51. | HandSchneiderAngesagt
  52. | HandSchneiderSchwarz
  53. | HandSchneiderAngesagtSchwarz
  54. | HandSchwarzAngesagt
  55. | Ouvert
  56. deriving (Show, Eq)
  57. instance FromJSON Modifier where
  58. parseJSON = withObject "Modifier" $ \v -> do
  59. hnd <- v .: "hand"
  60. if hnd then do
  61. schneider <- v .:? "schneider" .!= False
  62. schwarz <- v .:? "schwarz" .!= False
  63. ouvert <- v .:? "ouvert" .!= False
  64. case (schneider, schwarz, ouvert) of
  65. (_, _, True) -> return Ouvert
  66. (True, False, _) -> return HandSchneiderAngesagt
  67. (_, True, _) -> return HandSchwarzAngesagt
  68. _ -> return Hand
  69. else return Einfach
  70. isHand :: Modifier -> Bool
  71. isHand Einfach = False
  72. isHand Schneider = False
  73. isHand Schwarz = False
  74. isHand _ = True
  75. -- | calculate the value of a game with given cards
  76. biddingScore :: HasCard c => Game -> [c] -> Int
  77. biddingScore game@(Grand mod) cards = (spitzen game cards + modifierFactor mod) * 24
  78. biddingScore game@(Colour Clubs mod) cards = (spitzen game cards + modifierFactor mod) * 12
  79. biddingScore game@(Colour Spades mod) cards = (spitzen game cards + modifierFactor mod) * 11
  80. biddingScore game@(Colour Hearts mod) cards = (spitzen game cards + modifierFactor mod) * 10
  81. biddingScore game@(Colour Diamonds mod) cards = (spitzen game cards + modifierFactor mod) * 9
  82. biddingScore Null _ = 23
  83. biddingScore NullHand _ = 35
  84. biddingScore NullOuvert _ = 46
  85. biddingScore NullOuvertHand _ = 59
  86. -- | calculate the modifier based on the game kind
  87. modifierFactor :: Modifier -> Int
  88. modifierFactor Einfach = 1
  89. modifierFactor Schneider = 2
  90. modifierFactor Schwarz = 3
  91. modifierFactor Hand = 2
  92. modifierFactor HandSchneider = 3
  93. modifierFactor HandSchneiderAngesagt = 4
  94. modifierFactor HandSchneiderSchwarz = 4
  95. modifierFactor HandSchneiderAngesagtSchwarz = 5
  96. modifierFactor HandSchwarzAngesagt = 6
  97. modifierFactor Ouvert = 7
  98. -- | get all available trumps for a given game
  99. allTrumps :: Game -> [Card]
  100. allTrumps (Grand _) = jacks
  101. allTrumps (Colour col _) = jacks ++ [Card t col | t <- [Ace,Ten .. Seven] ]
  102. jacks :: [Card]
  103. jacks = [ Card Jack Clubs, Card Jack Spades, Card Jack Hearts, Card Jack Diamonds ]
  104. -- | calculate the spitzen count
  105. spitzen :: HasCard c => Game -> [c] -> Int
  106. spitzen game cards
  107. | null trumps = length $ allTrumps game
  108. | mit = foldl (\val (a, o) -> if a == o then val + 1 else val) 0 zipped
  109. | otherwise = findOhne (allTrumps game) 0
  110. where trumps = getTrumps game cards
  111. zipped = zip (allTrumps game) trumps
  112. mit = Card Jack Clubs == head trumps
  113. findOhne [] acc = acc
  114. findOhne (c:cs) acc = if c /= highest then findOhne cs (acc+1) else acc
  115. highest = head trumps
  116. -- | get all trumps for a given game out of a hand of cards
  117. getTrumps :: HasCard c => Game -> [c] -> [Card]
  118. getTrumps (Grand _) cards = sortOn Down $ filter (isTrump Jacks) $ map toCard cards
  119. getTrumps (Colour col _) cards = sortOn Down $ filter (isTrump $ TrumpColour col) $ map toCard cards
  120. getTrumps _ _ = []
  121. -- | get trump for a given game
  122. getTrump :: Game -> Trump
  123. getTrump (Colour col _) = TrumpColour col
  124. getTrump (Grand _) = Jacks
  125. getTrump _ = None
  126. data Result = Result { resultGame :: Game
  127. , resultScore :: Int
  128. , resultSinglePoints :: Int
  129. , resultTeamPoints :: Int }
  130. deriving (Show, Eq)
  131. instance ToJSON Result where
  132. toJSON (Result game points sgl tm) =
  133. object ["game" .= game, "points" .= points, "single" .= sgl, "team" .= tm]
  134. isSchwarz :: Team -> Piles -> Bool
  135. isSchwarz tm = null . wonCards tm
  136. hasWon :: Game -> Piles -> (Bool, Game)
  137. hasWon Null ps = (Single `isSchwarz` ps, Null)
  138. hasWon NullHand ps = (Single `isSchwarz` ps, NullHand)
  139. hasWon NullOuvert ps = (Single `isSchwarz` ps, NullOuvert)
  140. hasWon NullOuvertHand ps = (Single `isSchwarz` ps, NullOuvertHand)
  141. hasWon (Colour col mod) ps = let (b, mod') = meetsCall mod ps
  142. in (b, Colour col mod')
  143. hasWon (Grand mod) ps = let (b, mod') = meetsCall mod ps
  144. in (b, Grand mod')
  145. meetsCall :: Modifier -> Piles -> (Bool, Modifier)
  146. meetsCall Hand ps = case wonByPoints ps of
  147. (b, Schneider) -> (b, HandSchneider)
  148. (b, Schwarz) -> (b, HandSchneiderSchwarz)
  149. (b, Einfach) -> (b, Hand)
  150. meetsCall HandSchneiderAngesagt ps = case wonByPoints ps of
  151. (b, Schneider) -> (b, HandSchneiderAngesagt)
  152. (b, Schwarz) -> (b, HandSchneiderAngesagtSchwarz)
  153. (b, Einfach) -> (False, HandSchneiderAngesagt)
  154. meetsCall HandSchwarzAngesagt ps = case wonByPoints ps of
  155. (b, Schneider) -> (False, HandSchwarzAngesagt)
  156. (b, Schwarz) -> (b, HandSchwarzAngesagt)
  157. (b, Einfach) -> (False, HandSchwarzAngesagt)
  158. meetsCall _ ps = wonByPoints ps
  159. wonByPoints :: Piles -> (Bool, Modifier)
  160. wonByPoints ps
  161. | Team `isSchwarz` ps = (True, Schwarz)
  162. | sgl >= 90 = (True, Schneider)
  163. | Single `isSchwarz` ps = (False, Schwarz)
  164. | sgl <= 30 = (False, Schneider)
  165. | otherwise = (sgl > 60, Einfach)
  166. where (sgl, _) = count ps :: (Int, Int)
  167. -- | get result of game
  168. getResults :: Game -> Hand -> Piles -> Piles -> Result
  169. getResults game sglPlayer before after = Result afterGame score sglPoints teamPoints
  170. where (won, afterGame) = hasWon game after
  171. hand = skatCards before ++ (map toCard $ handCards sglPlayer before)
  172. (sglPoints, teamPoints) = count after
  173. gameScore = biddingScore afterGame hand
  174. score = if won then gameScore else (-2) * gameScore