mcts progress
This commit is contained in:
+14
-7
@@ -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"
|
randomR = Rand.randomRIO
|
||||||
chooser os = (os!!) <$> Rand.randomRIO (0, length os -1)
|
|
||||||
|
|
||||||
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"
|
randomR bds = do
|
||||||
chooser os = 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)
|
||||||
@@ -164,8 +211,72 @@ randomGuess guess (n1, n2, n3, n4) = (flip evalStateT) ( cardsPerHand + n1
|
|||||||
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
|
||||||
|
|||||||
+43
-18
@@ -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')
|
values <- replicateM 1000 (simulate state')
|
||||||
let tr = if maxing (current state) then id else invert
|
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 =
|
--diff2 =
|
||||||
if newSimRuns == snd d then 0
|
-- if newSimRuns == snd d then 0
|
||||||
else
|
-- else
|
||||||
if current state == current (treestate updated)
|
-- if current state == current (treestate updated)
|
||||||
then diff
|
-- then diff
|
||||||
else fromIntegral (simruns updated) - diff
|
-- else fromIntegral (simruns updated) - diff
|
||||||
newWins = diff2 + fst d
|
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 :: Bool -> SimCount -> (WinCount, SimCount) -> Float
|
||||||
selectcoeff _ (_, 0) = 10000000
|
selectcoeff _ _ (_, 0) = 10000000
|
||||||
selectcoeff t (w, s) = w / fromIntegral s + explorationParam * sqrt (log (fromIntegral t) / fromIntegral s)
|
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 ()
|
||||||
|
|||||||
+47
-8
@@ -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 ]
|
||||||
|
|||||||
+3
-3
@@ -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
|
||||||
|
|||||||
+31
-3
@@ -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)]
|
||||||
|
|||||||
Reference in New Issue
Block a user