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.

429 lignes
14KB

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