| @@ -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 | |||