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.

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