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.

430 lignes
14KB

  1. {-# LANGUAGE NamedFieldPuns #-}
  2. {-# LANGUAGE TypeSynonymInstances #-}
  3. {-# LANGUAGE FlexibleInstances #-}
  4. {-# LANGUAGE FlexibleContexts #-}
  5. module AI.Rulebased (
  6. mkAIEnv, testds, simplify
  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. type Abstract = (Int, Int, Int, Int)
  156. abstract :: [Card] -> Abstract
  157. abstract cs = foldr f (0, 0, 0, 0) cs
  158. where f c (clubs, spades, hearts, diamonds) =
  159. let v = getID c in
  160. case getColour c of
  161. Diamonds -> (clubs, spades, hearts, diamonds + 1 + v*100)
  162. Hearts -> (clubs, spades, hearts + 1 + v*100, diamonds)
  163. Spades -> (clubs, spades + 1 + v*100, hearts, diamonds)
  164. Clubs -> (clubs + 1 + v*100, spades, hearts, diamonds)
  165. remove789s :: Hand -> [Distribution] -> M.Map (Abstract, Abstract)(Distribution, Int)
  166. remove789s hand ds = foldl' f M.empty ds
  167. where f cleaned d =
  168. let (c1, c2) = reduce hand d
  169. a = (abstract c1, abstract c2) in
  170. M.insertWith (\(oldD, n) _ -> (oldD, n+1)) a (d, 1) cleaned
  171. reduce Hand1 (_, h2, h3, _) = (h2, h3)
  172. reduce Hand2 (h1, _, h3, _) = (h1, h3)
  173. reduce Hand3 (h1, h2, _, _) = (h1, h2)
  174. simplify :: Hand -> [Distribution] -> M.Map Distribution Int
  175. simplify hand ds = M.foldl' f M.empty cleaned
  176. where cleaned = remove789s hand ds
  177. f m (d, n) = M.insert d n m
  178. onPlayed :: MonadPlayer m => CardS Played -> AI m ()
  179. onPlayed c = do
  180. liftIO $ print c
  181. modifyg (getCard c `hasBeenPlayed`)
  182. trCol <- trumpColour
  183. turnCol <- turnColour
  184. let col = effectiveColour trCol (getCard c)
  185. case turnCol of
  186. Just demanded -> if col /= demanded
  187. then origin c `hasNoLonger` demanded else return ()
  188. Nothing -> return ()
  189. choose :: MonadPlayer m => AI m Card
  190. choose = do
  191. handCards <- gets myHand
  192. table <- gets table
  193. case length table of
  194. 0 -> if length handCards >= 7
  195. then chooseLead
  196. else chooseStatistic
  197. n -> chooseStatistic
  198. chooseStatistic :: MonadPlayer m => AI m Card
  199. chooseStatistic = do
  200. h <- gets getHand
  201. handCards <- gets myHand
  202. let depth = case length handCards of
  203. 0 -> 0
  204. 1 -> 1
  205. -- simulate whole game
  206. 2 -> 2
  207. 3 -> 3
  208. -- simulate only partially
  209. 4 -> 2
  210. 5 -> 1
  211. 6 -> 1
  212. 7 -> 1
  213. 8 -> 1
  214. 9 -> 1
  215. 10 -> 1
  216. modify $ setDepth depth
  217. guess__ <- gets guess
  218. self <- get
  219. maySkat <- showSkat self
  220. let guess_ = (hand self `has` handCards) guess__
  221. guess = case maySkat of
  222. Just cs -> (cs `isSkat`) guess_
  223. Nothing -> guess_
  224. table <- gets table
  225. let ns = case length table of
  226. 0 -> (0, 0, 0, 0)
  227. 1 -> (-1, 0, -1, 0)
  228. 2 -> (0, 0, -1, 0)
  229. let realDis = distributions guess ns
  230. realDisNo = length realDis
  231. reducedDis = simplify Hand3 realDis
  232. reducedDisNo = length reducedDis
  233. piless = M.mapKeys (toPiles table) reducedDis
  234. limit = if depth == 1 && length table == 2
  235. then 1
  236. else min 10000 $ realDisNo `div` 2
  237. liftIO $ putStrLn $ "possible distrs without simp " ++ show realDisNo
  238. liftIO $ putStrLn $ "possible distrs " ++ show reducedDisNo
  239. vals <- M.toList <$> foldWithLimit limit runOnPiles M.empty (M.toList piless)
  240. liftIO $ print vals
  241. return $ fst $ maximumBy (comparing snd) vals
  242. foldWithLimit :: Monad m
  243. => Int
  244. -> (M.Map k Int -> a -> m (M.Map k Int))
  245. -> M.Map k Int
  246. -> [a]
  247. -> m (M.Map k Int)
  248. foldWithLimit _ _ start [] = return start
  249. foldWithLimit limit f start (x:xs) = do
  250. case M.size (M.filter (>=limit) start) of
  251. 0 -> do m <- f start x
  252. foldWithLimit limit f m xs
  253. _ -> return start
  254. runOnPiles :: MonadPlayer m
  255. => M.Map Card Int -> (Piles, Int) -> AI m (M.Map Card Int)
  256. runOnPiles m (ps, n) = do
  257. c <- runWithPiles ps chooseOpen
  258. return $ M.insertWith (+) c n m
  259. chooseOpen :: (MonadState AIEnv m, MonadPlayerOpen m) => m Card
  260. chooseOpen = do
  261. piles <- showPiles
  262. hand <- gets getHand
  263. let myCards = handCards hand piles
  264. possible <- filterM (P.isAllowed myCards) myCards
  265. case length myCards of
  266. 0 -> do
  267. liftIO $ print hand
  268. liftIO $ print piles
  269. error "no cards left to choose from"
  270. 1 -> return $ head myCards
  271. _ -> chooseSimulating
  272. chooseSimulating :: (MonadState AIEnv m, MonadPlayerOpen m)
  273. => m Card
  274. chooseSimulating = do
  275. piles <- showPiles
  276. hand <- gets getHand
  277. let myCards = handCards hand piles
  278. possible <- filterM (P.isAllowed myCards) myCards
  279. case possible of
  280. [card] -> return card
  281. cs -> do
  282. results <- mapM simulate cs
  283. let both = zip results cs
  284. best = maximumBy (comparing fst) both
  285. return $ snd best
  286. simulate :: (MonadState AIEnv m, MonadPlayerOpen m)
  287. => Card -> m Int
  288. simulate card = do
  289. -- retrieve all relevant info
  290. piles <- showPiles
  291. turnCol <- turnColour
  292. trumpCol <- trumpColour
  293. myTeam <- gets getTeam
  294. myHand <- gets getHand
  295. depth <- gets simulationDepth
  296. let newDepth = depth - 1
  297. -- create a virtual env with 3 ai players
  298. ps = Players
  299. (PL $ mkAIEnv Team Hand1 newDepth)
  300. (PL $ mkAIEnv Team Hand2 newDepth)
  301. (PL $ mkAIEnv Single Hand3 newDepth)
  302. env = mkSkatEnv piles turnCol trumpCol ps
  303. -- simulate the game after playing the given card
  304. (sgl, tm) <- liftIO $ evalStateT (do
  305. modifyp $ playCard card
  306. turnGeneric playOpen depth (next myHand)) env
  307. let v = if myTeam == Single then (sgl, tm) else (tm, sgl)
  308. -- put the value into context for when not the whole game is
  309. -- simulated
  310. predictValue v
  311. predictValue :: (MonadState AIEnv m, MonadPlayerOpen m)
  312. => (Int, Int) -> m Int
  313. predictValue (own, others) = do
  314. hand <- gets getHand
  315. piles <- showPiles
  316. let cs = handCards hand piles
  317. pot <- potential cs
  318. return $ own + pot
  319. potential :: (MonadState AIEnv m, MonadPlayerOpen m)
  320. => [Card] -> m Int
  321. potential cs = do
  322. tr <- trumpColour
  323. let trs = filter (isTrump tr) cs
  324. value = count cs
  325. positions <- filter (==0) <$> mapM position cs
  326. return $ length trs * 10 + value + length positions * 5
  327. position :: (MonadState AIEnv m, MonadPlayer m)
  328. => Card -> m Int
  329. position card = do
  330. tr <- trumpColour
  331. guess <- gets guess
  332. let effCol = effectiveColour tr card
  333. l = M.toList guess
  334. cs = filterMap ((==effCol) . effectiveColour tr . fst) fst l
  335. csInd = zip [0..] cs
  336. Just (pos, _) = find ((== card) . snd) csInd
  337. return pos
  338. leadPotential :: (MonadState AIEnv m, MonadPlayer m)
  339. => Card -> m Int
  340. leadPotential card = do
  341. pos <- position card
  342. isTr <- P.isTrump card
  343. let value = count card
  344. case pos of
  345. 0 -> return value
  346. _ -> return $ -value
  347. chooseLead :: (MonadState AIEnv m, MonadPlayer m) => m Card
  348. chooseLead = do
  349. cards <- gets myHand
  350. possible <- filterM (P.isAllowed cards) cards
  351. pots <- mapM leadPotential possible
  352. return $ snd $ maximumBy (comparing fst) (zip pots possible)
  353. mkAIEnv :: Team -> Hand -> Int -> AIEnv
  354. mkAIEnv tm h depth = AIEnv tm h [] [] [] newGuess depth
  355. -- | TESTING VARS
  356. aienv :: AIEnv
  357. aienv = AIEnv Single Hand3 [] [] [] newGuess 10
  358. testguess :: Guess
  359. testguess = isSkat (take 2 $ drop 10 cs)
  360. $ Hand3 `has` (take 10 cs) $ m
  361. where l = map (\c -> (c, [H Hand1, H Hand2, H Hand3, Skt])) (take 32 cs)
  362. m = M.fromList l
  363. cs = allCards
  364. testguess2 :: Guess
  365. testguess2 = isSkat (take 2 $ drop 6 cs)
  366. $ Hand3 `has` [head cs, head $ drop 5 cs] $ m
  367. where l = map (\c -> (c, [H Hand1, H Hand2, H Hand3, Skt])) cs
  368. m = M.fromList l
  369. cs = take 8 $ drop 8 allCards
  370. testds :: [Distribution]
  371. testds = distributions testguess (0, 0, 0, 0)
  372. testds2 :: [Distribution]
  373. testds2 = distributions testguess2 (0, 0, 0, 0)