some optimizations

This commit is contained in:
2020-03-01 21:35:23 +01:00
parent 5846a22d8a
commit 672746e302
18 changed files with 336 additions and 192 deletions
+12 -15
View File
@@ -19,7 +19,12 @@ import Skat.AI.Rulebased
import Skat.AI.Minmax (playCLI) import Skat.AI.Minmax (playCLI)
main :: IO () main :: IO ()
main = testAI 10 main = testMinmax 10
testMinmax :: Int -> IO ()
testMinmax n = do
let acs = repeat playSkat
sequence_ (take n acs)
testAI :: Int -> IO () testAI :: Int -> IO ()
testAI n = do testAI n = do
@@ -71,14 +76,11 @@ shuffledEnv2 = do
return $ SkatEnv (distribute cards) Nothing Spades pls2 Hand1 return $ SkatEnv (distribute cards) Nothing Spades pls2 Hand1
env2 :: SkatEnv env2 :: SkatEnv
env2 = SkatEnv piles Nothing Spades playersExamp Hand1 env2 = SkatEnv piles Nothing Hearts playersExamp Hand2
where hand1 = [Card Seven Clubs, Card King Clubs, Card Ace Clubs, Card Queen Diamonds] where hand1 = [Card Eight Hearts, Card Queen Hearts, Card Ace Clubs, Card Queen Diamonds]
hand2 = [Card Seven Hearts, Card King Hearts, Card Ace Hearts, Card Queen Spades] hand2 = [Card Seven Hearts, Card King Hearts, Card Ten Hearts, Card Queen Spades]
hand3 = [Card Seven Spades, Card King Spades, Card Ace Spades, Card Queen Clubs] hand3 = [Card Seven Spades, Card King Spades, Card Ace Spades, Card Queen Clubs]
h1 = map (putAt Hand1) hand1 piles = emptyPiles hand1 hand2 hand3 []
h2 = map (putAt Hand2) hand2
h3 = map (putAt Hand3) hand3
piles = Piles (h1 ++ h2 ++ h3) [] []
env3 :: SkatEnv env3 :: SkatEnv
env3 = SkatEnv piles Nothing Diamonds pls2 Hand3 env3 = SkatEnv piles Nothing Diamonds pls2 Hand3
@@ -91,11 +93,7 @@ env3 = SkatEnv piles Nothing Diamonds pls2 Hand3
hand3 = [ Card Jack Hearts, Card Jack Spades, Card Ten Spades, Card Ace Spades, Card Eight Diamonds hand3 = [ Card Jack Hearts, Card Jack Spades, Card Ten Spades, Card Ace Spades, Card Eight Diamonds
, Card Queen Diamonds, Card Ten Diamonds, Card Ten Hearts, Card Queen Hearts, Card King Hearts ] , Card Queen Diamonds, Card Ten Diamonds, Card Ten Hearts, Card Queen Hearts, Card King Hearts ]
skat = [ Card Queen Clubs, Card Queen Spades] skat = [ Card Queen Clubs, Card Queen Spades]
h1 = map (putAt Hand1) hand1 piles = emptyPiles hand1 hand2 hand3 skat
h2 = map (putAt Hand2) hand2
h3 = map (putAt Hand3) hand3
skt = map (putAt SkatP) skat
piles = Piles (h1 ++ h2 ++ h3) [] skt
runWebSocketServer :: IO () runWebSocketServer :: IO ()
runWebSocketServer = do runWebSocketServer = do
@@ -110,5 +108,4 @@ application pending = do
putStrLn $ BS.unpack msg putStrLn $ BS.unpack msg
playSkat :: IO () playSkat :: IO ()
playSkat = do playSkat = void $ (flip runStateT) env3 playCLI
void $ (flip runStateT) env3 playCLI
+31
View File
@@ -0,0 +1,31 @@
module TestEnvs where
import Skat
import Skat.Card
import Skat.Pile
import Skat.Player
import Skat.AI.Stupid
pls2 :: Players
pls2 = Players
(PL $ Stupid Team Hand1)
(PL $ Stupid Team Hand2)
(PL $ Stupid Single Hand3)
env3 :: SkatEnv
env3 = SkatEnv piles Nothing Diamonds pls2 Hand3
where hand1 = [ Card Jack Diamonds, Card Jack Clubs, Card Nine Spades, Card King Spades
, Card Seven Diamonds, Card Nine Diamonds, Card Seven Clubs, Card Eight Clubs
, Card Ten Clubs, Card Eight Hearts ]
hand2 = [ Card Seven Spades, Card Eight Spades, Card Seven Hearts, Card Nine Hearts
, Card Ace Hearts, Card King Diamonds, Card Ace Diamonds, Card Nine Clubs
, Card King Clubs, Card Ace Clubs ]
hand3 = [ Card Jack Hearts, Card Jack Spades, Card Ten Spades, Card Ace Spades, Card Eight Diamonds
, Card Queen Diamonds, Card Ten Diamonds, Card Ten Hearts, Card Queen Hearts, Card King Hearts ]
skat = [ Card Queen Clubs, Card Queen Spades]
piles = emptyPiles hand1 hand2 hand3 skat
shuffledEnv2 :: IO SkatEnv
shuffledEnv2 = do
cards <- shuffleCards
return $ SkatEnv (distribute cards) Nothing Spades pls2 Hand1
+3
View File
@@ -33,6 +33,7 @@ dependencies:
- parallel - parallel
- containers - containers
- case-insensitive - case-insensitive
- vector
library: library:
source-dirs: src source-dirs: src
@@ -45,6 +46,7 @@ executables:
- -threaded - -threaded
- -rtsopts - -rtsopts
- -with-rtsopts=-N - -with-rtsopts=-N
- -O2
dependencies: dependencies:
- skat - skat
@@ -56,5 +58,6 @@ tests:
- -threaded - -threaded
- -rtsopts - -rtsopts
- -with-rtsopts=-N - -with-rtsopts=-N
- -O2
dependencies: dependencies:
- skat - skat
+7 -3
View File
@@ -4,7 +4,7 @@ cabal-version: 1.12
-- --
-- see: https://github.com/sol/hpack -- see: https://github.com/sol/hpack
-- --
-- hash: 589f4321e3ce9847f3a53afb14e0fa9eaa1b98b3fc7386eac20f8fae7f7b6bf7 -- hash: 3f130a9bf454b63893b6f1958214229a75ad6916b19eb7bb6797a19f0f14dd3e
name: skat name: skat
version: 0.1.0.1 version: 0.1.0.1
@@ -60,16 +60,18 @@ library
, random , random
, split , split
, text , text
, vector
, websockets , websockets
default-language: Haskell2010 default-language: Haskell2010
executable skat-exe executable skat-exe
main-is: Main.hs main-is: Main.hs
other-modules: other-modules:
TestEnvs
Paths_skat Paths_skat
hs-source-dirs: hs-source-dirs:
app app
ghc-options: -threaded -rtsopts -with-rtsopts=-N ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2
build-depends: build-depends:
aeson aeson
, base >=4.7 && <5 , base >=4.7 && <5
@@ -84,6 +86,7 @@ executable skat-exe
, skat , skat
, split , split
, text , text
, vector
, websockets , websockets
default-language: Haskell2010 default-language: Haskell2010
@@ -94,7 +97,7 @@ test-suite skat-test
Paths_skat Paths_skat
hs-source-dirs: hs-source-dirs:
test test
ghc-options: -threaded -rtsopts -with-rtsopts=-N ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2
build-depends: build-depends:
aeson aeson
, base >=4.7 && <5 , base >=4.7 && <5
@@ -109,5 +112,6 @@ test-suite skat-test
, skat , skat
, split , split
, text , text
, vector
, websockets , websockets
default-language: Haskell2010 default-language: Haskell2010
+3 -5
View File
@@ -7,6 +7,7 @@ module Skat where
import Control.Monad.State import Control.Monad.State
import Control.Monad.Reader import Control.Monad.Reader
import Data.List import Data.List
import Data.Vector (Vector)
import Skat.Card import Skat.Card
import Skat.Pile import Skat.Pile
@@ -52,13 +53,10 @@ setCurrentHand hand sk = sk { currentHand = hand }
mkSkatEnv :: Piles -> Maybe Colour -> Colour -> Players -> Hand -> SkatEnv mkSkatEnv :: Piles -> Maybe Colour -> Colour -> Players -> Hand -> SkatEnv
mkSkatEnv = SkatEnv mkSkatEnv = SkatEnv
allowedCards :: Skat [Card] allowedCards :: Skat [CardS Owner]
allowedCards = do allowedCards = do
curHand <- gets currentHand curHand <- gets currentHand
pls <- gets players pls <- gets players
turnCol <- gets turnColour turnCol <- gets turnColour
trumpCol <- gets trumpColour trumpCol <- gets trumpColour
ps <- gets piles getp $ allowed curHand trumpCol turnCol
let p = P.player pls curHand
cards = handCards curHand ps
return $ filter (isAllowed trumpCol turnCol cards) cards
+1 -1
View File
@@ -19,7 +19,7 @@ instance Player Human where
trumpCol <- trumpColour trumpCol <- trumpColour
turnCol <- turnColour turnCol <- turnColour
let possible = filter (isAllowed trumpCol turnCol hand) hand let possible = filter (isAllowed trumpCol turnCol hand) hand
c <- liftIO $ askIO (map getCard table) possible hand c <- liftIO $ askIO (map getCard table) (map toCard possible) (map toCard hand)
return $ (c, p) return $ (c, p)
askIO :: [Card] -> [Card] -> [Card] -> IO Card askIO :: [Card] -> [Card] -> [Card] -> IO Card
+61 -41
View File
@@ -10,18 +10,20 @@ module Skat.AI.Minmax (
) where ) where
import Control.Monad.State import Control.Monad.State
import Control.Exception (assert)
import Control.Monad.Fail import Control.Monad.Fail
import Data.Ord import Data.Ord
import Text.Read (readMaybe) import Text.Read (readMaybe)
import Data.List (minimumBy, maximumBy) import Data.List (maximumBy, sortBy)
import Debug.Trace import Debug.Trace
import qualified Skat as S import qualified Skat as S
import qualified Skat.Card as S import qualified Skat.Card as S
import qualified Skat.Operations as S import qualified Skat.Operations as S
import qualified Skat.Pile as S import qualified Skat.Pile as S
import qualified Skat.Player as S import qualified Skat.Player as S hiding (trumpColour, turnColour)
import qualified Skat.Render as S import qualified Skat.Render as S
--import TestEnvs (env3, shuffledEnv2)
debug :: Bool debug :: Bool
debug = False debug = False
@@ -34,15 +36,15 @@ class (Ord v, Eq v) => Value v where
class Player p where class Player p where
maxing :: p -> Bool maxing :: p -> Bool
class (Monad m, Value v, Player p, Eq t) => MonadGame t v p m | m -> t, m -> p, m -> v where class (Traversable l, Monad m, Value v, Player p, Eq t) => MonadGame t l v p m | m -> t, m -> p, m -> v, m -> l where
currentPlayer :: m p currentPlayer :: m p
turns :: m [t] turns :: m (l t)
play :: t -> m () play :: t -> m ()
simulate :: t -> m a -> m a simulate :: t -> m a -> m a
evaluate :: m v evaluate :: m v
over :: m Bool over :: m Bool
class (MonadIO m, Show t, Show v, Show p, MonadGame t v p m) => PlayableGame t v p m | m -> t, m -> p, m -> v where class (MonadIO m, Show t, Show v, Show p, MonadGame t l v p m) => PlayableGame t l v p m | m -> t, m -> p, m -> v where
showTurns :: m () showTurns :: m ()
showBoard :: m () showBoard :: m ()
askTurn :: m (Maybe t) askTurn :: m (Maybe t)
@@ -59,25 +61,50 @@ instance Value Int where
win = 120 win = 120
loss = -120 loss = -120
instance MonadGame S.Card Int S.PL S.Skat where instance MonadGame (S.CardS S.Owner) [] Int S.PL S.Skat where
currentPlayer = do currentPlayer = do
hand <- gets S.currentHand hand <- gets S.currentHand
pls <- gets S.players pls <- gets S.players
return $ S.player pls hand return $! S.player pls hand
turns = S.allowedCards turns = S.allowedCards
--player <- currentPlayer
--trCol <- gets S.trumpColour
--return $! if maxing player
-- then sortBy (optimalTeam trCol) cards
-- else sortBy (optimalSingle trCol) cards
play = S.play_ play = S.play_
simulate card action = do simulate card action = do
--oldCurrent <- gets S.currentHand
--oldTurnCol <- gets S.turnColour
backup <- get backup <- get
play card play card
--oldWinner <- currentPlayer
res <- action res <- action
--S.undo_ card oldCurrent oldTurnCol (S.team oldWinner)
put backup put backup
return res return $! res
over = ((==0) . length) <$> S.allowedCards over = ((==0) . length) <$!> S.allowedCards
evaluate = do evaluate = do
player <- currentPlayer player <- currentPlayer
piles <- gets S.piles piles <- gets S.piles
let (sgl, tm) = S.count piles let (sgl, tm) = S.count piles
return $ (if maxing player then tm - sgl else sgl - tm) return $! (if maxing player then tm - sgl else sgl - tm)
potentialByType :: S.Type -> Int
potentialByType S.Ace = 11
potentialByType S.Jack = 10
potentialByType S.Ten = 4
potentialByType S.Seven = 7
potentialByType S.Eight = 7
potentialByType S.Nine = 7
potentialByType S.Queen = 5
potentialByType S.King = 5
optimalSingle :: S.Colour -> S.Card -> S.Card -> Ordering
optimalSingle trCol (S.Card t1 _) (S.Card t2 _) = (comparing potentialByType) t2 t1
optimalTeam :: S.Colour -> S.Card -> S.Card -> Ordering
optimalTeam trCol (S.Card t1 _) (S.Card t2 _) = (comparing potentialByType) t2 t1
-- TIC TAC TOE implementation -- TIC TAC TOE implementation
@@ -106,7 +133,7 @@ data GameState = GameState { getBoard :: [TicTacToe]
instance Player Bool where instance Player Bool where
maxing = id maxing = id
instance Monad m => MonadGame Int WinLossTie Bool (StateT GameState m) where instance Monad m => MonadGame Int [] WinLossTie Bool (StateT GameState m) where
currentPlayer = gets getCurrent currentPlayer = gets getCurrent
turns = do turns = do
board <- gets getBoard board <- gets getBoard
@@ -123,7 +150,7 @@ instance Monad m => MonadGame Int WinLossTie Bool (StateT GameState m) where
play turn play turn
res <- action res <- action
put backup put backup
return res return $! res
evaluate = do evaluate = do
board <- gets getBoard board <- gets getBoard
current <- currentPlayer current <- currentPlayer
@@ -162,45 +189,37 @@ updateAt :: Int -> [a] -> a -> [a]
updateAt n xs y = map f $ zip [0..] xs updateAt n xs y = map f $ zip [0..] xs
where f (i, x) = if i == n then y else x where f (i, x) = if i == n then y else x
minmax :: (MonadIO m, Show v, Show t, Show p, Value v, Eq t, Player p, MonadGame t v p m) minmax :: (MonadIO m, Show v, Show t, Show p, Value v, Eq t, Player p, MonadGame t l v p m)
=> Int => Int
-> t -> t
-> v -> v
-> v -> v
-> m (t, v) -> m (t, v)
minmax depth turn alpha beta = (flip evalStateT) (alpha, beta) $ do minmax depth turn_ alpha beta = (flip evalStateT) (alpha, beta) $ do
gameOver <- lift over gameOver <- lift over
-- if last step or game is over then evaluate situation -- if last step or game is over then evaluate situation
if depth == 0 || gameOver then do if depth == 0 || gameOver then (turn_,) <$> lift evaluate
val <- lift evaluate
when debug $ liftIO $ putStrLn $ "evaluation: " ++ show val
return (turn, val)
else do else do
when debug $ liftIO $ putStrLn $ "depth " ++ show depth
-- generate a list of possible turns -- generate a list of possible turns
currentlyMaxing <- maxing <$> lift currentPlayer currentlyMaxing <- maxing <$> lift currentPlayer
availableTurns <- lift turns availableTurns <- lift turns
(alpha, beta) <- get (alpha, beta) <- get
-- try every turn, StateT wraps current best turn and current max value -- try every turn, StateT wraps current best turn and current max value
(flip execStateT) (undefined, alpha) $ forM_ availableTurns $ \turn -> do (flip execStateT) (turn_, alpha) $ forM_ availableTurns $ \turn -> do
currentMax <- gets snd currentMax <- gets snd
when debug $ liftIO $ putStrLn $ "simulating " ++ show turn ++ " with max " ++ show currentMax
++ " and beta " ++ show beta
--when (currentMax >= beta && debug) $ liftIO $ putStrLn "beta cutoff"
-- beta cutoff -- beta cutoff
unless (currentMax >= beta) $ do unless (currentMax >= beta) $ do
--unless False $ do value <- lift $! lift $! simulate turn $! do
value <- lift $ lift $ simulate turn $ step currentlyMaxing beta currentMax nextMaxing <- maxing <$!> currentPlayer
when debug $ liftIO $ putStrLn $ "value " ++ show value if nextMaxing /= currentlyMaxing
then (invert . snd) <$!> minmax (depth-1) turn (invert beta) (invert currentMax)
else snd <$!> minmax (depth-1) turn currentMax beta
when (value > currentMax) (put (turn, value)) when (value > currentMax) (put (turn, value))
where step currentlyMaxing beta currentMax = do
nextMaxing <- maxing <$> currentPlayer
if nextMaxing /= currentlyMaxing
then (invert . snd) <$> minmax (depth-1) turn (invert beta) (invert currentMax)
else snd <$> minmax (depth-1) turn currentMax beta
choose :: (MonadIO m, Show v, Show t, Show p, Value v, Eq t, Player p, MonadGame t v p m) => m t choose :: (MonadIO m, Show v, Show t, Show p, Value v, Eq t, Player p, MonadGame t l v p m)
choose = fst <$> minmax 10 undefined loss win => Int
-> m t
choose depth = fst <$> minmax depth (error "choose") loss win
emptyBoard :: [TicTacToe] emptyBoard :: [TicTacToe]
emptyBoard = [Toe, Toe, Toe, Toe, Toe, Toe, Toe, Toe, Toe] emptyBoard = [Toe, Toe, Toe, Toe, Toe, Toe, Toe, Toe, Toe]
@@ -223,7 +242,7 @@ printOptions opts = print9x9 pr
| n `elem` opts = putStr (show n) >> putStr " " | n `elem` opts = putStr (show n) >> putStr " "
| otherwise = putStr " " | otherwise = putStr " "
instance MonadIO m => PlayableGame Int WinLossTie Bool (StateT GameState m) where instance MonadIO m => PlayableGame Int [] WinLossTie Bool (StateT GameState m) where
showBoard = do showBoard = do
board <- gets getBoard board <- gets getBoard
liftIO $ printBoard board liftIO $ printBoard board
@@ -239,7 +258,7 @@ instance MonadIO m => PlayableGame Int WinLossTie Bool (StateT GameState m) wher
askTurn = readMaybe <$> liftIO getLine askTurn = readMaybe <$> liftIO getLine
showTurn _ = return () showTurn _ = return ()
instance PlayableGame S.Card Int S.PL S.Skat where instance PlayableGame (S.CardS S.Owner) [] Int S.PL S.Skat where
showBoard = do showBoard = do
liftIO $ putStrLn "" liftIO $ putStrLn ""
table <- S.getp S.tableCards table <- S.getp S.tableCards
@@ -249,7 +268,7 @@ instance PlayableGame S.Card Int S.PL S.Skat where
cards <- turns cards <- turns
player <- currentPlayer player <- currentPlayer
liftIO $ print player liftIO $ print player
liftIO $ S.render (S.sortRender cards) liftIO $ S.render cards
winner = do winner = do
piles <- gets S.piles piles <- gets S.piles
pls <- gets S.players pls <- gets S.players
@@ -259,7 +278,7 @@ instance PlayableGame S.Card Int S.PL S.Skat where
return $ Just $ head winners return $ Just $ head winners
askTurn = do askTurn = do
cards <- turns cards <- turns
let sorted = S.sortRender cards let sorted = cards
input <- liftIO getLine input <- liftIO getLine
case readMaybe input of case readMaybe input of
Just n -> if n >= 0 && n < length sorted then return $ Just (sorted !! n) Just n -> if n >= 0 && n < length sorted then return $ Just (sorted !! n)
@@ -269,19 +288,20 @@ instance PlayableGame S.Card Int S.PL S.Skat where
player <- currentPlayer player <- currentPlayer
liftIO $ putStrLn $ show player ++ " plays " ++ show card liftIO $ putStrLn $ show player ++ " plays " ++ show card
playCLI :: (MonadFail m, Read t, PlayableGame t v p m) => m () playCLI :: (MonadFail m, Read t, PlayableGame t l v p m) => m ()
playCLI = do playCLI = do
gameOver <- over gameOver <- over
if gameOver if gameOver
then announceWinner then announceWinner
else do else do
showBoard when debug showBoard
current <- currentPlayer current <- currentPlayer
turn <- if not (maxing current) then readTurn else choose turn <- choose 10
showTurn turn when debug $ showTurn turn
play turn play turn
playCLI playCLI
where where
readTurn :: (MonadFail m, Read t, PlayableGame t l v p m) => m t
readTurn = do readTurn = do
options <- turns options <- turns
showTurns showTurns
+6 -4
View File
@@ -6,6 +6,7 @@ module Skat.AI.Online where
import Control.Monad.Reader import Control.Monad.Reader
import Data.Aeson import Data.Aeson
import Data.Maybe
import qualified Data.ByteString.Lazy.Char8 as BS import qualified Data.ByteString.Lazy.Char8 as BS
import Skat.Player import Skat.Player
@@ -52,15 +53,16 @@ instance MonadPlayer m => MonadPlayer (Online a m) where
turnColour = lift $ turnColour turnColour = lift $ turnColour
showSkat = lift . showSkat showSkat = lift . showSkat
choose :: (Communicator c, MonadPlayer m) => [CardS Played] -> [Card] -> Online c m Card choose :: HasCard a => (Communicator c, MonadPlayer m) => [CardS Played] -> [a] -> Online c m Card
choose table hand = do choose table hand' = do
let hand = map toCard hand'
query (BS.unpack $ encode $ ChooseQuery hand table) query (BS.unpack $ encode $ ChooseQuery hand table)
r <- response r <- response
case decode (BS.pack r) of case decode (BS.pack r) of
Just (ChosenResponse card) -> do Just (ChosenResponse card) -> do
allowed <- P.isAllowed hand card allowed <- P.isAllowed hand card
if card `elem` hand && allowed then return card else choose table hand if card `elem` hand && allowed then return card else choose table hand'
Nothing -> choose table hand Nothing -> choose table hand'
cardPlayed :: (Communicator c, MonadPlayer m) => CardS Played -> Online c m () cardPlayed :: (Communicator c, MonadPlayer m) => CardS Played -> Online c m ()
cardPlayed card = query (BS.unpack $ encode $ CardPlayedQuery card) cardPlayed card = query (BS.unpack $ encode $ CardPlayedQuery card)
+28 -20
View File
@@ -19,7 +19,7 @@ import qualified Data.Map.Strict as M
import Skat.Player import Skat.Player
import qualified Skat.Player.Utils as P import qualified Skat.Player.Utils as P
import Skat.Pile import Skat.Pile hiding (isSkat)
import Skat.Card import Skat.Card
import Skat.Utils import Skat.Utils
import Skat (Skat, modifyp, mkSkatEnv) import Skat (Skat, modifyp, mkSkatEnv)
@@ -81,7 +81,7 @@ instance Player AIEnv where
hand = getHand hand = getHand
chooseCard p table fallen hand = runStateT (do chooseCard p table fallen hand = runStateT (do
modify $ setTable table modify $ setTable table
modify $ setHand hand modify $ setHand (map toCard hand)
modify $ setFallen fallen modify $ setFallen fallen
choose) p choose) p
onCardPlayed p card = execStateT (do onCardPlayed p card = execStateT (do
@@ -142,20 +142,16 @@ analyzeTurn (c1, c2, c3) = do
col2 = effectiveColour trCol (getCard c2) col2 = effectiveColour trCol (getCard c2)
col3 = effectiveColour trCol (getCard c3) col3 = effectiveColour trCol (getCard c3)
if col2 /= demanded if col2 /= demanded
then origin c2 `hasNoLonger` demanded then uorigin (getPile c2) `hasNoLonger` demanded
else return () else return ()
if col3 /= demanded if col3 /= demanded
then origin c3 `hasNoLonger` demanded then uorigin (getPile c3) `hasNoLonger` demanded
else return () else return ()
type Distribution = ([Card], [Card], [Card], [Card]) type Distribution = ([Card], [Card], [Card], [Card])
toPiles :: [CardS Played] -> Distribution -> Piles toPiles :: [CardS Played] -> Distribution -> Piles
toPiles table (h1, h2, h3, skt) = Piles (cs1 ++ cs2 ++ cs3) table ss toPiles table (h1, h2, h3, skt) = makePiles h1 h2 h3 table skt
where cs1 = map (putAt Hand1) h1
cs2 = map (putAt Hand2) h2
cs3 = map (putAt Hand3) h3
ss = map (putAt SkatP) skt
compareGuess :: (Card, [Option]) -> (Card, [Option]) -> Ordering compareGuess :: (Card, [Option]) -> (Card, [Option]) -> Ordering
compareGuess (c1, ops1) (c2, ops2) compareGuess (c1, ops1) (c2, ops2)
@@ -227,7 +223,7 @@ onPlayed c = do
let col = effectiveColour trCol (getCard c) let col = effectiveColour trCol (getCard c)
case turnCol of case turnCol of
Just demanded -> if col /= demanded Just demanded -> if col /= demanded
then origin c `hasNoLonger` demanded else return () then uorigin (getPile c) `hasNoLonger` demanded else return ()
Nothing -> return () Nothing -> return ()
choose :: MonadPlayer m => AI m Card choose :: MonadPlayer m => AI m Card
@@ -237,6 +233,19 @@ chooseStatistic :: MonadPlayer m => AI m Card
chooseStatistic = do chooseStatistic = do
h <- gets getHand h <- gets getHand
handCards <- gets myHand handCards <- gets myHand
table <- gets table
let tableNo = length table
left = 3 - tableNo
depth = case length handCards of
10 -> 3 + tableNo
9 -> 3 + tableNo
8 -> 3 + tableNo
7 -> 6 + tableNo
6 -> 9 + tableNo
5 -> 12 + tableNo
4 -> 15 + tableNo
_ -> 100
modify $ setDepth depth
guess__ <- gets guess guess__ <- gets guess
self <- get self <- get
maySkat <- showSkat self maySkat <- showSkat self
@@ -244,8 +253,7 @@ chooseStatistic = do
guess = case maySkat of guess = case maySkat of
Just cs -> (cs `isSkat`) guess_ Just cs -> (cs `isSkat`) guess_
Nothing -> guess_ Nothing -> guess_
table <- gets table let ns = case tableNo of
let ns = case length table of
0 -> (0, 0, 0, 0) 0 -> (0, 0, 0, 0)
1 -> (-1, 0, -1, 0) 1 -> (-1, 0, -1, 0)
2 -> (0, 0, -1, 0) 2 -> (0, 0, -1, 0)
@@ -286,14 +294,13 @@ chooseOpen = do
piles <- showPiles piles <- showPiles
hand <- gets getHand hand <- gets getHand
let myCards = handCards hand piles let myCards = handCards hand piles
liftIO $ putStrLn $ show hand ++ " chooses from " ++ show myCards
possible <- filterM (P.isAllowed myCards) myCards possible <- filterM (P.isAllowed myCards) myCards
case length possible of case length possible of
0 -> do 0 -> do
liftIO $ print hand liftIO $ print hand
liftIO $ print piles liftIO $ print piles
error "no cards left to choose from" error "no cards left to choose from"
1 -> return $ head possible 1 -> return $ toCard $ head possible
_ -> chooseSimulating _ -> chooseSimulating
chooseSimulating :: (MonadState AIEnv m, MonadPlayerOpen m) chooseSimulating :: (MonadState AIEnv m, MonadPlayerOpen m)
@@ -303,11 +310,12 @@ chooseSimulating = do
turnCol <- turnColour turnCol <- turnColour
trumpCol <- trumpColour trumpCol <- trumpColour
myHand <- gets getHand myHand <- gets getHand
depth <- gets simulationDepth
let ps = Players (PL $ Stupid.Stupid Team Hand1) let ps = Players (PL $ Stupid.Stupid Team Hand1)
(PL $ Stupid.Stupid Team Hand2) (PL $ Stupid.Stupid Team Hand2)
(PL $ Stupid.Stupid Single Hand3) (PL $ Stupid.Stupid Single Hand3)
env = mkSkatEnv piles turnCol trumpCol ps myHand env = mkSkatEnv piles turnCol trumpCol ps myHand
liftIO $ evalStateT (Minmax.choose :: Skat Card) env liftIO $ evalStateT (toCard <$> (Minmax.choose depth :: Skat (CardS Owner))) env
simulate :: (MonadState AIEnv m, MonadPlayerOpen m) simulate :: (MonadState AIEnv m, MonadPlayerOpen m)
=> Card -> m Int => Card -> m Int
@@ -329,7 +337,7 @@ simulate card = do
env = mkSkatEnv piles turnCol trumpCol ps (next myHand) env = mkSkatEnv piles turnCol trumpCol ps (next myHand)
-- simulate the game after playing the given card -- simulate the game after playing the given card
(sgl, tm) <- liftIO $ evalStateT (do (sgl, tm) <- liftIO $ evalStateT (do
modifyp $ playCard card modifyp $ playCard myHand card
turnGeneric playOpen depth) env turnGeneric playOpen depth) env
let v = if myTeam == Single then (sgl, tm) else (tm, sgl) let v = if myTeam == Single then (sgl, tm) else (tm, sgl)
-- put the value into context for when not the whole game is -- put the value into context for when not the whole game is
@@ -346,13 +354,13 @@ predictValue (own, others) = do
--return $ own + pot --return $ own + pot
return (own-others) return (own-others)
potential :: (MonadState AIEnv m, MonadPlayerOpen m) potential :: (MonadState AIEnv m, MonadPlayerOpen m, HasCard c)
=> [Card] -> m Int => [c] -> m Int
potential cs = do potential cs = do
tr <- trumpColour tr <- trumpColour
let trs = filter (isTrump tr) cs let trs = filter (isTrump tr) cs
value = count cs value = count . map toCard $ cs
positions <- filter (==0) <$> mapM position cs positions <- filter (==0) <$> mapM (position . toCard) cs
return $ length trs * 10 + value + length positions * 5 return $ length trs * 10 + value + length positions * 5
position :: (MonadState AIEnv m, MonadPlayer m) position :: (MonadState AIEnv m, MonadPlayer m)
+1 -1
View File
@@ -15,4 +15,4 @@ instance Player Stupid where
trumpCol <- trumpColour trumpCol <- trumpColour
turnCol <- turnColour turnCol <- turnColour
let possible = filter (isAllowed trumpCol turnCol hand) hand let possible = filter (isAllowed trumpCol turnCol hand) hand
return (head possible, p) return (toCard $ head possible, p)
+48 -23
View File
@@ -1,16 +1,23 @@
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module Skat.Card where module Skat.Card where
import Data.List import Data.List
import Data.Foldable (Foldable)
import qualified Data.Foldable as F
import qualified Data.Set as S
import Data.Aeson import Data.Aeson
import System.Random (newStdGen, StdGen) import System.Random (newStdGen, StdGen)
import Control.DeepSeq import Control.DeepSeq
import Skat.Utils import Skat.Utils
class HasCard c where
toCard :: c -> Card
class Countable a b where class Countable a b where
count :: a -> b count :: a -> b
@@ -41,6 +48,15 @@ data Colour = Diamonds
data Card = Card Type Colour data Card = Card Type Colour
deriving (Eq, Show, Ord, Read) deriving (Eq, Show, Ord, Read)
getType :: Card -> Type
getType (Card t _) = t
getColour :: Card -> Colour
getColour (Card _ c) = c
instance HasCard Card where
toCard = id
instance ToJSON Card where instance ToJSON Card where
toJSON (Card t c) = toJSON (Card t c) =
object ["type" .= show t, "colour" .= show c] object ["type" .= show t, "colour" .= show c]
@@ -51,11 +67,8 @@ instance FromJSON Card where
c <- v .: "colour" c <- v .: "colour"
return $ Card (read t) (read c) return $ Card (read t) (read c)
getColour :: Card -> Colour getID :: HasCard c => c -> Int
getColour (Card _ c) = c getID card = let t = getType $ toCard card in case t of
getID :: Card -> Int
getID (Card t _) = case t of
Seven -> 0 Seven -> 0
Eight -> 0 Eight -> 0
Nine -> 0 Nine -> 0
@@ -65,11 +78,22 @@ getID (Card t _) = case t of
Ace -> 16 Ace -> 16
Jack -> 32 Jack -> 32
instance Enum Card where
fromEnum (Card tp col) = fromEnum col * 8 + fromEnum tp
toEnum n = Card tp col
where col = toEnum (n `div` 8)
tp = toEnum (n `mod` 8)
instance Countable Card Int where instance Countable Card Int where
count (Card t _) = count t count (Card t _) = count t
instance Countable [Card] Int where instance Foldable t => Countable (t Card) Int where
count = sum . map count count = foldl' f 0
where f acc c = count c + acc
instance Countable (S.Set Card) Int where
count = S.foldl' f 0
where f acc card = count card + acc
instance NFData Card where instance NFData Card where
rnf (Card t c) = t `seq` c `seq` () rnf (Card t c) = t `seq` c `seq` ()
@@ -78,22 +102,21 @@ equals :: Colour -> Maybe Colour -> Bool
equals col (Just x) = col == x equals col (Just x) = col == x
equals col Nothing = True equals col Nothing = True
isTrump :: Colour -> Card -> Bool isTrump :: HasCard c => Colour -> c -> Bool
isTrump trumpCol (Card tp col) isTrump trumpCol crd
| tp == Jack = True | getType (toCard crd) == Jack = True
| otherwise = col == trumpCol | otherwise = getColour (toCard crd) == trumpCol
effectiveColour :: Colour -> Card -> Colour effectiveColour :: HasCard c => Colour -> c -> Colour
effectiveColour trumpCol card@(Card _ col) = effectiveColour trumpCol crd = if trump then trumpCol else getColour (toCard crd)
if trump then trumpCol else col where trump = isTrump trumpCol crd
where trump = isTrump trumpCol card
isAllowed :: Colour -> Maybe Colour -> [Card] -> Card -> Bool isAllowed :: (Foldable t, HasCard c1, HasCard c2) => Colour -> Maybe Colour -> t c1 -> c2 -> Bool
isAllowed trumpCol turnCol cs card = isAllowed trumpCol turnCol cs crd =
if col `equals` turnCol if col `equals` turnCol
then True then True
else not $ any (\ca -> effectiveColour trumpCol ca `equals` turnCol && ca /= card) cs else not $ F.any (\ca -> effectiveColour trumpCol ca `equals` turnCol && toCard ca /= toCard crd) cs
where col = effectiveColour trumpCol card where col = effectiveColour trumpCol (toCard crd)
compareCards :: Colour compareCards :: Colour
-> Maybe Colour -> Maybe Colour
@@ -112,11 +135,13 @@ compareCards trumpCol turnCol c1@(Card tp1 col1) c2@(Card tp2 col2) =
where trp1 = isTrump trumpCol c1 where trp1 = isTrump trumpCol c1
trp2 = isTrump trumpCol c2 trp2 = isTrump trumpCol c2
sortCards :: Colour -> Maybe Colour -> [Card] -> [Card] sortCards :: HasCard c => Colour -> Maybe Colour -> [c] -> [c]
sortCards trumpCol turnCol cs = sortBy (compareCards trumpCol turnCol) cs sortCards trumpCol turnCol cs = sortBy f cs
where f c1 c2 = compareCards trumpCol turnCol (toCard c1) (toCard c2)
highestCard :: Colour -> Maybe Colour -> [Card] -> Card highestCard :: HasCard c => Colour -> Maybe Colour -> [c] -> c
highestCard trumpCol turnCol cs = maximumBy (compareCards trumpCol turnCol) cs highestCard trumpCol turnCol cs = maximumBy f cs
where f c1 c2 = compareCards trumpCol turnCol (toCard c1) (toCard c2)
shuffleCards :: IO [Card] shuffleCards :: IO [Card]
shuffleCards = do shuffleCards = do
+1 -4
View File
@@ -17,7 +17,7 @@ import Skat.AI.Stupid
-- | predefined card distribution for testing purposes -- | predefined card distribution for testing purposes
cardDistr :: Piles cardDistr :: Piles
cardDistr = Piles hands [] (map (putAt SkatP) skt) cardDistr = emptyPiles hand1 hand2 hand3 skt
where hand3 = [Card Ace Spades, Card Jack Diamonds, Card Jack Clubs, Card King Spades, where hand3 = [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]
@@ -27,9 +27,6 @@ cardDistr = Piles hands [] (map (putAt SkatP) skt)
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,
Card Seven Hearts, Card Eight Hearts, Card Queen Hearts, Card King Hearts, Card Seven Hearts, Card Eight Hearts, Card Queen Hearts, Card King Hearts,
Card Nine Diamonds, Card Eight Diamonds] Card Nine Diamonds, Card Eight Diamonds]
hands = map (putAt Hand1) hand1
++ map (putAt Hand2) hand2
++ map (putAt Hand3) hand3
skt = [Card Nine Clubs, Card Queen Clubs] skt = [Card Nine Clubs, Card Queen Clubs]
singleVsBots :: Communicator c => c -> IO () singleVsBots :: Communicator c => c -> IO ()
+21 -15
View File
@@ -1,12 +1,13 @@
module Skat.Operations ( module Skat.Operations (
turn, turnGeneric, play, playOpen, publishGameResults, turn, turnGeneric, play, playOpen, publishGameResults,
publishGameStart, play_, sortRender publishGameStart, play_, sortRender, undo_
) where ) where
import Control.Monad.State import Control.Monad.State
import System.Random (newStdGen, randoms) import System.Random (newStdGen, randoms)
import Data.List import Data.List
import Data.Ord import Data.Ord
import qualified Data.Set as S
import Skat import Skat
import Skat.Card import Skat.Card
@@ -23,11 +24,11 @@ compareRender (Card t1 c1) (Card t2 c2) = case compare c1 c2 of
sortRender :: [Card] -> [Card] sortRender :: [Card] -> [Card]
sortRender = sortBy compareRender sortRender = sortBy compareRender
play_ :: Card -> Skat () play_ :: HasCard c => c -> Skat ()
play_ card = do play_ card = do
hand <- gets currentHand hand <- gets currentHand
trCol <- gets trumpColour trCol <- gets trumpColour
modifyp $ playCard card modifyp $ playCard hand card
table <- getp tableCards table <- getp tableCards
case length table of case length table of
1 -> do modify (setCurrentHand $ next hand) 1 -> do modify (setCurrentHand $ next hand)
@@ -35,6 +36,12 @@ play_ card = do
3 -> evaluateTable >>= modify . setCurrentHand 3 -> evaluateTable >>= modify . setCurrentHand
_ -> modify (setCurrentHand $ next hand) _ -> modify (setCurrentHand $ next hand)
undo_ :: HasCard c => c -> Hand -> Maybe Colour -> Team -> Skat ()
undo_ card oldCurrent oldTurnCol oldWinner = do
modify $ setCurrentHand oldCurrent
modify $ setTurnColour oldTurnCol
modifyp $ unplayCard oldCurrent (toCard card) oldWinner
turnGeneric :: (PL -> Skat Card) turnGeneric :: (PL -> Skat Card)
-> Int -> Int
-> Skat (Int, Int) -> Skat (Int, Int)
@@ -43,7 +50,7 @@ turnGeneric playFunc depth = do
table <- getp tableCards table <- getp tableCards
ps <- gets players ps <- gets players
let p = player ps n let p = player ps n
hand <- getp $ handCards n over <- getp $ handEmpty n
trCol <- gets trumpColour trCol <- gets trumpColour
case length table of case length table of
0 -> playFunc p >> modify (setCurrentHand $ next n) >> turnGeneric playFunc depth 0 -> playFunc p >> modify (setCurrentHand $ next n) >> turnGeneric playFunc depth
@@ -56,7 +63,7 @@ turnGeneric playFunc depth = do
2 -> playFunc p >> modify (setCurrentHand $ next n) >> turnGeneric playFunc depth 2 -> playFunc p >> modify (setCurrentHand $ next n) >> turnGeneric playFunc depth
3 -> do 3 -> do
w <- evaluateTable w <- evaluateTable
if depth <= 1 || length hand == 0 if depth <= 1 || over
then countGame then countGame
else modify (setCurrentHand w) >> turnGeneric playFunc (depth - 1) else modify (setCurrentHand w) >> turnGeneric playFunc (depth - 1)
@@ -69,9 +76,8 @@ evaluateTable = do
turnCol <- gets turnColour turnCol <- gets turnColour
table <- getp tableCards table <- getp tableCards
ps <- gets players ps <- gets players
let winningCard = highestCard trumpCol turnCol table let winnerHand = uorigin $ getPile $ highestCard trumpCol turnCol table
Just winnerHand <- getp $ originOfCard winningCard winner = player ps winnerHand
let winner = player ps winnerHand
modifyp $ cleanTable (team winner) modifyp $ cleanTable (team winner)
modify $ setTurnColour Nothing modify $ setTurnColour Nothing
return $ hand winner return $ hand winner
@@ -82,25 +88,25 @@ countGame = getp count
play :: (Show p, Player p) => p -> Skat Card play :: (Show p, Player p) => p -> Skat Card
play p = do play p = do
liftIO $ putStrLn "playing" liftIO $ putStrLn "playing"
table <- getp tableCardsS table <- getp tableCards
turnCol <- gets turnColour turnCol <- gets turnColour
trump <- gets trumpColour trump <- gets trumpColour
hand <- getp $ handCards (hand p) cards <- getp $ handCards (hand p)
fallen <- getp played fallen <- getp played
(card, p') <- chooseCard p table fallen hand (card, p') <- chooseCard p table fallen cards
modifyPlayers $ updatePlayer p' modifyPlayers $ updatePlayer p'
modifyp $ playCard card modifyp $ playCard (hand p) card
ps <- fmap playersToList $ gets players ps <- fmap playersToList $ gets players
table' <- getp tableCardsS table' <- getp tableCards
ps' <- mapM (\p -> onCardPlayed p (head table')) ps ps' <- mapM (\p -> onCardPlayed p (head table')) ps
mapM_ (modifyPlayers . updatePlayer) ps' mapM_ (modifyPlayers . updatePlayer) ps'
return card return (toCard card)
playOpen :: (Show p, Player p) => p -> Skat Card playOpen :: (Show p, Player p) => p -> Skat Card
playOpen p = do playOpen p = do
--liftIO $ putStrLn $ show (hand p) ++ " playing open" --liftIO $ putStrLn $ show (hand p) ++ " playing open"
card <- chooseCardOpen p card <- chooseCardOpen p
modifyp $ playCard card modifyp $ playCard (hand p) card
return card return card
publishGameResults :: (Int, Int) -> Skat () publishGameResults :: (Int, Int) -> Skat ()
+99 -51
View File
@@ -1,32 +1,47 @@
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
module Skat.Pile where module Skat.Pile where
import Data.List import Prelude hiding (lookup)
import qualified Data.Map.Strict as M
import qualified Data.Vector as V
import Data.Vector (Vector)
import Data.Foldable (toList, foldl', Foldable)
import Data.Maybe
import Data.Aeson import Data.Aeson
import Control.Exception import Control.Exception
import Data.List (delete)
import Skat.Card import Skat.Card
import Skat.Utils import Skat.Utils
data Team = Team | Single data Team = Team | Single
deriving (Show, Eq, Ord, Enum) deriving (Show, Eq, Ord, Enum, Read)
data CardS p = CardS { getCard :: Card data CardS p = CardS { getCard :: Card
, getPile :: p } , getPile :: p }
deriving (Show, Eq, Ord) deriving (Show, Eq, Ord, Read)
instance HasCard (CardS p) where
toCard = getCard
instance Countable (CardS p) Int where instance Countable (CardS p) Int where
count = count . getCard count = count . getCard
instance Foldable t => Countable (t (CardS p)) Int where
count = foldl' f 0
where f acc c = count c + acc
instance ToJSON p => ToJSON (CardS p) where instance ToJSON p => ToJSON (CardS p) where
toJSON (CardS card pile) = toJSON (CardS card pile) =
object ["card" .= card, "pile" .= pile] object ["card" .= card, "pile" .= pile]
data Hand = Hand1 | Hand2 | Hand3 data Hand = Hand1 | Hand2 | Hand3
deriving (Show, Eq, Ord) deriving (Show, Eq, Ord, Read)
toInt :: Hand -> Int toInt :: Hand -> Int
toInt Hand1 = 1 toInt Hand1 = 1
@@ -43,76 +58,112 @@ prev Hand1 = Hand3
prev Hand2 = Hand1 prev Hand2 = Hand1
prev Hand3 = Hand2 prev Hand3 = Hand2
data Played = Table Hand data Owner = P Hand | S
| Won Hand Team deriving (Show, Eq, Ord, Read)
deriving (Show, Eq, Ord)
instance ToJSON Played where instance ToJSON Owner where
toJSON (Table hand) = toJSON _ = undefined -- TODO: fix
object ["state" .= ("table" :: String), "played_by" .= show hand]
toJSON (Won hand team) =
object ["state" .= ("won" :: String), "played_by" .= show hand, "won_by" .= show team]
data SkatP = SkatP type Played = Owner -- TODO: remove
data Piles = Piles { _hand1 :: [CardS Owner]
, _hand2 :: [CardS Owner]
, _hand3 :: [CardS Owner]
, _table :: [CardS Owner]
, _wonSingle :: [CardS Owner]
, _wonTeam :: [CardS Owner]
, _skat :: [CardS Owner] }
deriving (Show, Eq, Ord) deriving (Show, Eq, Ord)
data Piles = Piles { hands :: [CardS Hand] toTable :: Hand -> Card -> Piles -> Piles
, played :: [CardS Played] toTable hand card ps = ps { _table = (CardS card (P hand)) : _table ps }
, skat :: [CardS SkatP] }
deriving (Show, Eq, Ord)
instance Countable Piles (Int, Int) where instance Countable Piles (Int, Int) where
count ps = (sgl, tm) count ps = (sgl, tm)
where sgl = count (skatCards ps) + count (wonCards Single ps) where sgl = count (skatCards ps) + count (wonCards Single ps)
tm = count (wonCards Team ps) tm = count (wonCards Team ps)
origin :: CardS Played -> Hand played :: Piles -> [CardS Owner]
origin (CardS _ (Table hand)) = hand played ps = _wonSingle ps ++ _wonTeam ps ++ _table ps
origin (CardS _ (Won hand _)) = hand
originOfCard :: Card -> Piles -> Maybe Hand origin :: Owner -> Maybe Hand
originOfCard card (Piles _ pld _) = origin <$> find ((==card) . getCard) pld origin (P hand) = Just hand
origin S = Nothing
playCard :: Card -> Piles -> Piles uorigin :: Owner -> Hand
playCard card (Piles hs pld skt) = Piles hs' (ca : pld) skt uorigin owner = case origin owner of
where (CardS _ hand, hs') = remove ((==card) . getCard) hs Just hand -> hand
ca = CardS card (Table hand) Nothing -> error "has no origin"
winCard :: Team -> CardS Played -> CardS Played removeFromHand :: Hand -> Card -> Piles -> Piles
winCard team (CardS card (Table hand)) = CardS card (Won hand team) removeFromHand Hand1 card ps = ps { _hand1 = delete (CardS card (P Hand1)) (_hand1 ps) }
winCard team c = c removeFromHand Hand2 card ps = ps { _hand2 = delete (CardS card (P Hand2)) (_hand2 ps) }
removeFromHand Hand3 card ps = ps { _hand3 = delete (CardS card (P Hand3)) (_hand3 ps) }
wonCards :: Team -> Piles -> [Card] addToHand :: Hand -> Card -> Piles -> Piles
wonCards team (Piles _ pld _) = filterMap (f . getPile) getCard pld addToHand Hand1 card ps = ps { _hand1 = (CardS card (P Hand1)) : (_hand1 ps) }
where f (Won _ tm) = tm == team addToHand Hand2 card ps = ps { _hand2 = (CardS card (P Hand2)) : (_hand2 ps) }
f _ = False addToHand Hand3 card ps = ps { _hand3 = (CardS card (P Hand3)) : (_hand3 ps) }
playCard :: HasCard c => Hand -> c -> Piles -> Piles
playCard hand card' ps = (removeFromHand hand card ps) { _table = (CardS card (P hand)) : _table ps }
where card = toCard card'
unplayCard :: Hand -> Card -> Team -> Piles -> Piles
unplayCard hand card winner ps
| null table = case winner of
Team -> ps' { _table = tail $ take 3 (_wonTeam ps), _wonTeam = drop 3 (_wonTeam ps) }
Single -> ps' { _table = tail $ take 3 (_wonSingle ps), _wonSingle = drop 3 (_wonSingle ps) }
| otherwise = ps' { _table = tail (_table ps) }
where ps' = addToHand hand card ps
table = tableCards ps
wonCards :: Team -> Piles -> [CardS Owner]
wonCards Team = _wonTeam
wonCards Single = _wonSingle
cleanTable :: Team -> Piles -> Piles cleanTable :: Team -> Piles -> Piles
cleanTable winner ps@(Piles hs pld skt) = Piles hs pld' skt cleanTable Team ps = ps { _table = [], _wonTeam = _table ps ++ _wonTeam ps }
where table = tableCards ps cleanTable Single ps = ps { _table = [], _wonSingle = _table ps ++ _wonSingle ps }
pld' = map (winCard winner) pld
tableCards :: Piles -> [Card] tableCards :: Piles -> [CardS Owner]
tableCards (Piles _ pld _) = filterMap (f . getPile) getCard pld tableCards = _table
where f (Table _) = True
f _ = False
tableCardsS :: Piles -> [CardS Played] handEmpty :: Hand -> Piles -> Bool
tableCardsS (Piles _ pld _) = filter (f . getPile) pld handEmpty Hand1 = null . _hand1
where f (Table _) = True handEmpty Hand2 = null . _hand2
f _ = False handEmpty Hand3 = null . _hand3
handCards :: Hand -> Piles -> [Card] handCards :: Hand -> Piles -> [CardS Owner]
handCards hand (Piles hs _ _) = filterMap ((==hand) . getPile) getCard hs handCards Hand1 = _hand1
handCards Hand2 = _hand2
handCards Hand3 = _hand3
allowed :: Hand -> Colour -> Maybe Colour -> Piles -> [CardS Owner]
allowed hand trCol turnCol ps
| null sameColour = cards
| otherwise = sameColour
where cards = handCards hand ps
sameColour = filter (\ca -> effectiveColour trCol ca `equals` turnCol) cards
skatCards :: Piles -> [Card] skatCards :: Piles -> [Card]
skatCards (Piles _ _ skat) = map getCard skat skatCards = map getCard . _skat
emptyPiles :: [Card] -> [Card] -> [Card] -> [Card] -> Piles
emptyPiles h1 h2 h3 skt = makePiles h1 h2 h3 [] skt
putAt :: p -> Card -> CardS p putAt :: p -> Card -> CardS p
putAt = flip CardS putAt = flip CardS
makePiles :: [Card] -> [Card] -> [Card] -> [CardS Owner] -> [Card] -> Piles
makePiles h1 h2 h3 table skt = Piles h1' h2' h3' table [] [] skt'
where h1' = map (putAt $ P Hand1) h1
h2' = map (putAt $ P Hand2) h2
h3' = map (putAt $ P Hand3) h3
skt' = map (putAt S) skt
distribute :: [Card] -> Piles distribute :: [Card] -> Piles
distribute cards = Piles hands [] (map (putAt SkatP) skt) distribute cards = emptyPiles hand1 hand2 hand3 skt
where round1 = chunksOf 3 (take 9 cards) where round1 = chunksOf 3 (take 9 cards)
skt = take 2 $ drop 9 cards skt = take 2 $ drop 9 cards
round2 = chunksOf 4 (take 12 $ drop 11 cards) round2 = chunksOf 4 (take 12 $ drop 11 cards)
@@ -120,6 +171,3 @@ distribute cards = Piles hands [] (map (putAt SkatP) skt)
hand1 = concatMap (!! 0) [round1, round2, round3] hand1 = concatMap (!! 0) [round1, round2, round3]
hand2 = concatMap (!! 1) [round1, round2, round3] hand2 = concatMap (!! 1) [round1, round2, round3]
hand3 = concatMap (!! 2) [round1, round2, round3] hand3 = concatMap (!! 2) [round1, round2, round3]
hands = map (putAt Hand1) hand1
++ map (putAt Hand2) hand2
++ map (putAt Hand3) hand3
+4 -4
View File
@@ -18,11 +18,11 @@ class (Monad m, MonadIO m, MonadPlayer m) => MonadPlayerOpen m where
class Player p where class Player p where
team :: p -> Team team :: p -> Team
hand :: p -> Hand hand :: p -> Hand
chooseCard :: MonadPlayer m chooseCard :: (HasCard c, MonadPlayer m)
=> p => p
-> [CardS Played] -> [CardS Played]
-> [CardS Played] -> [CardS Played]
-> [Card] -> [c]
-> m (Card, p) -> m (Card, p)
onCardPlayed :: MonadPlayer m onCardPlayed :: MonadPlayer m
=> p => p
@@ -34,10 +34,10 @@ class Player p where
-> m Card -> m Card
chooseCardOpen p = do chooseCardOpen p = do
piles <- showPiles piles <- showPiles
let table = tableCardsS piles let table = tableCards piles
fallen = played piles fallen = played piles
myCards = handCards (hand p) piles myCards = handCards (hand p) piles
fmap fst $ chooseCard p table fallen myCards fst <$> chooseCard p table fallen myCards
onGameResults :: MonadIO m onGameResults :: MonadIO m
=> p => p
-> (Int, Int) -> (Int, Int)
+2 -2
View File
@@ -4,9 +4,9 @@ module Skat.Player.Utils (
import Skat.Player import Skat.Player
import qualified Skat.Card as C import qualified Skat.Card as C
import Skat.Card (Card) import Skat.Card (Card, HasCard(..))
isAllowed :: MonadPlayer m => [Card] -> Card -> m Bool isAllowed :: (HasCard c, MonadPlayer m) => [c] -> c -> m Bool
isAllowed hand card = do isAllowed hand card = do
trCol <- trumpColour trCol <- trumpColour
turnCol <- turnColour turnCol <- turnColour
+6 -2
View File
@@ -1,8 +1,12 @@
module Skat.Render where module Skat.Render where
import Data.List import Data.List
import Data.Vector (Vector, toList)
import Skat.Card import Skat.Card
render :: [Card] -> IO () render :: HasCard c => [c] -> IO ()
render = putStrLn . intercalate "\n" . zipWith (\n c -> show n ++ ") " ++ show c) [0..] render = putStrLn . intercalate "\n" . zipWith (\n c -> show n ++ ") " ++ show c) [0..] . map toCard
renderVector :: Vector Card -> IO ()
renderVector = render . toList
+2 -1
View File
@@ -4,6 +4,7 @@ import System.Random
import Text.Read import Text.Read
import qualified Data.ByteString.Char8 as B (ByteString, unpack, pack) import qualified Data.ByteString.Char8 as B (ByteString, unpack, pack)
import qualified Data.Text as T (Text, unpack, pack) import qualified Data.Text as T (Text, unpack, pack)
import Data.List (foldl')
shuffle :: StdGen -> [a] -> [a] shuffle :: StdGen -> [a] -> [a]
shuffle g xs = shuffle' (randoms g) xs shuffle g xs = shuffle' (randoms g) xs
@@ -31,7 +32,7 @@ remove pred xs = foldr f (undefined, []) xs
filterMap :: (a -> Bool) -> (a -> b) -> [a] -> [b] filterMap :: (a -> Bool) -> (a -> b) -> [a] -> [b]
filterMap pred f as = foldr g [] as filterMap pred f as = foldr g [] as
where g a bs = if pred a then f a : bs else bs where g a bs = if pred a then (f $! a) : bs else bs
--filterM :: Monad m => (a -> m Bool) -> [a] -> m [a] --filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]
--filterM _ [] = return [] --filterM _ [] = return []