Skat Engine und AI auf Haskell Basis
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

198 lignes
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