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.

406 lignes
13KB

  1. {-# LANGUAGE NamedFieldPuns #-}
  2. {-# LANGUAGE TypeSynonymInstances #-}
  3. {-# LANGUAGE FlexibleInstances #-}
  4. {-# LANGUAGE FlexibleContexts #-}
  5. module AI.Rulebased (
  6. mkAIEnv
  7. ) where
  8. import Data.Ord
  9. import Data.Monoid ((<>))
  10. import Data.List
  11. import Control.Monad.State
  12. import Control.Monad.Reader
  13. import qualified Data.Map.Strict as M
  14. import Player
  15. import qualified Player.Utils as P
  16. import Pile
  17. import Card
  18. import Utils
  19. import Skat (Skat, modifyp, mkSkatEnv)
  20. import Operations
  21. data AIEnv = AIEnv { getTeam :: Team
  22. , getHand :: Hand
  23. , table :: [CardS Played]
  24. , fallen :: [CardS Played]
  25. , myHand :: [Card]
  26. , guess :: Guess
  27. , simulationDepth :: Int }
  28. deriving Show
  29. setTable :: [CardS Played] -> AIEnv -> AIEnv
  30. setTable tab env = env { table = tab }
  31. setHand :: [Card] -> AIEnv -> AIEnv
  32. setHand hand env = env { myHand = hand }
  33. setFallen :: [CardS Played] -> AIEnv -> AIEnv
  34. setFallen fallen env = env { fallen = fallen }
  35. setDepth :: Int -> AIEnv -> AIEnv
  36. setDepth depth env = env { simulationDepth = depth }
  37. modifyg :: MonadPlayer m => (Guess -> Guess) -> AI m ()
  38. modifyg f = modify g
  39. where g env@(AIEnv {guess}) = env { guess = f guess }
  40. type AI m = StateT AIEnv m
  41. instance MonadPlayer m => MonadPlayer (AI m) where
  42. trumpColour = lift $ trumpColour
  43. turnColour = lift $ turnColour
  44. showSkat = lift . showSkat
  45. instance MonadPlayerOpen m => MonadPlayerOpen (AI m) where
  46. showPiles = lift $ showPiles
  47. type Simulator m = ReaderT Piles (AI m)
  48. instance MonadPlayer m => MonadPlayer (Simulator m) where
  49. trumpColour = lift $ trumpColour
  50. turnColour = lift $ turnColour
  51. showSkat = lift . showSkat
  52. instance MonadPlayer m => MonadPlayerOpen (Simulator m) where
  53. showPiles = ask
  54. runWithPiles :: MonadPlayer m
  55. => Piles -> Simulator m a -> AI m a
  56. runWithPiles ps sim = runReaderT sim ps
  57. instance Player AIEnv where
  58. team = getTeam
  59. hand = getHand
  60. chooseCard p table fallen hand = runStateT (do
  61. modify $ setTable table
  62. modify $ setHand hand
  63. modify $ setFallen fallen
  64. choose) p
  65. onCardPlayed p card = execStateT (do
  66. onPlayed card) p
  67. chooseCardOpen p = evalStateT chooseOpen p
  68. value :: Card -> Int
  69. value (Card Ace _) = 100
  70. value _ = 0
  71. data Option = H Hand
  72. | Skt
  73. deriving (Show, Eq, Ord)
  74. -- | possible card distributions
  75. type Guess = M.Map Card [Option]
  76. newGuess :: Guess
  77. newGuess = M.fromList l
  78. where l = map (\c -> (c, [H Hand1, H Hand2, H Hand3, Skt])) allCards
  79. hasBeenPlayed :: Card -> Guess -> Guess
  80. hasBeenPlayed card = M.delete card
  81. has :: Hand -> [Card] -> Guess -> Guess
  82. has hand cs = M.mapWithKey f
  83. where f card hands
  84. | card `elem` cs = [H hand]
  85. | otherwise = hands
  86. hasNoLonger :: MonadPlayer m => Hand -> Colour -> AI m ()
  87. hasNoLonger hand colour = do
  88. trCol <- trumpColour
  89. modifyg $ hasNoLonger_ trCol hand colour
  90. hasNoLonger_ :: Colour -> Hand -> Colour -> Guess -> Guess
  91. hasNoLonger_ trColour hand effCol = M.mapWithKey f
  92. where f card hands
  93. | effectiveColour trColour card == effCol && (H hand) `elem` hands = filter (/=H hand) hands
  94. | otherwise = hands
  95. isSkat :: [Card] -> Guess -> Guess
  96. isSkat cs = M.mapWithKey f
  97. where f card hands
  98. | card `elem` cs = [Skt]
  99. | otherwise = hands
  100. type Turn = (CardS Played, CardS Played, CardS Played)
  101. analyzeTurn :: MonadPlayer m => Turn -> AI m ()
  102. analyzeTurn (c1, c2, c3) = do
  103. modifyg (getCard c1 `hasBeenPlayed`)
  104. modifyg (getCard c2 `hasBeenPlayed`)
  105. modifyg (getCard c3 `hasBeenPlayed`)
  106. trCol <- trumpColour
  107. let turnCol = getColour $ getCard c1
  108. demanded = effectiveColour trCol (getCard c1)
  109. col2 = effectiveColour trCol (getCard c2)
  110. col3 = effectiveColour trCol (getCard c3)
  111. if col2 /= demanded
  112. then origin c2 `hasNoLonger` demanded
  113. else return ()
  114. if col3 /= demanded
  115. then origin c3 `hasNoLonger` demanded
  116. else return ()
  117. type Distribution = ([Card], [Card], [Card], [Card])
  118. toPiles :: [CardS Played] -> Distribution -> Piles
  119. toPiles table (h1, h2, h3, skt) = Piles (cs1 ++ cs2 ++ cs3) table ss
  120. where cs1 = map (putAt Hand1) h1
  121. cs2 = map (putAt Hand2) h2
  122. cs3 = map (putAt Hand3) h3
  123. ss = map (putAt SkatP) skt
  124. distributions :: Guess -> (Int, Int, Int, Int) -> [Distribution]
  125. distributions guess nos =
  126. helper (sortBy (comparing $ length . snd) $ M.toList guess) nos
  127. where helper [] _ = []
  128. helper ((c, hs):[]) ns = map fst (distr c hs ns)
  129. helper ((c, hs):gs) ns =
  130. let dsWithNs = distr c hs ns
  131. go (d, ns') = map (d <>) (helper gs ns')
  132. in concatMap go dsWithNs
  133. distr card hands (n1, n2, n3, n4) =
  134. let f card (H Hand1) =
  135. (([card], [], [], []), (n1+1, n2, n3, n4))
  136. f card (H Hand2) =
  137. (([], [card], [], []), (n1, n2+1, n3, n4))
  138. f card (H Hand3) =
  139. (([], [], [card], []), (n1, n2, n3+1, n4))
  140. f card Skt =
  141. (([], [], [], [card]), (n1, n2, n3, n4+1))
  142. isOk (H Hand1) = n1 < cardsPerHand
  143. isOk (H Hand2) = n2 < cardsPerHand
  144. isOk (H Hand3) = n3 < cardsPerHand
  145. isOk Skt = n4 < 2
  146. in filterMap isOk (f card) hands
  147. cardsPerHand = (length guess - 2) `div` 3
  148. simplify :: Int -> [Distribution] -> [Distribution]
  149. simplify 10 ds = nubBy is789Variation ds
  150. simplify _ ds = ds
  151. is789Variation :: Distribution -> Distribution -> Bool
  152. is789Variation (ha1, ha2, ha3, sa) (hb1, hb2, hb3, sb) =
  153. f ha1 hb1 && f ha2 hb2 && f ha3 hb3 && f sa sb
  154. where f cs1 cs2
  155. | n789s cs1 /= n789s cs2 = False
  156. | otherwise = and (zipCs (c789s cs1) (c789s cs2))
  157. zipCs :: [[Card]] -> [[Card]] -> [Bool]
  158. zipCs xs ys = zipWith g xs ys
  159. c789s :: [Card] -> [[Card]]
  160. c789s cs = groupBy (grouping getColour) $
  161. sortBy (comparing getColour) $
  162. filter ((==(0 :: Int)) . count) cs
  163. n789s :: [Card] -> [Card]
  164. n789s cs = filter ((/=(0 :: Int)) . count) cs
  165. g :: [a] -> [b] -> Bool
  166. g xs ys = length xs == length ys
  167. onPlayed :: MonadPlayer m => CardS Played -> AI m ()
  168. onPlayed c = do
  169. liftIO $ print c
  170. modifyg (getCard c `hasBeenPlayed`)
  171. trCol <- trumpColour
  172. turnCol <- turnColour
  173. let col = effectiveColour trCol (getCard c)
  174. case turnCol of
  175. Just demanded -> if col /= demanded
  176. then origin c `hasNoLonger` demanded else return ()
  177. Nothing -> return ()
  178. choose :: MonadPlayer m => AI m Card
  179. choose = do
  180. handCards <- gets myHand
  181. table <- gets table
  182. case length table of
  183. 0 -> if length handCards >= 7
  184. then chooseLead
  185. else chooseStatistic
  186. n -> chooseStatistic
  187. chooseStatistic :: MonadPlayer m => AI m Card
  188. chooseStatistic = do
  189. h <- gets getHand
  190. handCards <- gets myHand
  191. let depth = case length handCards of
  192. 0 -> 0
  193. 1 -> 1
  194. -- simulate whole game
  195. 2 -> 2
  196. 3 -> 3
  197. -- simulate only partially
  198. 4 -> 2
  199. 5 -> 1
  200. 6 -> 1
  201. 7 -> 1
  202. 8 -> 1
  203. 9 -> 1
  204. 10 -> 1
  205. modify $ setDepth depth
  206. guess__ <- gets guess
  207. self <- get
  208. maySkat <- showSkat self
  209. let guess_ = (hand self `has` handCards) guess__
  210. guess = case maySkat of
  211. Just cs -> (cs `isSkat`) guess_
  212. Nothing -> guess_
  213. table <- gets table
  214. let ns = case length table of
  215. 0 -> (0, 0, 0, 0)
  216. 1 -> (-1, 0, -1, 0)
  217. 2 -> (0, 0, -1, 0)
  218. let dis = distributions guess ns
  219. disNo = length dis
  220. piless = map (toPiles table) dis
  221. limit = if depth == 1 && length table == 2
  222. then 1
  223. else min 10000 $ disNo `div` 2
  224. liftIO $ putStrLn $ "possible distrs " ++ show disNo
  225. vals <- M.toList <$> foldWithLimit limit runOnPiles M.empty piless
  226. liftIO $ print vals
  227. return $ fst $ maximumBy (comparing snd) vals
  228. foldWithLimit :: Monad m
  229. => Int
  230. -> (M.Map k Int -> a -> m (M.Map k Int))
  231. -> M.Map k Int
  232. -> [a]
  233. -> m (M.Map k Int)
  234. foldWithLimit _ _ start [] = return start
  235. foldWithLimit limit f start (x:xs) = do
  236. case M.size (M.filter (>=limit) start) of
  237. 0 -> do m <- f start x
  238. foldWithLimit limit f m xs
  239. _ -> return start
  240. runOnPiles :: MonadPlayer m
  241. => M.Map Card Int -> Piles -> AI m (M.Map Card Int)
  242. runOnPiles m ps = do
  243. c <- runWithPiles ps chooseOpen
  244. return $ M.insertWith (+) c 1 m
  245. chooseOpen :: (MonadState AIEnv m, MonadPlayerOpen m) => m Card
  246. chooseOpen = do
  247. piles <- showPiles
  248. hand <- gets getHand
  249. let myCards = handCards hand piles
  250. possible <- filterM (P.isAllowed myCards) myCards
  251. case length myCards of
  252. 0 -> do
  253. liftIO $ print hand
  254. liftIO $ print piles
  255. error "no cards left to choose from"
  256. 1 -> return $ head myCards
  257. _ -> chooseSimulating
  258. chooseSimulating :: (MonadState AIEnv m, MonadPlayerOpen m)
  259. => m Card
  260. chooseSimulating = do
  261. piles <- showPiles
  262. hand <- gets getHand
  263. let myCards = handCards hand piles
  264. possible <- filterM (P.isAllowed myCards) myCards
  265. case possible of
  266. [card] -> return card
  267. cs -> do
  268. results <- mapM simulate cs
  269. let both = zip results cs
  270. best = maximumBy (comparing fst) both
  271. return $ snd best
  272. simulate :: (MonadState AIEnv m, MonadPlayerOpen m)
  273. => Card -> m Int
  274. simulate card = do
  275. -- retrieve all relevant info
  276. piles <- showPiles
  277. turnCol <- turnColour
  278. trumpCol <- trumpColour
  279. myTeam <- gets getTeam
  280. myHand <- gets getHand
  281. depth <- gets simulationDepth
  282. let newDepth = depth - 1
  283. -- create a virtual env with 3 ai players
  284. ps = Players
  285. (PL $ mkAIEnv Team Hand1 newDepth)
  286. (PL $ mkAIEnv Team Hand2 newDepth)
  287. (PL $ mkAIEnv Single Hand3 newDepth)
  288. env = mkSkatEnv piles turnCol trumpCol ps
  289. -- simulate the game after playing the given card
  290. (sgl, tm) <- liftIO $ evalStateT (do
  291. modifyp $ playCard card
  292. turnGeneric playOpen depth (next myHand)) env
  293. let v = if myTeam == Single then (sgl, tm) else (tm, sgl)
  294. -- put the value into context for when not the whole game is
  295. -- simulated
  296. predictValue v
  297. predictValue :: (MonadState AIEnv m, MonadPlayerOpen m)
  298. => (Int, Int) -> m Int
  299. predictValue (own, others) = do
  300. hand <- gets getHand
  301. piles <- showPiles
  302. let cs = handCards hand piles
  303. pot <- potential cs
  304. return $ own + pot
  305. potential :: (MonadState AIEnv m, MonadPlayerOpen m)
  306. => [Card] -> m Int
  307. potential cs = do
  308. tr <- trumpColour
  309. let trs = filter (isTrump tr) cs
  310. value = count cs
  311. positions <- filter (==0) <$> mapM position cs
  312. return $ length trs * 10 + value + length positions * 5
  313. position :: (MonadState AIEnv m, MonadPlayer m)
  314. => Card -> m Int
  315. position card = do
  316. tr <- trumpColour
  317. guess <- gets guess
  318. let effCol = effectiveColour tr card
  319. l = M.toList guess
  320. cs = filterMap ((==effCol) . effectiveColour tr . fst) fst l
  321. csInd = zip [0..] cs
  322. Just (pos, _) = find ((== card) . snd) csInd
  323. return pos
  324. leadPotential :: (MonadState AIEnv m, MonadPlayer m)
  325. => Card -> m Int
  326. leadPotential card = do
  327. pos <- position card
  328. isTr <- P.isTrump card
  329. let value = count card
  330. case pos of
  331. 0 -> return value
  332. _ -> return $ -value
  333. chooseLead :: (MonadState AIEnv m, MonadPlayer m) => m Card
  334. chooseLead = do
  335. cards <- gets myHand
  336. possible <- filterM (P.isAllowed cards) cards
  337. pots <- mapM leadPotential possible
  338. return $ snd $ maximumBy (comparing fst) (zip pots possible)
  339. mkAIEnv :: Team -> Hand -> Int -> AIEnv
  340. mkAIEnv tm h depth = AIEnv tm h [] [] [] newGuess depth
  341. -- | TESTING VARS
  342. aienv :: AIEnv
  343. aienv = AIEnv Single Hand3 [] [] [] newGuess 10
  344. testguess :: Guess
  345. testguess = isSkat (take 2 $ drop 10 allCards)
  346. $ Hand3 `has` (take 10 allCards) $ m
  347. where l = map (\c -> (c, [H Hand1, H Hand2, H Hand3, Skt])) (take 32 allCards)
  348. m = M.fromList l
  349. testds :: [Distribution]
  350. testds = distributions testguess (0, 0, 0, 0)