Skat Engine und AI auf Haskell Basis
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

406 строки
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)