| @@ -0,0 +1,169 @@ | |||||
| module Card where | |||||
| import Data.List | |||||
| import Utils | |||||
| data Type = Seven | |||||
| | Eight | |||||
| | Nine | |||||
| | Queen | |||||
| | King | |||||
| | Ten | |||||
| | Ace | |||||
| | Jack | |||||
| deriving (Eq, Ord, Show, Enum) | |||||
| countType :: Type -> Int | |||||
| countType Ace = 11 | |||||
| countType Ten = 10 | |||||
| countType King = 4 | |||||
| countType Queen = 3 | |||||
| countType Jack = 2 | |||||
| countType _ = 0 | |||||
| data Colour = Diamonds | |||||
| | Hearts | |||||
| | Spades | |||||
| | Clubs | |||||
| deriving (Eq, Ord, Show, Enum, Read) | |||||
| data Card = Card Type Colour | |||||
| deriving (Eq, Show) | |||||
| countCard :: Card -> Int | |||||
| countCard (Card t _) = countType t | |||||
| count :: [Card] -> Int | |||||
| count = sum . map countCard | |||||
| data Team = Team | Single | |||||
| deriving (Show, Eq, Ord, Enum) | |||||
| data Space = Table | Hand1 | Hand2 | Hand3 | WonTeam | WonSingle | SkatP | |||||
| deriving (Show, Eq, Ord, Enum) | |||||
| teamPile :: Team -> Space | |||||
| teamPile Team = WonTeam | |||||
| teamPile Single = WonSingle | |||||
| playerHand :: Index -> Space | |||||
| playerHand One = Hand1 | |||||
| playerHand Two = Hand2 | |||||
| playerHand Three = Hand3 | |||||
| playerOfHand :: Space -> Index | |||||
| playerOfHand Hand1 = One | |||||
| playerOfHand Hand2 = Two | |||||
| playerOfHand Hand3 = Three | |||||
| data CardS = CardS { getCard :: Card | |||||
| , getSpace :: Space | |||||
| , getOwner :: Space } | |||||
| deriving (Show, Eq) | |||||
| moveCard :: Card -> Space -> [CardS] -> [CardS] | |||||
| moveCard card sp cards = map f cards | |||||
| where f c = if card == getCard c then c { getSpace = sp } else c | |||||
| findCards :: Space -> [CardS] -> [Card] | |||||
| findCards sp cards = foldr f [] cards | |||||
| where f (CardS c s _) cs | |||||
| | s == sp = c : cs | |||||
| | otherwise = cs | |||||
| data Index = One | Two | Three | |||||
| deriving (Show, Ord, Eq, Enum) | |||||
| next :: Index -> Index | |||||
| next One = Two | |||||
| next Two = Three | |||||
| next Three = One | |||||
| prev :: Index -> Index | |||||
| prev One = Three | |||||
| prev Two = One | |||||
| prev Three = Two | |||||
| data Player = Player { team :: Team | |||||
| , index :: Index } | |||||
| deriving Show | |||||
| data Players = Players Player Player Player | |||||
| deriving Show | |||||
| player :: Players -> Index -> Player | |||||
| player (Players p _ _) One = p | |||||
| player (Players _ p _) Two = p | |||||
| player (Players _ _ p) Three = p | |||||
| type Hand = [Card] | |||||
| equals :: Colour -> Maybe Colour -> Bool | |||||
| equals col (Just x) = col == x | |||||
| equals col Nothing = True | |||||
| isTrump :: Colour -> Card -> Bool | |||||
| isTrump trumpCol (Card tp col) | |||||
| | tp == Jack = True | |||||
| | otherwise = col == trumpCol | |||||
| effectiveColour :: Colour -> Card -> Colour | |||||
| effectiveColour trumpCol card@(Card _ col) = | |||||
| if trump then trumpCol else col | |||||
| where trump = isTrump trumpCol card | |||||
| isAllowed :: Colour -> Maybe Colour -> Hand -> Card -> Bool | |||||
| isAllowed trumpCol turnCol cs card = | |||||
| if col `equals` turnCol | |||||
| then True | |||||
| else not $ any (\ca -> effectiveColour trumpCol ca `equals` turnCol && ca /= card) cs | |||||
| where col = effectiveColour trumpCol card | |||||
| putAt :: Space -> Card -> CardS | |||||
| putAt sp c = CardS c sp sp | |||||
| distribute :: [Card] -> [CardS] | |||||
| distribute cards = map (putAt Hand1) hand1 | |||||
| ++ map (putAt Hand2) hand2 | |||||
| ++ map (putAt Hand3) hand3 | |||||
| ++ map (putAt SkatP) skt | |||||
| where round1 = chunksOf 3 (take 9 cards) | |||||
| skt = take 2 $ drop 9 cards | |||||
| round2 = chunksOf 4 (take 12 $ drop 11 cards) | |||||
| round3 = chunksOf 3 (take 9 $ drop 23 cards) | |||||
| hand1 = concatMap (!! 0) [round1, round2, round3] | |||||
| hand2 = concatMap (!! 1) [round1, round2, round3] | |||||
| hand3 = concatMap (!! 2) [round1, round2, round3] | |||||
| playersFromTable :: Players -> [CardS] -> [Player] | |||||
| playersFromTable ps = map (player ps . playerOfHand . getOwner) | |||||
| -- TESTING VARS | |||||
| c1 :: Card | |||||
| c1 = Card Jack Spades | |||||
| c2 :: Card | |||||
| c2 = Card Ace Diamonds | |||||
| c3 :: Card | |||||
| c3 = Card Queen Diamonds | |||||
| c4 :: Card | |||||
| c4 = Card Queen Hearts | |||||
| c5 :: Card | |||||
| c5 = Card Jack Clubs | |||||
| h1 :: Hand | |||||
| h1 = [c1,c2,c3,c4,c5] | |||||
| allCards :: [Card] | |||||
| allCards = [ Card t c | t <- tps, c <- cols ] | |||||
| where tps = [Seven .. Jack] | |||||
| cols = [Diamonds .. Clubs] | |||||
| distributePutSkat :: [Card] -> [CardS] | |||||
| distributePutSkat cards = foldr (\c m -> moveCard c WonSingle m) distributed skt | |||||
| where distributed = distribute cards | |||||
| skt = findCards SkatP distributed | |||||
| @@ -0,0 +1,16 @@ | |||||
| module Main where | |||||
| import Control.Monad.State | |||||
| import Card | |||||
| import Skat | |||||
| import Reizen | |||||
| import Operations | |||||
| main :: IO () | |||||
| main = do | |||||
| env <- reizen | |||||
| (sgl, tm) <- evalStateT runGame env | |||||
| putStrLn $ "Single player has " ++ show sgl ++ " points." | |||||
| putStrLn $ "Team has " ++ show tm ++ " points." | |||||
| @@ -0,0 +1,203 @@ | |||||
| module Operations where | |||||
| import Control.Monad.State | |||||
| import System.Random (newStdGen, randoms) | |||||
| import Data.List | |||||
| import Data.Ord | |||||
| import Card | |||||
| import Skat | |||||
| import Utils (shuffle) | |||||
| compareCards :: Colour | |||||
| -> Maybe Colour | |||||
| -> Card | |||||
| -> Card | |||||
| -> Ordering | |||||
| compareCards _ _ (Card Jack col1) (Card Jack col2) = compare col1 col2 | |||||
| compareCards trumpCol turnCol c1@(Card tp1 col1) c2@(Card tp2 col2) = | |||||
| case compare trp1 trp2 of | |||||
| EQ -> | |||||
| case compare (col1 `equals` turnCol) | |||||
| (col2 `equals` turnCol) of | |||||
| EQ -> compare tp1 tp2 | |||||
| v -> v | |||||
| v -> v | |||||
| where trp1 = isTrump trumpCol c1 | |||||
| trp2 = isTrump trumpCol c2 | |||||
| sortCards :: Colour -> Maybe Colour -> [Card] -> [Card] | |||||
| sortCards trumpCol turnCol cs = sortBy (compareCards trumpCol turnCol) cs | |||||
| compareRender :: Card -> Card -> Ordering | |||||
| compareRender (Card t1 c1) (Card t2 c2) = case compare c1 c2 of | |||||
| EQ -> compare t1 t2 | |||||
| v -> v | |||||
| sortRender :: [Card] -> [Card] | |||||
| sortRender = sortBy compareRender | |||||
| -- | finishes the calculation of a match | |||||
| turning :: Index -> Skat (Int, Int) | |||||
| turning n = undefined | |||||
| turn2 :: Index -> Skat (Int, Int) | |||||
| turn2 n = do | |||||
| t <- table | |||||
| ps <- gets players | |||||
| let p = player ps n | |||||
| hand <- cardsAt (playerHand $ index p) | |||||
| if length hand == 0 | |||||
| then countGame | |||||
| else case length t of | |||||
| 0 -> play p >> turn2 (next n) | |||||
| 1 -> do | |||||
| modify (setTurnColour . f . head $ t) | |||||
| play p | |||||
| turn2 (next n) | |||||
| 2 -> play p >> evaluateTable >>= turn2 | |||||
| 3 -> evaluateTable >>= turn2 | |||||
| where f (Card _ col) = Just col | |||||
| simulate :: Team -> Index -> Skat (Int, Int) | |||||
| simulate team n = do | |||||
| t <- table | |||||
| ps <- gets players | |||||
| let p = player ps n | |||||
| hand <- cardsAt (playerHand $ index p) | |||||
| if length hand == 0 | |||||
| then countGame | |||||
| else case length t of | |||||
| 0 -> playOpen team p >> simulate team (next n) | |||||
| 1 -> do | |||||
| modify (setTurnColour . f . head $ t) | |||||
| playOpen team p | |||||
| simulate team (next n) | |||||
| 2 -> playOpen team p >> evaluateTable >>= simulate team | |||||
| 3 -> evaluateTable >>= simulate team | |||||
| where f (Card _ col) = Just col | |||||
| evaluateTable :: Skat Index | |||||
| evaluateTable = do | |||||
| trumpCol <- gets trumpColour | |||||
| turnCol <- gets turnColour | |||||
| t <- table | |||||
| ts <- tableS | |||||
| ps <- gets players | |||||
| let psOrdered = playersFromTable ps ts | |||||
| l = zip psOrdered t | |||||
| g a b = compareCards trumpCol turnCol (snd a) (snd b) | |||||
| (winner, _) = last (sortBy g l) | |||||
| pile = teamPile $ team winner | |||||
| forM t (\c -> move c pile) | |||||
| modify $ setTurnColour Nothing | |||||
| return $ index winner | |||||
| countGame :: Skat (Int, Int) | |||||
| countGame = do | |||||
| sgl <- count <$> cardsAt WonSingle | |||||
| tm <- count <$> cardsAt WonTeam | |||||
| return (sgl, tm) | |||||
| turn :: Index -> Skat Index | |||||
| turn n = do | |||||
| ps <- gets players | |||||
| let p1 = player ps n | |||||
| p2 = player ps (next n) | |||||
| p3 = player ps (next $ next n) | |||||
| c1@(Card _ col) <- play p1 | |||||
| modify $ setTurnColour (Just col) | |||||
| c2 <- play p2 | |||||
| c3 <- play p3 | |||||
| trumpCol <- gets trumpColour | |||||
| turnCol <- gets turnColour | |||||
| let l = zip3 [p1, p2, p3] [c1, c2, c3] [n, next n, next $ next n] | |||||
| g a b = compareCards trumpCol turnCol (f a) (f b) | |||||
| (winner, _, idx) = last (sortBy g l) | |||||
| pile = teamPile $ team winner | |||||
| move c1 pile | |||||
| move c2 pile | |||||
| move c3 pile | |||||
| modify $ setTurnColour Nothing | |||||
| return idx | |||||
| where f (_, x, _) = x | |||||
| play :: Player -> Skat Card | |||||
| play p = do | |||||
| table <- table | |||||
| turnCol <- gets turnColour | |||||
| trump <- gets trumpColour | |||||
| hand <- cardsAt (playerHand $ index p) | |||||
| let card = playCard p table hand trump turnCol | |||||
| move card Table | |||||
| return card | |||||
| playOpen :: Team -> Player -> Skat Card | |||||
| playOpen team p = do | |||||
| card <- playCardOpenAI team p | |||||
| move card Table | |||||
| return card | |||||
| -- | cheating AI that knows all cards (open play) | |||||
| playCardOpenAI :: Team -> Player -> Skat Card | |||||
| playCardOpenAI team p = do | |||||
| table <- table | |||||
| turnCol <- gets turnColour | |||||
| trump <- gets trumpColour | |||||
| hand <- cardsAt (playerHand $ index p) | |||||
| let possible = filter (isAllowed trump turnCol hand) hand | |||||
| ownResult = if team == Single then fst else snd | |||||
| ownIdx = index p | |||||
| results <- forM possible (\card -> do | |||||
| move card Table | |||||
| val <- ownResult <$> simulate team ownIdx | |||||
| move card (playerHand $ index p) | |||||
| return (val, card)) | |||||
| return $ snd $ maximumBy (comparing fst) results | |||||
| playCard :: Player | |||||
| -> [Card] | |||||
| -> [Card] | |||||
| -> Colour | |||||
| -> Maybe Colour | |||||
| -> Card | |||||
| playCard p table hand trump turnCol = head possible | |||||
| where possible = filter (isAllowed trump turnCol hand) hand | |||||
| runGame :: Skat (Int, Int) | |||||
| runGame = do | |||||
| foldM_ (\i _ -> turn i) One [1..10] | |||||
| sgl <- fmap count $ cardsAt WonSingle | |||||
| tm <- fmap count $ cardsAt WonTeam | |||||
| return (sgl, tm) | |||||
| shuffleCards :: IO [Card] | |||||
| shuffleCards = do | |||||
| gen <- newStdGen | |||||
| return $ shuffle gen allCards | |||||
| -- TESTING VARS | |||||
| env :: SkatEnv | |||||
| env = SkatEnv cards Nothing Spades playersExamp | |||||
| where hand1 = take 10 allCards | |||||
| hand2 = take 10 $ drop 10 allCards | |||||
| hand3 = take 10 $ drop 20 allCards | |||||
| skt = drop 30 allCards | |||||
| cards = map (putAt Hand1) hand1 | |||||
| ++ map (putAt Hand2) hand2 | |||||
| ++ map (putAt Hand3) hand3 | |||||
| ++ map (putAt WonSingle) skt | |||||
| playersExamp :: Players | |||||
| playersExamp = Players (Player Team One) (Player Team Two) (Player Single Three) | |||||
| shuffledEnv :: IO SkatEnv | |||||
| shuffledEnv = do | |||||
| cards <- shuffleCards | |||||
| return $ SkatEnv (distribute cards) Nothing Spades playersExamp | |||||
| shuffledEnv2 :: IO SkatEnv | |||||
| shuffledEnv2 = do | |||||
| cards <- shuffleCards | |||||
| return $ SkatEnv (distributePutSkat cards) Nothing Spades playersExamp | |||||
| @@ -0,0 +1,73 @@ | |||||
| module Reizen where | |||||
| import Skat | |||||
| import Card | |||||
| import Utils | |||||
| import Operations | |||||
| import Render | |||||
| data Reizer = Reizer Index [Card] | |||||
| deriving Show | |||||
| getHand :: Index -> [Reizer] -> [Card] | |||||
| getHand n rs = let (Reizer _ h) = head $ filter (\(Reizer i cs) -> i == n) rs | |||||
| in h | |||||
| goWith :: [Card] -> Int -> IO Bool | |||||
| goWith cs n = query $ "Go with " ++ show n | |||||
| goUp :: [Card] -> Int -> IO Int | |||||
| goUp cs n = query $ "Go up " ++ show n | |||||
| askColour :: [Card] -> IO Colour | |||||
| askColour cs = render (sortRender cs) >> query "Trump should be:" | |||||
| askSkat :: [Card] -> IO (Card, Card) | |||||
| askSkat cs_ = do | |||||
| let cs = sortRender cs_ | |||||
| render cs | |||||
| (n1, n2) <- query "Drop two cards:" | |||||
| if n1 < length cs && n2 < length cs && n1 >= 0 && n2 >= 0 && n1 /= n2 | |||||
| then return (cs !! n1, cs !! n2) | |||||
| else askSkat cs | |||||
| reizen :: IO SkatEnv | |||||
| reizen = do | |||||
| cs <- shuffleCards | |||||
| let cards = distribute cs | |||||
| p1 = Reizer One $ findCards Hand1 cards | |||||
| p2 = Reizer Two $ findCards Hand2 cards | |||||
| p3 = Reizer Three $ findCards Hand3 cards | |||||
| skt = findCards SkatP cards | |||||
| (winner1, new) <- combat p2 p1 0 | |||||
| (Reizer idx _, _) <- combat p3 winner1 new | |||||
| let ps = Players (Player (if idx == One then Single else Team) One) | |||||
| (Player (if idx == Two then Single else Team) Two) | |||||
| (Player (if idx == Three then Single else Team) Three) | |||||
| sglHand = playerHand idx | |||||
| cards' = foldr (\c css -> moveCard c sglHand css) cards skt | |||||
| trumpCol <- askColour (findCards sglHand cards') | |||||
| (s1, s2) <- askSkat (findCards sglHand cards') | |||||
| let cards'' = moveCard s2 WonSingle (moveCard s1 WonSingle cards') | |||||
| return $ SkatEnv cards'' Nothing trumpCol ps | |||||
| combat :: Reizer -> Reizer -> Int -> IO (Reizer, Int) | |||||
| combat r2@(Reizer p2 h2) r1@(Reizer p1 h1) start = do | |||||
| -- advantage for h1 (being challenged) | |||||
| putStrLn $ "Player " ++ show p2 ++ " challenging " ++ show p1 | |||||
| putStrLn $ "Player " ++ show p2 ++ "'s turn" | |||||
| new <- goUp h2 start | |||||
| if new > start | |||||
| then do | |||||
| putStrLn $ "Player " ++ show p2 ++ " goes up to " ++ show new | |||||
| putStrLn $ "Player " ++ show p1 ++ "'s turn" | |||||
| yes <- goWith h1 new | |||||
| if yes then combat r2 r1 new | |||||
| else do | |||||
| putStrLn $ "Player " ++ show p1 ++ " gives up" | |||||
| putStrLn $ "Player " ++ show p2 ++ " wins" | |||||
| return (r2, new) | |||||
| else do | |||||
| putStrLn $ "Player " ++ show p2 ++ " gives up" | |||||
| putStrLn $ "Player " ++ show p1 ++ " wins" | |||||
| return (r1, start) | |||||
| @@ -0,0 +1,8 @@ | |||||
| module Render where | |||||
| import Card | |||||
| import Operations | |||||
| import Data.List | |||||
| render :: [Card] -> IO () | |||||
| render = putStrLn . intercalate "\n" . zipWith (\n c -> show n ++ ") " ++ show c) [0..] | |||||
| @@ -0,0 +1,36 @@ | |||||
| module Skat where | |||||
| import Card | |||||
| import Control.Monad.State | |||||
| import Control.Monad.Reader | |||||
| import Data.List | |||||
| data SkatEnv = SkatEnv { cards :: [CardS] | |||||
| , turnColour :: Maybe Colour | |||||
| , trumpColour :: Colour | |||||
| , players :: Players } | |||||
| deriving Show | |||||
| type Skat = StateT SkatEnv IO | |||||
| table :: Skat [Card] | |||||
| table = gets cards >>= return . foldr f [] | |||||
| where f (CardS c Table _) cs = c : cs | |||||
| f _ cs = cs | |||||
| tableS :: Skat [CardS] | |||||
| tableS = gets cards >>= return . foldr f [] | |||||
| where f c@(CardS _ Table _) cs = c : cs | |||||
| f _ cs = cs | |||||
| move :: Card -> Space -> Skat () | |||||
| move card sp = do | |||||
| cs <- gets cards | |||||
| let cs' = moveCard card sp cs | |||||
| modify (\env -> env { cards = cs' }) | |||||
| cardsAt :: Space -> Skat [Card] | |||||
| cardsAt sp = gets cards >>= return . findCards sp | |||||
| setTurnColour :: Maybe Colour -> SkatEnv -> SkatEnv | |||||
| setTurnColour col sk = sk { turnColour = col } | |||||
| @@ -0,0 +1,24 @@ | |||||
| module Utils where | |||||
| import System.Random | |||||
| import Text.Read | |||||
| shuffle :: StdGen -> [a] -> [a] | |||||
| shuffle g xs = shuffle' (randoms g) xs | |||||
| shuffle' :: [Int] -> [a] -> [a] | |||||
| shuffle' _ [] = [] | |||||
| shuffle' (i:is) xs = let (firsts, rest) = splitAt (1 + i `mod` length xs) xs | |||||
| in (last firsts) : shuffle' is (init firsts ++ rest) | |||||
| chunksOf :: Int -> [a] -> [[a]] | |||||
| chunksOf n [] = [] | |||||
| chunksOf n xs = take n xs : chunksOf n (drop n xs) | |||||
| query :: Read a => String -> IO a | |||||
| query s = do | |||||
| putStrLn s | |||||
| l <- fmap readMaybe getLine | |||||
| case l of | |||||
| Just x -> return x | |||||
| Nothing -> query s | |||||