| @@ -7,6 +7,8 @@ | |||||
| module Skat.AI.Base where | module Skat.AI.Base where | ||||
| import Data.Set (Set) | |||||
| import qualified Data.Set as S | |||||
| import System.Random (Random) | import System.Random (Random) | ||||
| import qualified System.Random as Rand | import qualified System.Random as Rand | ||||
| import Control.Monad.State | import Control.Monad.State | ||||
| @@ -49,14 +51,20 @@ class (MonadIO m, Show t, Show v, Show p, MonadGame t l v p m) => PlayableGame t | |||||
| class Choose t m | m -> t where | class Choose t m | m -> t where | ||||
| choose :: m t | choose :: m t | ||||
| class MonadRandom m where | |||||
| class Monad m => MonadRandom m where | |||||
| random :: Random a => m a | random :: Random a => m a | ||||
| randomR :: Random a => (a, a) -> m a | |||||
| chooser :: [a] -> m a | chooser :: [a] -> m a | ||||
| chooser [] = error "chooser: empty list" | |||||
| chooser os = (os!!) <$> randomR (0, length os - 1) | |||||
| chooserS :: Set a -> m a | |||||
| chooserS set | |||||
| | S.null set = error "chooserS: empty set" | |||||
| | otherwise = (`S.elemAt` set) <$> randomR (0, S.size set - 1) | |||||
| instance MonadRandom IO where | instance MonadRandom IO where | ||||
| random = Rand.randomIO | random = Rand.randomIO | ||||
| chooser [] = error "chooser: empty list" | |||||
| chooser os = (os!!) <$> Rand.randomRIO (0, length os -1) | |||||
| randomR = Rand.randomRIO | |||||
| instance MonadRandom (State Rand.StdGen) where | instance MonadRandom (State Rand.StdGen) where | ||||
| random = do | random = do | ||||
| @@ -64,9 +72,8 @@ instance MonadRandom (State Rand.StdGen) where | |||||
| let (a, gen') = Rand.random gen | let (a, gen') = Rand.random gen | ||||
| put gen' | put gen' | ||||
| return a | return a | ||||
| chooser [] = error "chooser: empty list" | |||||
| chooser os = do | |||||
| randomR bds = do | |||||
| gen <- get | gen <- get | ||||
| let (a, gen') = Rand.randomR (0, length os -1) gen | |||||
| let (a, gen') = Rand.randomR bds gen | |||||
| put gen' | put gen' | ||||
| return (os !! a) | |||||
| return a | |||||
| @@ -6,6 +6,7 @@ module Skat.AI.Games.Skat.Guess where | |||||
| import GHC.Generics (Generic, Generic1) | import GHC.Generics (Generic, Generic1) | ||||
| import Data.Ord | import Data.Ord | ||||
| import Data.Aeson | |||||
| import Data.Monoid ((<>)) | import Data.Monoid ((<>)) | ||||
| import Data.List | import Data.List | ||||
| import Data.Set (Set) | import Data.Set (Set) | ||||
| @@ -30,7 +31,7 @@ import Control.DeepSeq | |||||
| data Option = H Hand | data Option = H Hand | ||||
| | Skt | | Skt | ||||
| deriving (Show, Eq, Ord, Generic, NFData) | |||||
| deriving (Show, Eq, Ord, Generic, NFData, ToJSON) | |||||
| type Guess = Map Card (Set Option) | type Guess = Map Card (Set Option) | ||||
| @@ -69,6 +70,22 @@ hasNoLonger trump hand effCol = M.mapWithKey f | |||||
| S.filter (/=H hand) hands | S.filter (/=H hand) hands | ||||
| | otherwise = hands | | otherwise = hands | ||||
| observe :: Trump -> Maybe TurnColour -> [CardS Played] -> Guess -> Guess | |||||
| observe _ Nothing _ guess = guess | |||||
| observe trpCol (Just turnCol) tbl oldGuess = foldr f oldGuess tbl | |||||
| where f :: CardS Played -> Guess -> Guess | |||||
| f c g = let col = effectiveColour trpCol (toCard c) | |||||
| in if col /= turnCol | |||||
| then hasNoLonger trpCol (uorigin $ getPile c) turnCol g | |||||
| else g | |||||
| observeS :: Guess -> Skat Guess | |||||
| observeS guess = do | |||||
| trpCol <- trump | |||||
| turnCol <- gets Skat.turnColour | |||||
| tbl <- getp tableCards | |||||
| pure $ observe trpCol turnCol tbl guess | |||||
| isSkat :: [Card] -> Guess -> Guess | isSkat :: [Card] -> Guess -> Guess | ||||
| isSkat cs = M.mapWithKey f | isSkat cs = M.mapWithKey f | ||||
| where f card hands | where f card hands | ||||
| @@ -96,6 +113,36 @@ choosen = choosen2 | |||||
| smplguess :: Guess | smplguess :: Guess | ||||
| smplguess = Hand1 `hasOnly` [(Card Seven Diamonds)..(Card Eight Hearts)] $! newGuess | smplguess = Hand1 `hasOnly` [(Card Seven Diamonds)..(Card Eight Hearts)] $! newGuess | ||||
| smplguess2 :: Guess | |||||
| smplguess2 = M.fromList | |||||
| [ ( Card Seven Diamonds, S.fromList [H Hand2, H Hand3] ) | |||||
| , ( Card Eight Hearts, S.fromList [H Hand2, H Hand1] ) | |||||
| , ( Card Nine Spades, S.fromList [H Hand1, H Hand2] ) | |||||
| , ( Card Nine Diamonds, S.fromList [Skt] ) | |||||
| , ( Card Eight Diamonds, S.fromList [Skt] ) | |||||
| ] | |||||
| smplguess3 :: Guess | |||||
| smplguess3 = M.fromList | |||||
| [ (Card Nine Clubs, S.fromList [Skt]) | |||||
| , (Card Queen Clubs, S.fromList [Skt]) | |||||
| , (Card Ten Hearts, S.fromList [H Hand2,H Hand3]) | |||||
| , (Card Ace Diamonds, S.fromList [H Hand2]) | |||||
| ] | |||||
| smplguess4 :: Guess | |||||
| smplguess4 = M.fromList | |||||
| [ (Card Seven Spades, S.fromList [H Hand1]) | |||||
| , (Card Nine Spades, S.fromList [H Hand1]) | |||||
| , (Card Eight Spades, S.fromList [H Hand2]) | |||||
| , (Card Queen Diamonds, S.fromList [H Hand3]) | |||||
| , (Card Ace Diamonds, S.fromList [H Hand3]) | |||||
| , (Card King Clubs, S.fromList [H Hand2,H Hand3]) | |||||
| , (Card Ace Clubs, S.fromList [H Hand2,H Hand3]) | |||||
| , (Card Nine Clubs, S.fromList [Skt]) | |||||
| , (Card Queen Clubs, S.fromList [Skt]) | |||||
| ] | |||||
| distributions2 :: Guess -> (Int, Int, Int, Int) -> [Distribution] | distributions2 :: Guess -> (Int, Int, Int, Int) -> [Distribution] | ||||
| distributions2 !guess1 !(n1, n2, n3, nskt) = do | distributions2 !guess1 !(n1, n2, n3, nskt) = do | ||||
| let h1cards = M.keys $!! M.filter (H Hand1 `elem`) guess1 | let h1cards = M.keys $!! M.filter (H Hand1 `elem`) guess1 | ||||
| @@ -142,7 +189,7 @@ randomChoice options = do | |||||
| g Skt = n4 > 0 | g Skt = n4 > 0 | ||||
| opts = S.toList $ S.filter g options | opts = S.toList $ S.filter g options | ||||
| --when (null opts) $ error "randomChoice: after filtering options are empty" | --when (null opts) $ error "randomChoice: after filtering options are empty" | ||||
| option <- lift $ chooser opts | |||||
| option <- if null opts then (error ("randomChoice: opts empty, " ++ show options ++ " " ++ show (n1,n2,n3,n4))) else lift (chooser opts) | |||||
| let (n1', n2', n3', n4') = case option of | let (n1', n2', n3', n4') = case option of | ||||
| H Hand1 -> (n1-1, n2, n3, n4) | H Hand1 -> (n1-1, n2, n3, n4) | ||||
| H Hand2 -> (n1, n2-1, n3, n4) | H Hand2 -> (n1, n2-1, n3, n4) | ||||
| @@ -163,9 +210,73 @@ randomGuess guess (n1, n2, n3, n4) = (flip evalStateT) ( cardsPerHand + n1 | |||||
| let opts = M.findWithDefault (error "findWithDefault") card g | let opts = M.findWithDefault (error "findWithDefault") card g | ||||
| o <- randomChoice opts | o <- randomChoice opts | ||||
| pure $ M.insert card (S.singleton o) g | pure $ M.insert card (S.singleton o) g | ||||
| choosern :: (Eq a, Monad m, MonadRandom m) => Int -> [a] -> m [a] | |||||
| choosern 0 _ = pure [] | |||||
| choosern _ [] = error "chooseRn: list is empty and n /= 0" | |||||
| choosern !n !os = do | |||||
| o <- chooser os | |||||
| let !os' = delete o os | |||||
| rest <- choosern (n-1) os' | |||||
| pure $ o : rest | |||||
| choosernS :: (Ord a, Monad m, MonadRandom m) => Int -> Set a -> m (Set a) | |||||
| choosernS 0 _ = pure S.empty | |||||
| choosernS !n !os | |||||
| | S.size os == 0 = error "chooseRn: list is empty and n /= 0" | |||||
| | otherwise = do | |||||
| o <- chooserS os | |||||
| let !os' = S.delete o os | |||||
| rest <- choosernS (n-1) os' | |||||
| pure $ S.insert o rest | |||||
| snd3 :: (a,b,c) -> b | |||||
| snd3 (a,b,c) = b | |||||
| randomDistr2 :: (MonadRandom m, Monad m) => Guess -> (Int, Int, Int, Int) -> m Distribution | |||||
| randomDistr2 guess1 (n1, n2, n3, _) = do | |||||
| let h1cs = M.keysSet $!! M.filter (H Hand1 `S.member`) guess1 | |||||
| h2cs = M.keysSet $!! M.filter (H Hand2 `S.member`) guess1 | |||||
| h3cs = M.keysSet $!! M.filter (H Hand3 `S.member`) guess1 | |||||
| skcs = M.keysSet $!! M.filter (Skt `S.member`) guess1 | |||||
| priority = M.filter ((==1) . S.size) guess1 | |||||
| predist = M.foldrWithKey | |||||
| (\card opts dist -> M.insertWith (++) (S.elemAt 0 opts) [card] dist) | |||||
| M.empty | |||||
| priority | |||||
| banned = M.keysSet priority | |||||
| pots = sortBy (comparing $ \(_, cs, n) -> length cs - n) | |||||
| $ [ (H Hand1, h1cs, nh1 - length (M.findWithDefault [] (H Hand1) predist)) | |||||
| , (H Hand2, h2cs, nh2 - length (M.findWithDefault [] (H Hand2) predist)) | |||||
| , (H Hand3, h3cs, nh3 - length (M.findWithDefault [] (H Hand3) predist)) | |||||
| , (Skt , skcs, nh4 - length (M.findWithDefault [] Skt predist)) | |||||
| ] | |||||
| (dist, _) <- foldM f (predist, banned) pots | |||||
| return ( M.findWithDefault (error "randomDistr: missing option Hand1") (H Hand1) dist | |||||
| , M.findWithDefault (error "randomDistr: missing option Hand2") (H Hand2) dist | |||||
| , M.findWithDefault (error "randomDistr: missing option Hand3") (H Hand3) dist | |||||
| , M.findWithDefault (error "randomDistr: missing option Skt") Skt dist | |||||
| ) | |||||
| where cardsPerHand = (length guess1-2-n1-n2-n3) `div` 3 | |||||
| nh1 = cardsPerHand + n1 | |||||
| nh2 = cardsPerHand + n2 | |||||
| nh3 = cardsPerHand + n3 | |||||
| nh4 = 2 | |||||
| f (dist, banned) (option, cards, n) = do | |||||
| let available = S.filter (not . (`S.member` banned)) cards | |||||
| cs <- if S.size available < n | |||||
| then error ("Not enough options available: wanted " ++ show n ++ " for " ++ show option ++ " and got " ++ show (length available) ++ ", " ++ show guess1 ++ " with " ++ show (n1, n2, n3)) | |||||
| else choosernS n available | |||||
| let dist' = M.insertWith (++) option (S.toList cs) dist | |||||
| pure (dist', S.union banned cs) | |||||
| randomDistr :: (MonadRandom m, Monad m) => Guess -> (Int, Int, Int, Int) -> m Distribution | randomDistr :: (MonadRandom m, Monad m) => Guess -> (Int, Int, Int, Int) -> m Distribution | ||||
| randomDistr guess (n1, n2, n3, n4) = (flip evalStateT) ( cardsPerHand + n1 | |||||
| randomDistr = randomDistr2 | |||||
| randomDistr1 :: (MonadRandom m, Monad m) => Guess -> (Int, Int, Int, Int) -> m Distribution | |||||
| randomDistr1 guess (n1, n2, n3, n4) = (flip evalStateT) ( cardsPerHand + n1 | |||||
| , cardsPerHand + n2 | , cardsPerHand + n2 | ||||
| , cardsPerHand + n3 | , cardsPerHand + n3 | ||||
| , 2 + n4 | , 2 + n4 | ||||
| @@ -1,3 +1,4 @@ | |||||
| {-# LANGUAGE OverloadedStrings #-} | |||||
| {-# LANGUAGE MultiParamTypeClasses #-} | {-# LANGUAGE MultiParamTypeClasses #-} | ||||
| {-# LANGUAGE BlockArguments #-} | {-# LANGUAGE BlockArguments #-} | ||||
| {-# LANGUAGE TypeSynonymInstances #-} | {-# LANGUAGE TypeSynonymInstances #-} | ||||
| @@ -9,9 +10,11 @@ | |||||
| {-# LANGUAGE StandaloneDeriving #-} | {-# LANGUAGE StandaloneDeriving #-} | ||||
| {-# LANGUAGE ImportQualifiedPost #-} | {-# LANGUAGE ImportQualifiedPost #-} | ||||
| {-# LANGUAGE UndecidableInstances #-} | {-# LANGUAGE UndecidableInstances #-} | ||||
| {-# LANGUAGE DeriveGeneric #-} | |||||
| module Skat.AI.MonteCarlo where | module Skat.AI.MonteCarlo where | ||||
| import GHC.Generics | |||||
| import Control.Monad.State | import Control.Monad.State | ||||
| import Control.Exception (assert) | import Control.Exception (assert) | ||||
| import Control.Monad.Fail | import Control.Monad.Fail | ||||
| @@ -31,6 +34,7 @@ import System.Random (Random) | |||||
| import qualified System.Random as Rand | import qualified System.Random as Rand | ||||
| import Text.Printf | import Text.Printf | ||||
| import Data.List.Split | import Data.List.Split | ||||
| import Data.Aeson hiding (Value) | |||||
| import Skat.AI.Base hiding (simulate) | import Skat.AI.Base hiding (simulate) | ||||
| import qualified Skat as S | import qualified Skat as S | ||||
| @@ -48,6 +52,19 @@ type SimCount = Int | |||||
| data Tree t s = Leaf s Bool (WinCount, SimCount) | data Tree t s = Leaf s Bool (WinCount, SimCount) | ||||
| | Node s Bool (WinCount, SimCount) [Tree t s] | | Node s Bool (WinCount, SimCount) [Tree t s] | ||||
| | Pending s t | | Pending s t | ||||
| deriving (Generic) | |||||
| instance (ToJSON t, ToJSON s) => ToJSON (Tree t s) where | |||||
| toJSON x@Leaf{} = object [ "state" .= toJSON (treestate x) | |||||
| , "valuation" .= toJSON (valuation x) | |||||
| ] | |||||
| toJSON x@(Node _ _ _ children) = object [ "state" .= toJSON (treestate x) | |||||
| , "valuation" .= toJSON (valuation x) | |||||
| , "children" .= toJSON children | |||||
| ] | |||||
| toJSON x@(Pending _ t) = object [ "valuation" .= ("pending" :: String) | |||||
| , "turn" .= toJSON t | |||||
| ] | |||||
| simruns :: Tree t s -> SimCount | simruns :: Tree t s -> SimCount | ||||
| simruns (Leaf _ _ d) = snd d | simruns (Leaf _ _ d) = snd d | ||||
| @@ -106,8 +123,10 @@ valuetonum v | |||||
| | v == tie = 0.5 | | v == tie = 0.5 | ||||
| -} | -} | ||||
| {- | |||||
| restoint :: (Player p, Value v) => p -> v -> Float | restoint :: (Player p, Value v) => p -> v -> Float | ||||
| restoint p v = tonum $ if maxing p then v else invert v | restoint p v = tonum $ if maxing p then v else invert v | ||||
| -} | |||||
| {- | {- | ||||
| updateval :: (Player p, Value d) => p -> [d] -> (WinCount, SimCount) -> (WinCount, SimCount) | updateval :: (Player p, Value d) => p -> [d] -> (WinCount, SimCount) -> (WinCount, SimCount) | ||||
| @@ -133,12 +152,13 @@ montecarlo (Pending state turn) = do | |||||
| let currentTeam = current state | let currentTeam = current state | ||||
| state' = execute turn state | state' = execute turn state | ||||
| -- objectively get a final score of random playout (independent of perspective) | -- objectively get a final score of random playout (independent of perspective) | ||||
| values <- replicateM 100 (simulate state') | |||||
| let tr = if maxing (current state) then id else invert | |||||
| values <- replicateM 1000 (simulate state') | |||||
| let --tr = if maxing (current state) then id else invert | |||||
| tr = id | |||||
| vs = fmap (tonum . tr) values | vs = fmap (tonum . tr) values | ||||
| n = sum vs | n = sum vs | ||||
| --let v = if maxing (current state') then value else invert value | --let v = if maxing (current state') then value else invert value | ||||
| let val = (n, 100) | |||||
| let val = (n, 1000) | |||||
| pure $ Leaf state' False val | pure $ Leaf state' False val | ||||
| montecarlo (Leaf state terminal d) | montecarlo (Leaf state terminal d) | ||||
| | terminal || length ms == 0 = pure $ Leaf state True d | | terminal || length ms == 0 = pure $ Leaf state True d | ||||
| @@ -152,20 +172,22 @@ montecarlo n@(Node state _ d children) | |||||
| in pure $ Node state True d' children | in pure $ Node state True d' children | ||||
| | otherwise = do | | otherwise = do | ||||
| let myruns = snd d | let myruns = snd d | ||||
| cmp c = if isterminal c then -1 else selectcoeff myruns $ valuation c | |||||
| cmp c | |||||
| | isterminal c = -1 | |||||
| | otherwise = selectcoeff (maxing $ current state) myruns $ valuation c | |||||
| (idx, bestChild) = | (idx, bestChild) = | ||||
| maximumBy (comparing $ cmp . snd) $ zipWith (,) [0..] children | maximumBy (comparing $ cmp . snd) $ zipWith (,) [0..] children | ||||
| updated <- montecarlo bestChild | updated <- montecarlo bestChild | ||||
| let cs = updateAt idx children updated | let cs = updateAt idx children updated | ||||
| newSimRuns = simruns updated - simruns bestChild + snd d | newSimRuns = simruns updated - simruns bestChild + snd d | ||||
| diff = wins updated - wins bestChild | diff = wins updated - wins bestChild | ||||
| diff2 = | |||||
| if newSimRuns == snd d then 0 | |||||
| else | |||||
| if current state == current (treestate updated) | |||||
| then diff | |||||
| else fromIntegral (simruns updated) - diff | |||||
| newWins = diff2 + fst d | |||||
| --diff2 = | |||||
| -- if newSimRuns == snd d then 0 | |||||
| -- else | |||||
| -- if current state == current (treestate updated) | |||||
| -- then diff | |||||
| -- else fromIntegral (simruns updated) - diff | |||||
| newWins = diff + fst d | |||||
| --return $ trace ("updating node " ++ show diff2 ++ "\n" ++ show updated ++ "\n" ++ show bestChild) (Node state False (newWins, newSimRuns) cs) | --return $ trace ("updating node " ++ show diff2 ++ "\n" ++ show updated ++ "\n" ++ show bestChild) (Node state False (newWins, newSimRuns) cs) | ||||
| return $ Node state False (newWins, newSimRuns) cs | return $ Node state False (newWins, newSimRuns) cs | ||||
| @@ -189,15 +211,17 @@ bestmove (Node s _ _ cs) = treestate $ selection (comparing $ rate . valuation) | |||||
| mxing = maxing . current $ s | mxing = maxing . current $ s | ||||
| selection = if mxing then maximumBy else minimumBy | selection = if mxing then maximumBy else minimumBy | ||||
| -} | -} | ||||
| bestmove :: Tree t s -> s | |||||
| bestmove :: (HasGameState t p d s, Player p) => Tree t s -> s | |||||
| bestmove (Leaf s _ _) = s | bestmove (Leaf s _ _) = s | ||||
| bestmove (Node s _ _ cs) = treestate $ maximumBy (comparing $ rate . valuation) cs | |||||
| bestmove (Node s _ _ cs) = treestate $ choice (comparing $ rate . valuation) cs | |||||
| where rate (w, s) = w / fromIntegral s | where rate (w, s) = w / fromIntegral s | ||||
| choice = if maxing (current s) then maximumBy else minimumBy | |||||
| selectcoeff :: SimCount -> (WinCount, SimCount) -> Float | |||||
| selectcoeff _ (_, 0) = 10000000 | |||||
| selectcoeff t (w, s) = w / fromIntegral s + explorationParam * sqrt (log (fromIntegral t) / fromIntegral s) | |||||
| selectcoeff :: Bool -> SimCount -> (WinCount, SimCount) -> Float | |||||
| selectcoeff _ _ (_, 0) = 10000000 | |||||
| selectcoeff m t (w, s) = w' / fromIntegral s + explorationParam * sqrt (log (fromIntegral t) / fromIntegral s) | |||||
| where explorationParam = sqrt 2 | where explorationParam = sqrt 2 | ||||
| w' = if m then w else fromIntegral s - w | |||||
| reevaluate :: Tree t s -> (WinCount, SimCount) | reevaluate :: Tree t s -> (WinCount, SimCount) | ||||
| reevaluate tree | reevaluate tree | ||||
| @@ -218,9 +242,10 @@ reevaluateminmax tree | |||||
| (Node state _ _ children) -> | (Node state _ _ children) -> | ||||
| let vals = fmap ((\(w, s) -> w / fromIntegral s) . valuation) children | let vals = fmap ((\(w, s) -> w / fromIntegral s) . valuation) children | ||||
| -- m = maxing . current $ state | -- m = maxing . current $ state | ||||
| childrenMaxing = all (maxing . current . treestate) children | |||||
| --childrenMaxing = all (maxing . current . treestate) children | |||||
| selfMaxing = maxing . current $ state | selfMaxing = maxing . current $ state | ||||
| newval = if childrenMaxing /= selfMaxing then 1 - maximum vals else maximum vals | |||||
| choice = if selfMaxing then maximum else minimum | |||||
| newval = choice vals | |||||
| in (newval, 1) | in (newval, 1) | ||||
| --playCLI :: (MonadFail m, Read t, Choose t m, PlayableGame t l v p m) => m () | --playCLI :: (MonadFail m, Read t, Choose t m, PlayableGame t l v p m) => m () | ||||
| @@ -1,3 +1,4 @@ | |||||
| {-# LANGUAGE OverloadedStrings #-} | |||||
| {-# LANGUAGE MultiParamTypeClasses #-} | {-# LANGUAGE MultiParamTypeClasses #-} | ||||
| {-# LANGUAGE TypeSynonymInstances #-} | {-# LANGUAGE TypeSynonymInstances #-} | ||||
| {-# LANGUAGE FlexibleInstances #-} | {-# LANGUAGE FlexibleInstances #-} | ||||
| @@ -5,20 +6,26 @@ | |||||
| {-# LANGUAGE FunctionalDependencies #-} | {-# LANGUAGE FunctionalDependencies #-} | ||||
| {-# LANGUAGE TupleSections #-} | {-# LANGUAGE TupleSections #-} | ||||
| {-# LANGUAGE ScopedTypeVariables #-} | {-# LANGUAGE ScopedTypeVariables #-} | ||||
| {-# LANGUAGE DeriveGeneric #-} | |||||
| module Skat.AI.Skat where | module Skat.AI.Skat where | ||||
| import Data.String | |||||
| import System.IO | |||||
| import GHC.Generics | |||||
| import Control.Monad.State | import Control.Monad.State | ||||
| import Control.Exception (assert) | import Control.Exception (assert) | ||||
| import Control.Monad.Fail | import Control.Monad.Fail | ||||
| import Control.Monad.Writer | import Control.Monad.Writer | ||||
| import Data.Ord | import Data.Ord | ||||
| import Data.Aeson hiding (Value) | |||||
| import Text.Read (readMaybe) | import Text.Read (readMaybe) | ||||
| import Data.List (maximumBy, sortBy) | import Data.List (maximumBy, sortBy) | ||||
| import Debug.Trace | import Debug.Trace | ||||
| import Data.Map.Strict (Map) | import Data.Map.Strict (Map) | ||||
| import qualified Data.Map.Strict as Map | import qualified Data.Map.Strict as Map | ||||
| import qualified System.Random as Rand | import qualified System.Random as Rand | ||||
| import qualified Data.ByteString.Lazy.Char8 as BS8 | |||||
| import System.IO.Unsafe | import System.IO.Unsafe | ||||
| @@ -61,7 +68,14 @@ data SkatState = SkatState { skatEnv :: S.SkatEnv | |||||
| , self :: S.Hand | , self :: S.Hand | ||||
| , guess :: Guess | , guess :: Guess | ||||
| } | } | ||||
| deriving Show | |||||
| deriving (Show, Generic) | |||||
| instance ToJSON SkatState where | |||||
| toJSON state = object [ "guess" .= toJSON (guess state) | |||||
| , "table" .= toJSON (S.tableCards $ S.piles $ skatEnv state) | |||||
| , "won_single" .= toJSON (S.wonCards S.Single $ S.piles $ skatEnv state) | |||||
| , "won_team" .= toJSON (S.wonCards S.Team $ S.piles $ skatEnv state) | |||||
| ] | |||||
| instance Draw SkatState where | instance Draw SkatState where | ||||
| draw = show . S.tableCards . S.piles . skatEnv | draw = show . S.tableCards . S.piles . skatEnv | ||||
| @@ -129,6 +143,9 @@ data Turn = Turn { turnStartingEnv :: S.SkatEnv | |||||
| , turnCard :: S.Card } | , turnCard :: S.Card } | ||||
| deriving Show | deriving Show | ||||
| instance ToJSON Turn where | |||||
| toJSON turn = object [ "turn_card" .= turnCard turn ] | |||||
| instance Draw Turn where | instance Draw Turn where | ||||
| draw = show . turnCard | draw = show . turnCard | ||||
| @@ -140,9 +157,17 @@ instance HasGameState Turn Bool Float SkatState where | |||||
| monteevaluate s = let (sgl, tm) = ev S.countGame (skatEnv s) | monteevaluate s = let (sgl, tm) = ev S.countGame (skatEnv s) | ||||
| in if sgl > tm then 1.0 else 0.0 --fromIntegral sgl / (fromIntegral $ sgl + tm) | in if sgl > tm then 1.0 else 0.0 --fromIntegral sgl / (fromIntegral $ sgl + tm) | ||||
| execute turn state = | execute turn state = | ||||
| let newEnv = ex (S.play_ card) env | |||||
| let tbl = ev (S.getp S.tableCards) env | |||||
| curhand = S.currentHand env | |||||
| trpCol :: S.Trump | |||||
| trpCol = ev (S.getTrump <$> gets S.skatGame) env | |||||
| turnCol = ev (gets S.turnColour) env | |||||
| observed = observe trpCol turnCol (card':tbl) (guess state) | |||||
| guess' = card `hasBeenPlayed` observed | |||||
| card' = S.CardS card (S.P curhand) | |||||
| newEnv = ex (S.play_ card) env | |||||
| in state { skatEnv = newEnv | in state { skatEnv = newEnv | ||||
| , guess = card `hasBeenPlayed` (guess state) | |||||
| , guess = guess' | |||||
| } | } | ||||
| where env = turnStartingEnv turn | where env = turnStartingEnv turn | ||||
| card = turnCard turn | card = turnCard turn | ||||
| @@ -202,10 +227,20 @@ playCLI n = do | |||||
| liftIO $ putStrLn "iterating" | liftIO $ putStrLn "iterating" | ||||
| s <- get | s <- get | ||||
| let tree = Leaf s False (0, 0) | let tree = Leaf s False (0, 0) | ||||
| t = runmonte n (foldM (\tree _ -> montecarlo tree) tree [1..100]) | |||||
| l = length $ guess s | |||||
| depth | |||||
| | l >= 26 = 15 | |||||
| | l >= 20 = 100 | |||||
| | l >= 14 = 2000 | |||||
| | otherwise = 5000 | |||||
| t = runmonte n (foldM (\tree _ -> montecarlo tree) tree [1..depth]) | |||||
| newstate = bestmove t | newstate = bestmove t | ||||
| --liftIO $ putStrLn $ visualise t | |||||
| json :: String | |||||
| json = BS8.unpack $ encode t | |||||
| liftIO $ print newstate | liftIO $ print newstate | ||||
| --liftIO $ withFile "tree.json" WriteMode $ \handle -> | |||||
| -- hPutStrLn handle json | |||||
| --liftIO $ putStrLn $ visualise t | |||||
| put newstate | put newstate | ||||
| lift (put $ skatEnv newstate) | lift (put $ skatEnv newstate) | ||||
| else do | else do | ||||
| @@ -215,7 +250,11 @@ playCLI n = do | |||||
| lift $ play t | lift $ play t | ||||
| s <- get | s <- get | ||||
| env <- lift get | env <- lift get | ||||
| let s' = s { skatEnv = env } | |||||
| let guess' = (S.toCard t) `hasBeenPlayed` (guess s) | |||||
| observed <- lift $ observeS guess' | |||||
| let s' = s { skatEnv = env | |||||
| , guess = observed | |||||
| } | |||||
| put s' | put s' | ||||
| {- | {- | ||||
| showBoard | showBoard | ||||
| @@ -245,13 +284,13 @@ initSkatEnv n = | |||||
| let gen = Rand.mkStdGen n | let gen = Rand.mkStdGen n | ||||
| --cards = S.shuffle gen S.allCards | --cards = S.shuffle gen S.allCards | ||||
| --piles = S.distribute cards | --piles = S.distribute cards | ||||
| piles = S.cardDistr3 | |||||
| piles = S.cardDistr9 | |||||
| players = P.Players | players = P.Players | ||||
| (P.PL $ S.Stupid S.Single S.Hand1) | (P.PL $ S.Stupid S.Single S.Hand1) | ||||
| (P.PL $ S.Stupid S.Team S.Hand2) | (P.PL $ S.Stupid S.Team S.Hand2) | ||||
| (P.PL $ S.Stupid S.Team S.Hand3) | (P.PL $ S.Stupid S.Team S.Hand3) | ||||
| in S.SkatEnv { S.piles = piles | in S.SkatEnv { S.piles = piles | ||||
| , S.turnColour = Nothing | |||||
| , S.turnColour = Just (S.TurnColour S.Hearts) | |||||
| , S.skatGame = S.Colour S.Spades S.Einfach | , S.skatGame = S.Colour S.Spades S.Einfach | ||||
| , S.players = players | , S.players = players | ||||
| , S.currentHand = S.Hand1 | , S.currentHand = S.Hand1 | ||||
| @@ -153,6 +153,7 @@ modifierFactor Ouvert = 7 | |||||
| allTrumps :: Game -> [Card] | allTrumps :: Game -> [Card] | ||||
| allTrumps (Grand _) = jacks | allTrumps (Grand _) = jacks | ||||
| allTrumps (Colour col _) = jacks ++ [Card t col | t <- [Ace,Ten .. Seven] ] | allTrumps (Colour col _) = jacks ++ [Card t col | t <- [Ace,Ten .. Seven] ] | ||||
| allTrumps _ = [] | |||||
| jacks :: [Card] | jacks :: [Card] | ||||
| jacks = [ Card Jack Clubs, Card Jack Spades, Card Jack Hearts, Card Jack Diamonds ] | jacks = [ Card Jack Clubs, Card Jack Spades, Card Jack Hearts, Card Jack Diamonds ] | ||||
| @@ -33,7 +33,7 @@ data Type = Seven | |||||
| | Ten | | Ten | ||||
| | Ace | | Ace | ||||
| | Jack | | Jack | ||||
| deriving (Eq, Ord, Show, Enum, Read, Bounded, Generic, NFData) | |||||
| deriving (Eq, Ord, Show, Enum, Read, Bounded, Generic, NFData, ToJSON) | |||||
| data NullType = NSeven | data NullType = NSeven | ||||
| | NEight | | NEight | ||||
| @@ -57,7 +57,7 @@ data Colour = Diamonds | |||||
| | Hearts | | Hearts | ||||
| | Spades | | Spades | ||||
| | Clubs | | Clubs | ||||
| deriving (Eq, Ord, Show, Enum, Read, Bounded, Generic, NFData) | |||||
| deriving (Eq, Ord, Show, Enum, Read, Bounded, Generic, NFData, ToJSON) | |||||
| data Trump = TrumpColour Colour | data Trump = TrumpColour Colour | ||||
| | Jacks | | Jacks | ||||
| @@ -69,7 +69,7 @@ data TurnColour = TurnColour Colour | |||||
| deriving (Show, Eq) | deriving (Show, Eq) | ||||
| data Card = Card !Type !Colour | data Card = Card !Type !Colour | ||||
| deriving (Eq, Show, Ord, Read, Bounded, Generic) | |||||
| deriving (Eq, Show, Ord, Read, Bounded, Generic, ToJSONKey) | |||||
| getType :: Card -> Type | getType :: Card -> Type | ||||
| getType (Card t _) = t | getType (Card t _) = t | ||||
| @@ -53,7 +53,7 @@ instance ToJSON p => ToJSON (CardS p) where | |||||
| object ["card" .= card, "pile" .= pile] | object ["card" .= card, "pile" .= pile] | ||||
| data Hand = Hand1 | Hand2 | Hand3 | data Hand = Hand1 | Hand2 | Hand3 | ||||
| deriving (Show, Eq, Ord, Read, Enum, Bounded, Generic, NFData) | |||||
| deriving (Show, Eq, Ord, Read, Enum, Bounded, Generic, NFData, ToJSON) | |||||
| toInt :: Hand -> Int | toInt :: Hand -> Int | ||||
| toInt Hand1 = 1 | toInt Hand1 = 1 | ||||
| @@ -249,10 +249,10 @@ instance Serialize String [Trick] where | |||||
| cardDistr :: Piles | cardDistr :: Piles | ||||
| cardDistr = emptyPiles hand1 hand2 hand3 skt | cardDistr = emptyPiles hand1 hand2 hand3 skt | ||||
| where hand3 = [Card Ace Spades, Card Jack Diamonds, Card Jack Clubs, Card King Spades, | |||||
| where hand1 = [Card Ace Spades, Card Jack Diamonds, Card Jack Clubs, Card King Spades, | |||||
| Card Nine Spades, Card Ace Diamonds, Card Queen Diamonds, Card Ten Clubs, | Card Nine Spades, Card Ace Diamonds, Card Queen Diamonds, Card Ten Clubs, | ||||
| Card Eight Clubs, Card King Clubs] | Card Eight Clubs, Card King Clubs] | ||||
| hand1 = [Card Jack Spades, Card Jack Hearts, Card Ten Spades, Card Ace Hearts, Card Ten Hearts, | |||||
| hand3 = [Card Jack Spades, Card Jack Hearts, Card Ten Spades, Card Ace Hearts, Card Ten Hearts, | |||||
| Card Nine Hearts, Card Seven Clubs, Card Ace Clubs, Card King Diamonds, | Card Nine Hearts, Card Seven Clubs, Card Ace Clubs, Card King Diamonds, | ||||
| Card Ten Diamonds] | Card Ten Diamonds] | ||||
| hand2 = [Card Eight Spades, Card Queen Spades, Card Seven Spades, Card Seven Diamonds, | hand2 = [Card Eight Spades, Card Queen Spades, Card Seven Spades, Card Seven Diamonds, | ||||
| @@ -302,3 +302,31 @@ cardDistr6 = emptyPiles hand1 hand2 hand3 skt | |||||
| Card Seven Hearts, Card Eight Hearts, Card Queen Hearts | Card Seven Hearts, Card Eight Hearts, Card Queen Hearts | ||||
| ] | ] | ||||
| skt = [Card Nine Clubs, Card Queen Clubs] | skt = [Card Nine Clubs, Card Queen Clubs] | ||||
| cardDistr7 :: Piles | |||||
| cardDistr7 = emptyPiles hand1 hand2 hand3 skt | |||||
| where hand3 = [Card Eight Spades, Card Ace Clubs] | |||||
| hand1 = [Card Seven Spades, Card Nine Spades] | |||||
| hand2 = [Card Ace Hearts, Card Ten Clubs] | |||||
| skt = [Card Nine Clubs, Card Seven Clubs] | |||||
| cardDistr8 :: Piles | |||||
| cardDistr8 = emptyPiles hand1 hand2 hand3 skt | |||||
| where hand3 = [Card Ace Spades, Card Ace Clubs] | |||||
| hand1 = [Card Jack Spades, Card Seven Spades] | |||||
| hand2 = [Card Eight Hearts, Card King Clubs] | |||||
| skt = [Card Nine Clubs, Card Seven Clubs] | |||||
| cardDistr9 :: Piles | |||||
| cardDistr9 = makePiles hand1 hand2 hand3 tbl skt | |||||
| where hand1 = [Card Ace Spades, Card Jack Diamonds, Card Jack Clubs, Card King Spades, | |||||
| Card Nine Spades, Card Ace Diamonds, Card Queen Diamonds, Card Ten Clubs, | |||||
| Card Eight Clubs] | |||||
| hand3 = [Card Jack Spades, Card Jack Hearts, Card Ten Spades, Card Ten Hearts, | |||||
| Card Nine Hearts, Card Seven Clubs, Card King Diamonds, | |||||
| Card Ten Diamonds] | |||||
| hand2 = [Card Eight Spades, Card Seven Spades, Card Seven Diamonds, | |||||
| Card Seven Hearts, Card Eight Hearts, Card Queen Hearts, | |||||
| Card Nine Diamonds, Card Eight Diamonds] | |||||
| skt = [Card Nine Clubs, Card Queen Clubs] | |||||
| tbl = [CardS (Card Ace Hearts) (P Hand3), CardS (Card King Hearts) (P Hand2)] | |||||