9 Commits
Author SHA1 Message Date
christian 0984a188db set stupid bidding to 0 2022-01-19 11:47:40 +01:00
christian 444e50cdb1 add two vs bot 2022-01-19 11:43:18 +01:00
christian 3306e349d3 upgrade lts 2021-11-28 17:26:58 +01:00
christian c6f43b2c96 replace deprecated iNADDR_ANY 2021-11-28 17:20:21 +01:00
christian 195fd7ec34 upgrade matches api 2020-05-23 22:13:08 +02:00
christian 4a89eddc24 sort ouvert cards and properly compare types in sort render 2020-04-29 23:55:36 +02:00
christian dd629db320 handle ueberreizung 2020-04-07 01:34:38 +02:00
christian fac461b759 add ouvert games 2020-04-06 01:44:36 +02:00
christian 1c3f85b9a6 serialization of piles 2020-04-04 00:23:14 +02:00
19 changed files with 445 additions and 118 deletions
+6 -6
View File
@@ -47,11 +47,11 @@ runAI = do
else runAI else runAI
env :: SkatEnv env :: SkatEnv
env = SkatEnv piles Nothing (Colour Spades Einfach) playersExamp Hand1 env = SkatEnv piles Nothing (Colour Spades Einfach) playersExamp Hand1 Hand3
where piles = distribute allCards where piles = distribute allCards
envStupid :: SkatEnv envStupid :: SkatEnv
envStupid = SkatEnv piles Nothing (Colour Spades Einfach) pls2 Hand1 envStupid = SkatEnv piles Nothing (Colour Spades Einfach) pls2 Hand1 Hand3
where piles = distribute allCards where piles = distribute allCards
playersExamp :: Players playersExamp :: Players
@@ -69,22 +69,22 @@ pls2 = Players
shuffledEnv :: IO SkatEnv shuffledEnv :: IO SkatEnv
shuffledEnv = do shuffledEnv = do
cards <- shuffleCards cards <- shuffleCards
return $ SkatEnv (distribute cards) Nothing (Colour Spades Einfach) playersExamp Hand1 return $ SkatEnv (distribute cards) Nothing (Colour Spades Einfach) playersExamp Hand1 Hand3
shuffledEnv2 :: IO SkatEnv shuffledEnv2 :: IO SkatEnv
shuffledEnv2 = do shuffledEnv2 = do
cards <- shuffleCards cards <- shuffleCards
return $ SkatEnv (distribute cards) Nothing (Colour Spades Einfach) pls2 Hand1 return $ SkatEnv (distribute cards) Nothing (Colour Spades Einfach) pls2 Hand1 Hand3
env2 :: SkatEnv env2 :: SkatEnv
env2 = SkatEnv piles Nothing (Colour Hearts Einfach) playersExamp Hand2 env2 = SkatEnv piles Nothing (Colour Hearts Einfach) playersExamp Hand2 Hand3
where hand1 = [Card Eight Hearts, Card Queen Hearts, 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 Ten 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]
piles = emptyPiles hand1 hand2 hand3 [] piles = emptyPiles hand1 hand2 hand3 []
env3 :: SkatEnv env3 :: SkatEnv
env3 = SkatEnv piles Nothing (Colour Diamonds Einfach) pls2 Hand3 env3 = SkatEnv piles Nothing (Colour Diamonds Einfach) pls2 Hand3 Hand3
where hand1 = [ Card Jack Diamonds, Card Jack Clubs, Card Nine Spades, Card King Spades 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 Seven Diamonds, Card Nine Diamonds, Card Seven Clubs, Card Eight Clubs
, Card Ten Clubs, Card Eight Hearts ] , Card Ten Clubs, Card Eight Hearts ]
+2 -2
View File
@@ -14,7 +14,7 @@ pls2 = Players
(PL $ Stupid Single Hand3) (PL $ Stupid Single Hand3)
env3 :: SkatEnv env3 :: SkatEnv
env3 = SkatEnv piles Nothing (Colour Diamonds Einfach) pls2 Hand3 env3 = SkatEnv piles Nothing (Colour Diamonds Einfach) pls2 Hand3 Hand3
where hand1 = [ Card Jack Diamonds, Card Jack Clubs, Card Nine Spades, Card King Spades 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 Seven Diamonds, Card Nine Diamonds, Card Seven Clubs, Card Eight Clubs
, Card Ten Clubs, Card Eight Hearts ] , Card Ten Clubs, Card Eight Hearts ]
@@ -29,4 +29,4 @@ env3 = SkatEnv piles Nothing (Colour Diamonds Einfach) pls2 Hand3
shuffledEnv2 :: IO SkatEnv shuffledEnv2 :: IO SkatEnv
shuffledEnv2 = do shuffledEnv2 = do
cards <- shuffleCards cards <- shuffleCards
return $ SkatEnv (distribute cards) Nothing (Colour Spades Einfach) pls2 Hand1 return $ SkatEnv (distribute cards) Nothing (Colour Spades Einfach) pls2 Hand1 Hand3
+3 -1
View File
@@ -1,5 +1,5 @@
name: skat name: skat
version: 0.1.0.7 version: 0.1.0.8
github: "githubuser/skat" github: "githubuser/skat"
license: BSD3 license: BSD3
author: "flavis" author: "flavis"
@@ -34,6 +34,8 @@ dependencies:
- containers - containers
- case-insensitive - case-insensitive
- vector - vector
- transformers
- exceptions
library: library:
source-dirs: src source-dirs: src
+8 -2
View File
@@ -4,10 +4,10 @@ cabal-version: 1.12
-- --
-- see: https://github.com/sol/hpack -- see: https://github.com/sol/hpack
-- --
-- hash: 9c412ae20820c69f342fb431118c3d2be6a5461e1b5a521d92c1546f163ee94a -- hash: a2e08e04140990ba90e6d7b70c6bc70b99d073ba723efa9d5e35708995da45e1
name: skat name: skat
version: 0.1.0.7 version: 0.1.0.8
description: Please see the README on Gitea at <https://git.flavigny.de/christian/skat> description: Please see the README on Gitea at <https://git.flavigny.de/christian/skat>
homepage: https://github.com/githubuser/skat#readme homepage: https://github.com/githubuser/skat#readme
bug-reports: https://github.com/githubuser/skat/issues bug-reports: https://github.com/githubuser/skat/issues
@@ -56,12 +56,14 @@ library
, case-insensitive , case-insensitive
, containers , containers
, deepseq , deepseq
, exceptions
, mtl , mtl
, network , network
, parallel , parallel
, random , random
, split , split
, text , text
, transformers
, vector , vector
, websockets , websockets
default-language: Haskell2010 default-language: Haskell2010
@@ -81,6 +83,7 @@ executable skat-exe
, case-insensitive , case-insensitive
, containers , containers
, deepseq , deepseq
, exceptions
, mtl , mtl
, network , network
, parallel , parallel
@@ -88,6 +91,7 @@ executable skat-exe
, skat , skat
, split , split
, text , text
, transformers
, vector , vector
, websockets , websockets
default-language: Haskell2010 default-language: Haskell2010
@@ -107,6 +111,7 @@ test-suite skat-test
, case-insensitive , case-insensitive
, containers , containers
, deepseq , deepseq
, exceptions
, mtl , mtl
, network , network
, parallel , parallel
@@ -114,6 +119,7 @@ test-suite skat-test
, skat , skat
, split , split
, text , text
, transformers
, vector , vector
, websockets , websockets
default-language: Haskell2010 default-language: Haskell2010
+7 -5
View File
@@ -18,12 +18,12 @@ import qualified Skat.Player as P
data SkatEnv = SkatEnv { piles :: Piles data SkatEnv = SkatEnv { piles :: Piles
, turnColour :: Maybe TurnColour , turnColour :: Maybe TurnColour
, game :: Game , skatGame :: Game
, players :: Players , players :: Players
, currentHand :: Hand } , currentHand :: Hand
, skatSinglePlayer :: Hand }
deriving Show deriving Show
type Trick = (CardS Owner, CardS Owner, CardS Owner)
type Skat = StateT SkatEnv (WriterT [Trick] IO) type Skat = StateT SkatEnv (WriterT [Trick] IO)
runSkat :: Skat a -> SkatEnv -> IO (a, SkatEnv, [Trick]) runSkat :: Skat a -> SkatEnv -> IO (a, SkatEnv, [Trick])
@@ -38,11 +38,13 @@ execSkat :: Skat a -> SkatEnv -> IO SkatEnv
execSkat action = (fmap fst) . runWriterT . execStateT action execSkat action = (fmap fst) . runWriterT . execStateT action
instance P.MonadPlayer Skat where instance P.MonadPlayer Skat where
trump = gets $ getTrump . game trump = getTrump <$> P.game
turnColour = gets turnColour turnColour = gets turnColour
showSkat p = case P.team p of showSkat p = case P.team p of
Single -> fmap (Just . skatCards) $ gets piles Single -> fmap (Just . skatCards) $ gets piles
Team -> return Nothing Team -> return Nothing
singlePlayer = gets skatSinglePlayer
game = gets skatGame
instance P.MonadPlayerOpen Skat where instance P.MonadPlayerOpen Skat where
showPiles = gets piles showPiles = gets piles
@@ -64,7 +66,7 @@ setTurnColour col sk = sk { turnColour = col }
setCurrentHand :: Hand -> SkatEnv -> SkatEnv setCurrentHand :: Hand -> SkatEnv -> SkatEnv
setCurrentHand hand sk = sk { currentHand = hand } setCurrentHand hand sk = sk { currentHand = hand }
mkSkatEnv :: Piles -> Maybe TurnColour -> Game -> Players -> Hand -> SkatEnv mkSkatEnv :: Piles -> Maybe TurnColour -> Game -> Players -> Hand -> Hand -> SkatEnv
mkSkatEnv = SkatEnv mkSkatEnv = SkatEnv
allowedCards :: Skat [CardS Owner] allowedCards :: Skat [CardS Owner]
+1 -1
View File
@@ -15,7 +15,7 @@ data Human = Human { getTeam :: Team
instance Player Human where instance Player Human where
team = getTeam team = getTeam
hand = getHand hand = getHand
chooseCard p table _ hand = do chooseCard p table _ _ hand = do
trumpCol <- trump trumpCol <- trump
turnCol <- turnColour turnCol <- turnColour
let possible = filter (isAllowed trumpCol turnCol hand) hand let possible = filter (isAllowed trumpCol turnCol hand) hand
+22 -12
View File
@@ -47,7 +47,7 @@ instance Show (PrepOnline c) where
instance Communicator c => Player (OnlineEnv c) where instance Communicator c => Player (OnlineEnv c) where
team = getTeam team = getTeam
hand = getHand hand = getHand
chooseCard p table _ hand = runReaderT (choose table hand) p >>= \c -> return (c, p) chooseCard p table _ mayOuvert hand = runReaderT (choose table mayOuvert hand) p >>= \c -> return (c, p)
onCardPlayed p c = runReaderT (cardPlayed c) p >> return p onCardPlayed p c = runReaderT (cardPlayed c) p >> return p
instance Communicator c => Bidder (PrepOnline c) where instance Communicator c => Bidder (PrepOnline c) where
@@ -95,6 +95,8 @@ instance Communicator c => Bidder (PrepOnline c) where
liftIO $ send (prepConnection p) (BS.unpack $ encode $ GameResultsQuery res) liftIO $ send (prepConnection p) (BS.unpack $ encode $ GameResultsQuery res)
onGame p game sglPlayer = do onGame p game sglPlayer = do
liftIO $ send (prepConnection p) (BS.unpack $ encode $ GameStartQuery game sglPlayer) liftIO $ send (prepConnection p) (BS.unpack $ encode $ GameStartQuery game sglPlayer)
onNoGame p = do
liftIO $ send (prepConnection p) (BS.unpack $ encode $ NoGameQuery)
type Online a m = ReaderT (OnlineEnv a) m type Online a m = ReaderT (OnlineEnv a) m
@@ -110,27 +112,31 @@ instance MonadPlayer m => MonadPlayer (Online a m) where
trump = lift $ trump trump = lift $ trump
turnColour = lift $ turnColour turnColour = lift $ turnColour
showSkat = lift . showSkat showSkat = lift . showSkat
singlePlayer = lift singlePlayer
game = lift game
choose :: HasCard a => (Communicator c, MonadPlayer m) => [CardS Played] -> [a] -> Online c m Card choose :: (HasCard b, HasCard a) => (Communicator c, MonadPlayer m) => [CardS Played] -> Maybe [b] -> [a] -> Online c m Card
choose table hand' = do choose table mayOuvert hand' = do
let hand = sortRender Jacks $ map toCard hand' gm <- game
query (BS.unpack $ encode $ ChooseQuery hand table) let hand = sortRender (getTrump gm) $ map toCard hand'
ouvertCards = fmap (sortRender (getTrump gm) . map toCard) mayOuvert
query (BS.unpack $ encode $ ChooseQuery hand table ouvertCards)
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 mayOuvert hand'
Nothing -> choose table hand' Nothing -> choose table mayOuvert 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)
-- | QUERIES AND RESPONSES -- | QUERIES AND RESPONSES
data Query = ChooseQuery [Card] [CardS Played] data Query = ChooseQuery [Card] [CardS Played] (Maybe [Card])
| CardPlayedQuery (CardS Played) | CardPlayedQuery (CardS Played)
| GameResultsQuery Result | GameResultsQuery Result
| GameStartQuery Game Hand | GameStartQuery HideGame Hand
| BidQuery Hand Bid | BidQuery Hand Bid
| BidResponseQuery Hand Bid | BidResponseQuery Hand Bid
| AskGameQuery Bid | AskGameQuery Bid
@@ -139,6 +145,7 @@ data Query = ChooseQuery [Card] [CardS Played]
| CardsQuery [Card] | CardsQuery [Card]
| BidEvent (Maybe Bid) Hand Hand | BidEvent (Maybe Bid) Hand Hand
| ResponseEvent Bool Hand Hand | ResponseEvent Bool Hand Hand
| NoGameQuery
newtype ChosenResponse = ChosenResponse Card newtype ChosenResponse = ChosenResponse Card
newtype BidResponse = BidResponse Int newtype BidResponse = BidResponse Int
@@ -148,8 +155,9 @@ newtype GameResponse = GameResponse Game
newtype ChosenCards = ChosenCards [Card] newtype ChosenCards = ChosenCards [Card]
instance ToJSON Query where instance ToJSON Query where
toJSON (ChooseQuery hand table) = toJSON (ChooseQuery hand table mayOuvert) =
object ["query" .= ("choose_card" :: String), "hand" .= hand, "table" .= table] object [ "query" .= ("choose_card" :: String), "hand" .= hand, "table" .= table
, "single_hand" .= mayOuvert]
toJSON (CardPlayedQuery card) = toJSON (CardPlayedQuery card) =
object ["query" .= ("card_played" :: String), "card" .= card] object ["query" .= ("card_played" :: String), "card" .= card]
toJSON (GameResultsQuery result) = toJSON (GameResultsQuery result) =
@@ -157,7 +165,7 @@ instance ToJSON Query where
toJSON (GameStartQuery game sglPlayer) = toJSON (GameStartQuery game sglPlayer) =
object [ "query" .= ("start_game" :: String) object [ "query" .= ("start_game" :: String)
, "game" .= game , "game" .= game
, "single" .= toInt sglPlayer ] , "single" .= show sglPlayer ]
toJSON (BidQuery hand bid) = toJSON (BidQuery hand bid) =
object ["query" .= ("bid" :: String), "whom" .= show hand, "current" .= bid] object ["query" .= ("bid" :: String), "whom" .= show hand, "current" .= bid]
toJSON (BidResponseQuery hand bid) = toJSON (BidResponseQuery hand bid) =
@@ -183,6 +191,8 @@ instance ToJSON Query where
, "response" .= response , "response" .= response
, "reizer" .= show reizer , "reizer" .= show reizer
, "gereizter" .= show gereizter ] , "gereizter" .= show gereizter ]
toJSON NoGameQuery =
object [ "query" .= ("no_game" :: String) ]
instance FromJSON ChosenResponse where instance FromJSON ChosenResponse where
parseJSON = withObject "ChosenResponse" $ \v -> ChosenResponse parseJSON = withObject "ChosenResponse" $ \v -> ChosenResponse
+3 -3
View File
@@ -80,7 +80,7 @@ runWithPiles ps sim = runReaderT sim ps
instance Player AIEnv where instance Player AIEnv where
team = getTeam team = getTeam
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 (map toCard hand) modify $ setHand (map toCard hand)
modify $ setFallen fallen modify $ setFallen fallen
@@ -316,7 +316,7 @@ chooseSimulating = do
(PL $ Stupid.Stupid Team Hand2) (PL $ Stupid.Stupid Team Hand2)
(PL $ Stupid.Stupid Single Hand3) (PL $ Stupid.Stupid Single Hand3)
-- TODO: fix -- TODO: fix
env = mkSkatEnv piles turnCol undefined ps myHand env = mkSkatEnv piles turnCol undefined ps myHand undefined
liftIO $ evalSkat (toCard <$> (Minmax.choose depth :: Skat (CardS Owner))) env liftIO $ evalSkat (toCard <$> (Minmax.choose depth :: Skat (CardS Owner))) env
simulate :: (MonadState AIEnv m, MonadPlayerOpen m) simulate :: (MonadState AIEnv m, MonadPlayerOpen m)
@@ -337,7 +337,7 @@ simulate card = do
(PL $ mkAIEnv Team Hand2 newDepth) (PL $ mkAIEnv Team Hand2 newDepth)
(PL $ mkAIEnv Single Hand3 newDepth) (PL $ mkAIEnv Single Hand3 newDepth)
-- TODO: fix -- TODO: fix
env = mkSkatEnv piles turnCol undefined ps (next myHand) env = mkSkatEnv piles turnCol undefined ps (next myHand) undefined
-- simulate the game after playing the given card -- simulate the game after playing the given card
(sgl, tm) <- liftIO $ evalSkat (do (sgl, tm) <- liftIO $ evalSkat (do
modifyp $ playCard myHand card modifyp $ playCard myHand card
+6 -1
View File
@@ -48,13 +48,18 @@ initServer :: Net.PortNumber -> Buffering -> OnReceive -> IO ServerEnv
initServer port buffermode handler = do initServer port buffermode handler = do
sock <- Net.socket Net.AF_INET Net.Stream 0 sock <- Net.socket Net.AF_INET Net.Stream 0
Net.setSocketOption sock Net.ReuseAddr 1 Net.setSocketOption sock Net.ReuseAddr 1
Net.bind sock (Net.SockAddrInet port Net.iNADDR_ANY) addr <- Net.addrAddress <$> resolve
Net.bind sock addr
Net.listen sock 5 Net.listen sock 5
chan <- newChan chan <- newChan
forkIO $ forever $ do forkIO $ forever $ do
msg <- readChan chan -- clearing the main channel msg <- readChan chan -- clearing the main channel
return () return ()
return (ServerEnv buffermode sock chan handler) return (ServerEnv buffermode sock chan handler)
where resolve = do
let hints = Net.defaultHints { Net.addrSocketType = Net.Stream }
addrs <- Net.getAddrInfo (Just hints) (Just "127.0.0.1") (Just $ show port)
return $ head addrs
close :: ServerEnv -> IO () close :: ServerEnv -> IO ()
close = Net.close . socket close = Net.close . socket
+2 -2
View File
@@ -16,7 +16,7 @@ data Stupid = Stupid { getTeam :: Team
instance Player Stupid where instance Player Stupid where
team = getTeam team = getTeam
hand = getHand hand = getHand
chooseCard p _ _ hand = do chooseCard p _ _ _ hand = do
trumpCol <- trump trumpCol <- trump
turnCol <- turnColour turnCol <- turnColour
liftIO $ threadDelay 1000000 liftIO $ threadDelay 1000000
@@ -29,7 +29,7 @@ newtype NoBidder = NoBidder Hand
-- | no bidding from that player -- | no bidding from that player
instance Bidder NoBidder where instance Bidder NoBidder where
hand (NoBidder h) = h hand (NoBidder h) = h
askBid _ _ bid = return $ Just 20 askBid _ _ bid = return Nothing
askResponse _ _ bid = if bid < 24 then return True else return False askResponse _ _ bid = if bid < 24 then return True else return False
askGame _ _ = return $ Grand Hand askGame _ _ = return $ Grand Hand
askHand _ _ = return True askHand _ _ = return True
+104 -19
View File
@@ -2,7 +2,7 @@
module Skat.Bidding ( module Skat.Bidding (
biddingScore, Game(..), Modifier(..), isHand, getTrump, Result(..), biddingScore, Game(..), Modifier(..), isHand, getTrump, Result(..),
getResults getResults, isOuvert, isSchwarz, Bid, checkGame, HideGame(..)
) where ) where
import Data.Aeson hiding (Null, Result) import Data.Aeson hiding (Null, Result)
@@ -13,6 +13,8 @@ import Data.Ord (Down(..))
import Control.Monad import Control.Monad
import Skat.Pile import Skat.Pile
type Bid = Int
-- | different game types -- | different game types
data Game = Colour Colour Modifier data Game = Colour Colour Modifier
| Grand Modifier | Grand Modifier
@@ -22,6 +24,9 @@ data Game = Colour Colour Modifier
| NullOuvertHand | NullOuvertHand
deriving (Show, Eq) deriving (Show, Eq)
newtype HideGame = HideGame Game
deriving (Show, Eq)
instance ToJSON Game where instance ToJSON Game where
toJSON (Grand mod) = toJSON (Grand mod) =
object ["game" .= ("grand" :: String), "modifier" .= show mod] object ["game" .= ("grand" :: String), "modifier" .= show mod]
@@ -32,6 +37,13 @@ instance ToJSON Game where
toJSON NullOuvert = object ["game" .= ("nullouvert" :: String)] toJSON NullOuvert = object ["game" .= ("nullouvert" :: String)]
toJSON NullOuvertHand = object ["game" .= ("nullouverthand" :: String)] toJSON NullOuvertHand = object ["game" .= ("nullouverthand" :: String)]
instance ToJSON HideGame where
toJSON (HideGame (Grand mod)) =
object ["game" .= ("grand" :: String), "modifier" .= prettyShow mod]
toJSON (HideGame (Colour col mod)) =
object ["game" .= ("colour" :: String), "modifier" .= prettyShow mod, "colour" .= show col]
toJSON (HideGame game) = toJSON game
instance FromJSON Game where instance FromJSON Game where
parseJSON = withObject "Game" $ \v -> do parseJSON = withObject "Game" $ \v -> do
gamekind <- v .: "game" gamekind <- v .: "game"
@@ -56,7 +68,7 @@ data Modifier = Einfach
| Hand | Hand
| HandSchneider | HandSchneider
| HandSchneiderAngesagt | HandSchneiderAngesagt
| HandSchneiderSchwarz | HandSchwarz
| HandSchneiderAngesagtSchwarz | HandSchneiderAngesagtSchwarz
| HandSchwarzAngesagt | HandSchwarzAngesagt
| Ouvert | Ouvert
@@ -76,11 +88,44 @@ instance FromJSON Modifier where
_ -> return Hand _ -> return Hand
else return Einfach else return Einfach
isHand :: Modifier -> Bool prettyShow :: Modifier -> String
isHand Einfach = False prettyShow Schneider = show Einfach
isHand Schneider = False prettyShow Schwarz = show Einfach
isHand Schwarz = False prettyShow HandSchneider = show Hand
isHand _ = True prettyShow HandSchwarz = show Hand
prettyShow HandSchneiderAngesagtSchwarz = show HandSchneiderAngesagt
prettyShow mod = show mod
isHand :: Game -> Bool
isHand NullHand = True
isHand NullOuvertHand = True
isHand (Colour _ mod) = modIsHand mod
isHand (Grand mod) = modIsHand mod
isHand _ = False
modIsHand :: Modifier -> Bool
modIsHand Einfach = False
modIsHand Schneider = False
modIsHand Schwarz = False
modIsHand _ = True
isOuvert :: Game -> Bool
isOuvert NullOuvert = True
isOuvert NullOuvertHand = True
isOuvert (Grand Ouvert) = True
isOuvert (Colour _ Ouvert) = True
isOuvert _ = False
baseFactor :: Game -> Int
baseFactor (Grand _) = 24
baseFactor (Colour Clubs _) = 12
baseFactor (Colour Spades _) = 11
baseFactor (Colour Hearts _) = 10
baseFactor (Colour Diamonds _) = 9
baseFactor Null = 23
baseFactor NullHand = 35
baseFactor NullOuvert = 46
baseFactor NullOuvertHand = 59
-- | calculate the value of a game with given cards -- | calculate the value of a game with given cards
biddingScore :: HasCard c => Game -> [c] -> Int biddingScore :: HasCard c => Game -> [c] -> Int
@@ -89,10 +134,7 @@ biddingScore game@(Colour Clubs mod) cards = (spitzen game cards + modifierFa
biddingScore game@(Colour Spades mod) cards = (spitzen game cards + modifierFactor mod) * 11 biddingScore game@(Colour Spades mod) cards = (spitzen game cards + modifierFactor mod) * 11
biddingScore game@(Colour Hearts mod) cards = (spitzen game cards + modifierFactor mod) * 10 biddingScore game@(Colour Hearts mod) cards = (spitzen game cards + modifierFactor mod) * 10
biddingScore game@(Colour Diamonds mod) cards = (spitzen game cards + modifierFactor mod) * 9 biddingScore game@(Colour Diamonds mod) cards = (spitzen game cards + modifierFactor mod) * 9
biddingScore Null _ = 23 biddingScore game _ = baseFactor game
biddingScore NullHand _ = 35
biddingScore NullOuvert _ = 46
biddingScore NullOuvertHand _ = 59
-- | calculate the modifier based on the game kind -- | calculate the modifier based on the game kind
modifierFactor :: Modifier -> Int modifierFactor :: Modifier -> Int
@@ -102,7 +144,7 @@ modifierFactor Schwarz = 3
modifierFactor Hand = 2 modifierFactor Hand = 2
modifierFactor HandSchneider = 3 modifierFactor HandSchneider = 3
modifierFactor HandSchneiderAngesagt = 4 modifierFactor HandSchneiderAngesagt = 4
modifierFactor HandSchneiderSchwarz = 4 modifierFactor HandSchwarz = 4
modifierFactor HandSchneiderAngesagtSchwarz = 5 modifierFactor HandSchneiderAngesagtSchwarz = 5
modifierFactor HandSchwarzAngesagt = 6 modifierFactor HandSchwarzAngesagt = 6
modifierFactor Ouvert = 7 modifierFactor Ouvert = 7
@@ -166,16 +208,36 @@ hasWon (Grand mod) ps = let (b, mod') = meetsCall mod ps
meetsCall :: Modifier -> Piles -> (Bool, Modifier) meetsCall :: Modifier -> Piles -> (Bool, Modifier)
meetsCall Hand ps = case wonByPoints ps of meetsCall Hand ps = case wonByPoints ps of
(b, Schneider) -> (b, HandSchneider) (b, Schneider) -> (b, HandSchneider)
(b, Schwarz) -> (b, HandSchneiderSchwarz) (b, Schwarz) -> (b, HandSchwarz)
(b, Einfach) -> (b, Hand) (b, Einfach) -> (b, Hand)
meetsCall Schneider ps = case wonByPoints ps of
(b, Schneider) -> (b, Schneider)
(b, Schwarz) -> (b, Schwarz)
(b, Einfach) -> (False, Schneider)
meetsCall Schwarz ps = case wonByPoints ps of
(b, Schneider) -> (False, Schwarz)
(b, Schwarz) -> (b, Schwarz)
(b, Einfach) -> (False, Schwarz)
meetsCall HandSchneider ps = case wonByPoints ps of
(b, Schneider) -> (b, HandSchneider)
(b, Schwarz) -> (b, HandSchwarz)
(b, Einfach) -> (False, HandSchneider)
meetsCall HandSchneiderAngesagt ps = case wonByPoints ps of meetsCall HandSchneiderAngesagt ps = case wonByPoints ps of
(b, Schneider) -> (b, HandSchneiderAngesagt) (b, Schneider) -> (b, HandSchneiderAngesagt)
(b, Schwarz) -> (b, HandSchneiderAngesagtSchwarz) (b, Schwarz) -> (b, HandSchneiderAngesagtSchwarz)
(b, Einfach) -> (False, HandSchneiderAngesagt) (b, Einfach) -> (False, HandSchneiderAngesagt)
meetsCall HandSchwarz ps = case wonByPoints ps of
(b, Schneider) -> (False, HandSchwarz)
(b, Schwarz) -> (b, HandSchwarz)
(b, Einfach) -> (False, HandSchwarz)
meetsCall HandSchwarzAngesagt ps = case wonByPoints ps of meetsCall HandSchwarzAngesagt ps = case wonByPoints ps of
(b, Schneider) -> (False, HandSchwarzAngesagt) (b, Schneider) -> (False, HandSchwarzAngesagt)
(b, Schwarz) -> (b, HandSchwarzAngesagt) (b, Schwarz) -> (b, HandSchwarzAngesagt)
(b, Einfach) -> (False, HandSchwarzAngesagt) (b, Einfach) -> (False, HandSchwarzAngesagt)
meetsCall Ouvert ps = case wonByPoints ps of
(b, Schneider) -> (False, Ouvert)
(b, Schwarz) -> (b, Ouvert)
(b, Einfach) -> (False, Ouvert)
meetsCall _ ps = wonByPoints ps meetsCall _ ps = wonByPoints ps
wonByPoints :: Piles -> (Bool, Modifier) wonByPoints :: Piles -> (Bool, Modifier)
@@ -188,10 +250,33 @@ wonByPoints ps
where (sgl, _) = count ps :: (Int, Int) where (sgl, _) = count ps :: (Int, Int)
-- | get result of game -- | get result of game
getResults :: Game -> Hand -> Piles -> Piles -> Result getResults :: Game -> Bid -> Hand -> Piles -> Piles -> Result
getResults game sglPlayer before after = Result afterGame score sglPoints teamPoints getResults game bid sglPlayer before after = case checkGame bid hand game of
where (won, afterGame) = hasWon game after Just game' -> let (won, afterGame) = hasWon game' after
hand = skatCards before ++ (map toCard $ handCards sglPlayer before) gameScore = biddingScore afterGame hand
score = if won then gameScore else (-2) * gameScore
in Result afterGame score sglPoints teamPoints
Nothing -> let gameScore = baseFactor game * ceiling (fromIntegral bid / fromIntegral (baseFactor game))
score = (-2) * gameScore
in Result game score sglPoints teamPoints
where hand = skatCards before ++ (map toCard $ handCards sglPlayer before)
(sglPoints, teamPoints) = count after (sglPoints, teamPoints) = count after
gameScore = biddingScore afterGame hand
score = if won then gameScore else (-2) * gameScore checkGame :: HasCard c => Bid -> [c] -> Game -> Maybe Game
checkGame bid cards game@(Colour col mod)
| biddingScore game cards >= bid = Just game
| otherwise = upgrade mod >>= \mod' -> checkGame bid cards (Colour col mod')
checkGame bid cards game@(Grand mod)
| biddingScore game cards >= bid = Just game
| otherwise = upgrade mod >>= \mod' -> checkGame bid cards (Grand mod')
checkGame bid cards game
| biddingScore game cards >= bid = Just game
| otherwise = Nothing
upgrade :: Modifier -> Maybe Modifier
upgrade Einfach = Just Schneider
upgrade Schneider = Just Schwarz
upgrade Hand = Just HandSchneider
upgrade HandSchneider = Just HandSchwarz
upgrade HandSchneiderAngesagt = Just HandSchneiderAngesagtSchwarz
upgrade _ = Nothing
+22 -6
View File
@@ -29,7 +29,7 @@ data Type = Seven
| Ten | Ten
| Ace | Ace
| Jack | Jack
deriving (Eq, Ord, Show, Enum, Read) deriving (Eq, Ord, Show, Enum, Read, Bounded)
data NullType = NSeven data NullType = NSeven
| NEight | NEight
@@ -39,7 +39,7 @@ data NullType = NSeven
| NQueen | NQueen
| NKing | NKing
| NAce | NAce
deriving (Eq, Ord, Show, Enum, Read) deriving (Eq, Ord, Show, Enum, Read, Bounded)
instance Countable Type Int where instance Countable Type Int where
count Ace = 11 count Ace = 11
@@ -53,7 +53,7 @@ data Colour = Diamonds
| Hearts | Hearts
| Spades | Spades
| Clubs | Clubs
deriving (Eq, Ord, Show, Enum, Read) deriving (Eq, Ord, Show, Enum, Read, Bounded)
data Trump = TrumpColour Colour data Trump = TrumpColour Colour
| Jacks | Jacks
@@ -65,7 +65,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) deriving (Eq, Show, Ord, Read, Bounded)
getType :: Card -> Type getType :: Card -> Type
getType (Card t _) = t getType (Card t _) = t
@@ -117,6 +117,17 @@ instance Countable (S.Set Card) Int where
instance NFData Card where instance NFData Card where
rnf (Card t c) = t `seq` c `seq` () rnf (Card t c) = t `seq` c `seq` ()
base64table :: [Char]
base64table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
class Serialize c a where
serialize :: a -> c
deserialize :: c -> Maybe a
instance Serialize Char Card where
serialize card = base64table !! fromEnum card
deserialize char = base64table `indexOf` char >>= safeToEnum
equals :: TurnColour -> Maybe TurnColour -> Bool equals :: TurnColour -> Maybe TurnColour -> Bool
equals col (Just x) = col == x equals col (Just x) = col == x
equals col Nothing = True equals col Nothing = True
@@ -162,9 +173,11 @@ compareCards trump turnCol c1@(Card tp1 col1) c2@(Card tp2 col2) =
compareRender :: Trump -> Card -> Card -> Ordering compareRender :: Trump -> Card -> Card -> Ordering
compareRender trump c1@(Card tp1 col1) c2@(Card tp2 col2) = compareRender trump c1@(Card tp1 col1) c2@(Card tp2 col2) =
case (trp1, trp2) of case (trp1, trp2) of
(True, True) -> compare tp1 tp2 (True, True) -> case compare tp1 tp2 of
EQ -> compare col1 col2
v -> v
(False, False) -> case compare col1 col2 of (False, False) -> case compare col1 col2 of
EQ -> compare tp1 tp2 EQ -> compareTypes trump tp1 tp2
v -> v v -> v
_ -> compare trp1 trp2 _ -> compare trp1 trp2
where trp1 = isTrump trump c1 where trp1 = isTrump trump c1
@@ -185,12 +198,15 @@ compareTypes None tp1 tp2 = compare (toNullType tp1) (toNullType tp2)
toNullType Ace = NAce toNullType Ace = NAce
compareTypes _ tp1 tp2 = compare tp1 tp2 compareTypes _ tp1 tp2 = compare tp1 tp2
-- | ascending sort of cards, depending on turn colour
sortCards :: HasCard c => Trump -> Maybe TurnColour -> [c] -> [c] sortCards :: HasCard c => Trump -> Maybe TurnColour -> [c] -> [c]
sortCards trump turnCol cs = sortBy f cs sortCards trump turnCol cs = sortBy f cs
where f c1 c2 = compareCards trump turnCol (toCard c1) (toCard c2) where f c1 c2 = compareCards trump turnCol (toCard c1) (toCard c2)
-- | descending sort of cards, independent of turn colour
sortRender :: HasCard c => Trump -> [c] -> [c] sortRender :: HasCard c => Trump -> [c] -> [c]
sortRender trump cs = sortBy f cs sortRender trump cs = sortBy f cs
-- note: reversed order of c1 and c2 to get a descending sort
where f c1 c2 = compareRender trump (toCard c2) (toCard c1) where f c1 c2 = compareRender trump (toCard c2) (toCard c1)
highestCard :: HasCard c => Trump -> Maybe TurnColour -> [c] -> c highestCard :: HasCard c => Trump -> Maybe TurnColour -> [c] -> c
+87 -19
View File
@@ -1,43 +1,91 @@
module Skat.Matches ( module Skat.Matches (
singleVsBots, pvp, singleWithBidding, Match(..) singleVsBots, pvp, singleWithBidding, Match(..), Unfinished(..), continue,
Table(..), twoWithBidding, H(..), randomPositions
) where ) where
import Control.Monad.State import Control.Monad.State
import Control.Monad.Reader import Control.Monad.Reader
import System.Random (mkStdGen) import System.Random (mkStdGen, newStdGen)
import Skat import Skat
import Skat.Operations import Skat.Operations
import Skat.Player import Skat.Player as P
import Skat.Pile import Skat.Pile
import Skat.Card import Skat.Card
import Skat.Preperation import Skat.Preperation
import Skat.Bidding import Skat.Bidding
import Skat.Utils (shuffle)
import Skat.AI.Rulebased import Skat.AI.Rulebased
import Skat.AI.Online import Skat.AI.Online
import Skat.AI.Stupid import Skat.AI.Stupid
data Table = Unfinished Unfinished
| Finished Match
| Pass { tablePiles :: Piles }
deriving Show
data Match = Match { matchPiles :: Piles data Match = Match { matchPiles :: Piles
, matchResult :: Result , matchResult :: Result
, matchTricks :: [Trick] , matchTricks :: [Trick]
, matchSingle :: Hand } , matchSingle :: Hand }
deriving Show deriving Show
match :: PrepEnv -> IO (Maybe Match) data Unfinished = UnfinishedGame { unfinishedGame :: SkatEnv
, unfinishedPrep :: PrepEnv
, unfinishedTricks :: [Trick] }
| UnfinishedPrep { unfinishedPrep :: PrepEnv }
deriving Show
continue :: Communicator c => Unfinished -> c -> c -> c -> IO Table
continue (UnfinishedGame skatEnv prepEnv tricks) comm1 comm2 comm3 = do
let ps = players skatEnv
ps' = Players
(PL $ OnlineEnv (P.team $ player ps Hand1) (P.hand $ player ps Hand1) comm1)
(PL $ OnlineEnv (P.team $ player ps Hand2) (P.hand $ player ps Hand2) comm2)
(PL $ OnlineEnv (P.team $ player ps Hand3) (P.hand $ player ps Hand3) comm3)
bs = bidders prepEnv
bs' = Bidders
(BD $ PrepOnline (Skat.Preperation.hand $ bidder bs Hand1) comm1 [])
(BD $ PrepOnline (Skat.Preperation.hand $ bidder bs Hand2) comm2 [])
(BD $ PrepOnline (Skat.Preperation.hand $ bidder bs Hand3) comm3 [])
skatEnv' = skatEnv { players = ps' }
prepEnv' = prepEnv { bidders = bs' }
runGame prepEnv' skatEnv'
match :: PrepEnv -> IO Table
match prepEnv = do match prepEnv = do
maySkatEnv <- runReaderT runPreperation prepEnv (maySkatEnv, prepEnv') <- runStateT runPreperation prepEnv
case maySkatEnv of case maySkatEnv of
Just (sglPlayer, skatEnv) -> do Just skatEnv -> runGame prepEnv' skatEnv
(_, finished, tricks) <- runSkat turn skatEnv Nothing -> do
let res = getResults putStrLn "no one wanted to play"
(game skatEnv) return $ Pass $ Skat.Preperation.piles prepEnv'
sglPlayer
(Skat.piles skatEnv) runGame :: PrepEnv -> SkatEnv -> IO Table
(Skat.piles finished) runGame prepEnv skatEnv = do
publishGameResults res (bidders prepEnv) (isFinished, finalEnv, tricks) <- (flip runSkat) skatEnv $ do
return $ Just $ Match (Skat.piles skatEnv) res tricks sglPlayer -- send current table cards to clients
Nothing -> putStrLn "no one wanted to play" >> return Nothing -- only relevant if this is a continued game
-- otherwise table is empty
table <- getp tableCards
ps <- playersToList <$> gets players
mapM_ (\card -> mapM_ (\p -> onCardPlayed p card) ps) (reverse table)
-- run game
turn
-- return if game has finished
gameOver
if isFinished then do
let res = getResults
(skatGame skatEnv)
(Skat.Preperation.current prepEnv)
(skatSinglePlayer skatEnv)
(Skat.Preperation.piles prepEnv)
(Skat.piles finalEnv)
publishGameResults res (bidders prepEnv)
return $ Finished $ Match (Skat.Preperation.piles prepEnv) res tricks (skatSinglePlayer skatEnv)
else do -- if not finished an error has occured, thus returning unfinished game state
return $ Unfinished $ UnfinishedGame finalEnv prepEnv tricks
-- | predefined card distribution for testing purposes -- | predefined card distribution for testing purposes
cardDistr :: Piles cardDistr :: Piles
@@ -60,7 +108,7 @@ singleVsBots comm = do
(PL $ OnlineEnv Team Hand1 comm) (PL $ OnlineEnv Team Hand1 comm)
(PL $ Stupid Team Hand2) (PL $ Stupid Team Hand2)
(PL $ mkAIEnv Single Hand3 10) (PL $ mkAIEnv Single Hand3 10)
env = SkatEnv (distribute cards) Nothing (Colour Spades Einfach) ps Hand1 env = SkatEnv (distribute cards) Nothing (Colour Spades Einfach) ps Hand1 Hand3
void $ evalSkat turn env void $ evalSkat turn env
singleWithBidding :: Communicator c => c -> IO () singleWithBidding :: Communicator c => c -> IO ()
@@ -72,10 +120,30 @@ singleWithBidding comm = do
(BD $ PrepOnline Hand1 comm h1) (BD $ PrepOnline Hand1 comm h1)
(BD $ NoBidder Hand2) (BD $ NoBidder Hand2)
(BD $ NoBidder Hand3) (BD $ NoBidder Hand3)
env = PrepEnv ps bs env = makePrep ps bs
void $ match env void $ match env
pvp :: Communicator c => c -> c -> c -> IO (Maybe Match) --- helper object for twoWithBidding
data H = P1 | P2 | AI
randomPositions :: IO [H]
randomPositions = do
gen <- newStdGen
return $ shuffle gen [P1, P2, AI]
twoWithBidding :: Communicator c => [H] -> c -> c -> IO ()
twoWithBidding positions comm1 comm2 = do
cards <- shuffleCards
let bds = zipWith mkBidder [Hand1, Hand2, Hand3] positions
ps = distribute cards
mkBidder hand P1 = BD $ PrepOnline hand comm1 (map toCard $ handCards hand ps)
mkBidder hand P2 = BD $ PrepOnline hand comm2 (map toCard $ handCards hand ps)
mkBidder hand AI = BD $ NoBidder hand
bs = Bidders (bds !! 0) (bds !! 1) (bds !! 2)
env = makePrep ps bs
void $ match env
pvp :: Communicator c => c -> c -> c -> IO Table
pvp comm1 comm2 comm3 = do pvp comm1 comm2 comm3 = do
cards <- shuffleCards cards <- shuffleCards
let ps = distribute cards let ps = distribute cards
@@ -86,5 +154,5 @@ pvp comm1 comm2 comm3 = do
(BD $ PrepOnline Hand1 comm1 $ h1) (BD $ PrepOnline Hand1 comm1 $ h1)
(BD $ PrepOnline Hand2 comm2 $ h2) (BD $ PrepOnline Hand2 comm2 $ h2)
(BD $ PrepOnline Hand3 comm3 $ h3) (BD $ PrepOnline Hand3 comm3 $ h3)
env = PrepEnv ps bs env = makePrep ps bs
match env match env
+41 -9
View File
@@ -1,9 +1,11 @@
module Skat.Operations ( module Skat.Operations (
turn, turnGeneric, play, playOpen, turn, turnGeneric, play, playOpen,
play_, sortRender, undo_ play_, sortRender, undo_, gameOver
) where ) where
import Control.Monad.State import Control.Monad.State
import Control.Monad.Catch
import Control.Exception hiding (catch, bracketOnError)
import Control.Monad.Writer (tell) import Control.Monad.Writer (tell)
import System.Random (newStdGen, randoms) import System.Random (newStdGen, randoms)
import Data.List import Data.List
@@ -14,8 +16,10 @@ import Skat
import Skat.Card import Skat.Card
import Skat.Pile import Skat.Pile
import Skat.Player (chooseCard, Players(..), Player(..), PL(..), import Skat.Player (chooseCard, Players(..), Player(..), PL(..),
updatePlayer, playersToList, player, MonadPlayer, getSinglePlayer, trump) updatePlayer, playersToList, player, MonadPlayer, getSinglePlayer, trump, game,
singlePlayer)
import Skat.Utils (shuffle) import Skat.Utils (shuffle)
import Skat.Bidding
play_ :: HasCard c => c -> Skat () play_ :: HasCard c => c -> Skat ()
play_ card = do play_ card = do
@@ -43,19 +47,34 @@ 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
over <- getp $ handEmpty n
trCol <- trump trCol <- trump
case length table of case length table of
0 -> playFunc p >> modify (setCurrentHand $ next n) >> turnGeneric playFunc depth 0 -> do
catchAll
(do
playFunc p
modify (setCurrentHand $ next n)
turnGeneric playFunc depth)
(\_ -> countGame)
1 -> do 1 -> do
modify $ setTurnColour modify $ setTurnColour
(Just $ effectiveColour trCol $ head table) (Just $ effectiveColour trCol $ head table)
playFunc p catchAll
modify (setCurrentHand $ next n) (do
turnGeneric playFunc depth playFunc p
2 -> playFunc p >> modify (setCurrentHand $ next n) >> turnGeneric playFunc depth modify (setCurrentHand $ next n)
turnGeneric playFunc depth)
(\_ -> countGame)
2 -> do
catchAll
(do
playFunc p
modify (setCurrentHand $ next n)
turnGeneric playFunc depth)
(\_ -> countGame)
3 -> do 3 -> do
w <- evaluateTable w <- evaluateTable
over <- gameOver
if depth <= 1 || over if depth <= 1 || over
then countGame then countGame
else modify (setCurrentHand w) >> turnGeneric playFunc (depth - 1) else modify (setCurrentHand w) >> turnGeneric playFunc (depth - 1)
@@ -86,7 +105,10 @@ play p = do
trump <- trump trump <- trump
cards <- getp $ handCards (hand p) cards <- getp $ handCards (hand p)
fallen <- getp played fallen <- getp played
(card, p') <- chooseCard p table fallen cards ouvert <- isOuvert <$> game
mayOuvert <- if ouvert then Just <$> (singlePlayer >>= getp . handCards)
else return Nothing
(card, p') <- chooseCard p table fallen mayOuvert cards
modifyPlayers $ updatePlayer p' modifyPlayers $ updatePlayer p'
modifyp $ playCard (hand p) card modifyp $ playCard (hand p) card
ps <- fmap playersToList $ gets players ps <- fmap playersToList $ gets players
@@ -101,3 +123,13 @@ playOpen p = do
card <- chooseCardOpen p card <- chooseCardOpen p
modifyp $ playCard (hand p) card modifyp $ playCard (hand p) card
return card return card
gameOver :: Skat Bool
gameOver = do
tr <- trump
case tr of
None -> do
singleLost <- gets piles >>= return . not . (Single `isSchwarz`)
if singleLost then return True
else gets currentHand >>= getp . handCards >>= return . null
_ -> gets currentHand >>= getp . handCards >>= return . null
+52 -1
View File
@@ -6,6 +6,9 @@
module Skat.Pile where module Skat.Pile where
import Control.Monad.State
import Control.Monad.Trans.Maybe
import Prelude hiding (lookup) import Prelude hiding (lookup)
import qualified Data.Map.Strict as M import qualified Data.Map.Strict as M
import qualified Data.Vector as V import qualified Data.Vector as V
@@ -15,6 +18,8 @@ import Data.Maybe
import Data.Aeson import Data.Aeson
import Control.Exception import Control.Exception
import Data.List (delete) import Data.List (delete)
import Text.Read (readMaybe)
import Debug.Trace
import Skat.Card import Skat.Card
import Skat.Utils import Skat.Utils
@@ -41,7 +46,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) deriving (Show, Eq, Ord, Read, Enum, Bounded)
toInt :: Hand -> Int toInt :: Hand -> Int
toInt Hand1 = 1 toInt Hand1 = 1
@@ -61,12 +66,33 @@ prev Hand3 = Hand2
data Owner = P Hand | S data Owner = P Hand | S
deriving (Show, Eq, Ord, Read) deriving (Show, Eq, Ord, Read)
instance Enum Owner where
fromEnum (P hand) = fromEnum hand
fromEnum S = 3
toEnum 0 = P Hand1
toEnum 1 = P Hand2
toEnum 2 = P Hand3
toEnum 3 = S
instance Bounded Owner where
maxBound = S
minBound = P Hand1
instance ToJSON Owner where instance ToJSON Owner where
toJSON (P hand) = object ["owner" .= show hand] toJSON (P hand) = object ["owner" .= show hand]
toJSON S = object ["owner" .= ("skat" :: String) ] toJSON S = object ["owner" .= ("skat" :: String) ]
instance Serialize String (CardS Owner) where
serialize (CardS card owner) = show (fromEnum owner) ++ [serialize card]
deserialize str = (flip evalState) str $ runMaybeT $ do
owner <- pop >>= MaybeT . return . (>>= safeToEnum) . readMaybe . (:[])
card <- pop >>= MaybeT . return . deserialize
return $ CardS card owner
type Played = Owner -- TODO: remove type Played = Owner -- TODO: remove
type Trick = (CardS Owner, CardS Owner, CardS Owner)
data Piles = Piles { _hand1 :: [CardS Owner] data Piles = Piles { _hand1 :: [CardS Owner]
, _hand2 :: [CardS Owner] , _hand2 :: [CardS Owner]
, _hand3 :: [CardS Owner] , _hand3 :: [CardS Owner]
@@ -185,3 +211,28 @@ distribute cards = emptyPiles hand1 hand2 hand3 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]
instance Serialize String Piles where
serialize piles = sers (_hand1 piles) ++ sers (_hand2 piles) ++ sers (_hand3 piles)
++ sers (_skat piles)
where sers cards = map (serialize . toCard) cards
deserialize str = (flip evalState) str $ runMaybeT $ do
hand1 <- takeG 10 >>= mapM deser
hand2 <- takeG 10 >>= mapM deser
hand3 <- takeG 10 >>= mapM deser
skat <- takeG 2 >>= mapM deser
return $ emptyPiles hand1 hand2 hand3 skat
where deser char = MaybeT $ return $ deserialize char
instance Serialize String [Trick] where
serialize [] = ""
serialize ((c1, c2, c3):tricks) = serialize c1 ++ serialize c2 ++ serialize c3
++ serialize tricks
deserialize str = (flip evalState) str $ runMaybeT $ reverse <$> go []
where go acc = do
empty <- isEmpty
if empty then return acc else do
card1 <- takeG 2 >>= MaybeT . return . deserialize
card2 <- takeG 2 >>= MaybeT . return . deserialize
card3 <- takeG 2 >>= MaybeT . return . deserialize
go ((card1, card2, card3):acc)
+11 -4
View File
@@ -6,11 +6,14 @@ import Control.Monad.IO.Class
import Skat.Card import Skat.Card
import Skat.Pile import Skat.Pile
import Skat.Bidding
class (Monad m, MonadIO m) => MonadPlayer m where class (Monad m, MonadIO m) => MonadPlayer m where
trump :: m Trump trump :: m Trump
turnColour :: m (Maybe TurnColour) turnColour :: m (Maybe TurnColour)
showSkat :: Player p => p -> m (Maybe [Card]) showSkat :: Player p => p -> m (Maybe [Card])
singlePlayer :: m Hand
game :: m Game
class (Monad m, MonadIO m, MonadPlayer m) => MonadPlayerOpen m where class (Monad m, MonadIO m, MonadPlayer m) => MonadPlayerOpen m where
showPiles :: m (Piles) showPiles :: m (Piles)
@@ -18,10 +21,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 :: (HasCard c, MonadPlayer m) chooseCard :: (HasCard d, HasCard c, MonadPlayer m)
=> p => p
-> [CardS Played] -> [CardS Played]
-> [CardS Played] -> [CardS Played]
-> Maybe [d]
-> [c] -> [c]
-> m (Card, p) -> m (Card, p)
onCardPlayed :: MonadPlayer m onCardPlayed :: MonadPlayer m
@@ -37,7 +41,10 @@ class Player p where
let table = tableCards piles let table = tableCards piles
fallen = played piles fallen = played piles
myCards = handCards (hand p) piles myCards = handCards (hand p) piles
fst <$> chooseCard p table fallen myCards ouvert <- isOuvert <$> game
mayOuvert <- if ouvert then Just <$> (singlePlayer >>= \hnd -> return $ handCards hnd piles)
else return Nothing
fst <$> chooseCard p table fallen mayOuvert myCards
data PL = forall p. (Show p, Player p) => PL p data PL = forall p. (Show p, Player p) => PL p
@@ -47,8 +54,8 @@ instance Show PL where
instance Player PL where instance Player PL where
team (PL p) = team p team (PL p) = team p
hand (PL p) = hand p hand (PL p) = hand p
chooseCard (PL p) table fallen hand = do chooseCard (PL p) table fallen mayOuvert hand = do
(v, a) <- chooseCard p table fallen hand (v, a) <- chooseCard p table fallen mayOuvert hand
return $ (v, PL a) return $ (v, PL a)
onCardPlayed (PL p) card = do onCardPlayed (PL p) card = do
v <- onCardPlayed p card v <- onCardPlayed p card
+28 -23
View File
@@ -3,11 +3,11 @@
module Skat.Preperation ( module Skat.Preperation (
Bidder(..), Bid, BD(..), Bidders(..), PrepEnv(..), runPreperation, Bidder(..), Bid, BD(..), Bidders(..), PrepEnv(..), runPreperation,
publishGameResults publishGameResults, bidder, makePrep
) where ) where
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Control.Monad.Reader import Control.Monad.State
import Skat.Pile import Skat.Pile
import Skat.Card import Skat.Card
@@ -15,13 +15,15 @@ import Skat.Player (PL, Players(..))
import Skat.Bidding import Skat.Bidding
import Skat (SkatEnv, mkSkatEnv) import Skat (SkatEnv, mkSkatEnv)
type Bid = Int
data PrepEnv = PrepEnv { piles :: Piles data PrepEnv = PrepEnv { piles :: Piles
, bidders :: Bidders } , bidders :: Bidders
, current :: Bid }
deriving Show deriving Show
type Preperation = ReaderT PrepEnv IO makePrep :: Piles -> Bidders -> PrepEnv
makePrep ps bd = PrepEnv ps bd 0
type Preperation = StateT PrepEnv IO
class Bidder a where class Bidder a where
hand :: a -> Hand hand :: a -> Hand
@@ -36,10 +38,12 @@ class Bidder a where
onBid _ _ _ _ = return () onBid _ _ _ _ = return ()
onResponse :: MonadIO m => a -> Bool -> Hand -> Hand -> m () onResponse :: MonadIO m => a -> Bool -> Hand -> Hand -> m ()
onResponse _ _ _ _ = return () onResponse _ _ _ _ = return ()
onGame :: MonadIO m => a -> Game -> Hand -> m () onGame :: MonadIO m => a -> HideGame -> Hand -> m ()
onGame _ _ _ = return () onGame _ _ _ = return ()
onResult :: MonadIO m => a -> Result -> m () onResult :: MonadIO m => a -> Result -> m ()
onResult _ _ = return () onResult _ _ = return ()
onNoGame :: MonadIO m => a -> m ()
onNoGame _ = return ()
-- | trick to allow heterogenous bidder list -- | trick to allow heterogenous bidder list
data BD = forall b. (Show b, Bidder b) => BD b data BD = forall b. (Show b, Bidder b) => BD b
@@ -60,6 +64,7 @@ instance Bidder BD where
onResult (BD b) = onResult b onResult (BD b) = onResult b
onBid (BD b) = onBid b onBid (BD b) = onBid b
onResponse (BD b) = onResponse b onResponse (BD b) = onResponse b
onNoGame (BD b) = onNoGame b
data Bidders = Bidders BD BD BD data Bidders = Bidders BD BD BD
deriving Show deriving Show
@@ -75,9 +80,9 @@ toPlayers single (Bidders b1 b2 b3) =
(toPlayer b2 $ if single == Hand2 then Single else Team) (toPlayer b2 $ if single == Hand2 then Single else Team)
(toPlayer b3 $ if single == Hand3 then Single else Team) (toPlayer b3 $ if single == Hand3 then Single else Team)
runPreperation :: Preperation (Maybe (Hand, SkatEnv)) runPreperation :: Preperation (Maybe SkatEnv)
runPreperation = do runPreperation = do
bds <- asks bidders bds <- gets bidders
onStart (bidder bds Hand1) onStart (bidder bds Hand1)
onStart (bidder bds Hand2) onStart (bidder bds Hand2)
onStart (bidder bds Hand3) onStart (bidder bds Hand3)
@@ -87,9 +92,9 @@ runPreperation = do
bid <- askBid (bidder bds finalWinner) finalWinner 0 bid <- askBid (bidder bds finalWinner) finalWinner 0
publishBid bid finalWinner finalWinner publishBid bid finalWinner finalWinner
case bid of case bid of
Just val -> (Just . (finalWinner,)) <$> initGame finalWinner val Just val -> Just <$> initGame finalWinner val
Nothing -> return Nothing Nothing -> publishNoGame >> return Nothing
else (Just . (finalWinner,)) <$> initGame finalWinner finalBid else Just <$> initGame finalWinner finalBid
runBidding :: Bid -> BD -> BD -> Preperation (Hand, Bid) runBidding :: Bid -> BD -> BD -> Preperation (Hand, Bid)
runBidding startingBid reizer gereizter = do runBidding startingBid reizer gereizter = do
@@ -98,6 +103,7 @@ runBidding startingBid reizer gereizter = do
Just val Just val
| val > startingBid -> do | val > startingBid -> do
publishBid first (hand reizer) (hand gereizter) publishBid first (hand reizer) (hand gereizter)
modify $ \env -> env { current = val }
response <- askResponse gereizter (hand reizer) val response <- askResponse gereizter (hand reizer) val
publishResponse response (hand reizer) (hand gereizter) publishResponse response (hand reizer) (hand gereizter)
if response then runBidding val reizer gereizter if response then runBidding val reizer gereizter
@@ -111,8 +117,8 @@ runBidding startingBid reizer gereizter = do
initGame :: Hand -> Bid -> Preperation SkatEnv initGame :: Hand -> Bid -> Preperation SkatEnv
initGame single bid = do initGame single bid = do
ps <- asks piles ps <- gets piles
bds <- asks bidders bds <- gets bidders
-- ask if player wants to play hand -- ask if player wants to play hand
noSkat <- askHand (bidder bds single) bid noSkat <- askHand (bidder bds single) bid
-- either return piles or ask for skat cards and modify piles -- either return piles or ask for skat cards and modify piles
@@ -122,19 +128,15 @@ initGame single bid = do
-- publish game start -- publish game start
publishGameStart game single publishGameStart game single
-- construct skat env -- construct skat env
return $ mkSkatEnv ps' Nothing game (toPlayers single bds) Hand1 return $ mkSkatEnv ps' Nothing game (toPlayers single bds) Hand1 single
handleGame :: BD -> Bid -> Bool -> Preperation Game handleGame :: BD -> Bid -> Bool -> Preperation Game
handleGame bd bid noSkat = do handleGame bd bid noSkat = do
cards <- (\ps -> map toCard (handCards (hand bd) ps) ++ skatCards ps) <$> gets piles
-- ask bidder for game -- ask bidder for game
proposal <- askGame bd bid proposal <- askGame bd bid
-- check if proposal is allowed -- check if proposal is allowed
case proposal of if isHand proposal == noSkat then return proposal else handleGame bd bid noSkat
g@(Colour col mod) -> if isHand mod == noSkat
then return g else handleGame bd bid noSkat
g@(Grand mod) -> if isHand mod == noSkat
then return g else handleGame bd bid noSkat
g -> return g
handleSkat :: BD -> Bid -> Piles -> Preperation Piles handleSkat :: BD -> Bid -> Piles -> Preperation Piles
handleSkat bd bid ps = do handleSkat bd bid ps = do
@@ -152,7 +154,7 @@ publishGameResults res bidders = do
onResult (bidder bidders Hand3) res onResult (bidder bidders Hand3) res
publishGameStart :: Game -> Hand -> Preperation () publishGameStart :: Game -> Hand -> Preperation ()
publishGameStart game sglPlayer = mapBidders (\b -> onGame b game sglPlayer) publishGameStart game sglPlayer = mapBidders (\b -> onGame b (HideGame game) sglPlayer)
publishBid :: Maybe Bid -> Hand -> Hand -> Preperation () publishBid :: Maybe Bid -> Hand -> Hand -> Preperation ()
publishBid bid reizer gereizter = mapBidders (\b -> onBid b bid reizer gereizter) publishBid bid reizer gereizter = mapBidders (\b -> onBid b bid reizer gereizter)
@@ -160,9 +162,12 @@ publishBid bid reizer gereizter = mapBidders (\b -> onBid b bid reizer gereizter
publishResponse :: Bool -> Hand -> Hand -> Preperation () publishResponse :: Bool -> Hand -> Hand -> Preperation ()
publishResponse response reizer gereizter = mapBidders (\b -> onResponse b response reizer gereizter) publishResponse response reizer gereizter = mapBidders (\b -> onResponse b response reizer gereizter)
publishNoGame :: Preperation ()
publishNoGame = mapBidders onNoGame
mapBidders :: (BD -> Preperation ()) -> Preperation () mapBidders :: (BD -> Preperation ()) -> Preperation ()
mapBidders f = do mapBidders f = do
bds <- asks bidders bds <- gets bidders
f (bidder bds Hand1) f (bidder bds Hand1)
f (bidder bds Hand2) f (bidder bds Hand2)
f (bidder bds Hand3) f (bidder bds Hand3)
+39 -1
View File
@@ -1,7 +1,12 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Skat.Utils where module Skat.Utils where
import Control.Monad.State
import Control.Monad.Trans.Maybe
import System.Random import System.Random
import Text.Read import Text.Read hiding (get, lift)
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') import Data.List (foldl')
@@ -57,3 +62,36 @@ instance Stringy B.ByteString where
instance Stringy T.Text where instance Stringy T.Text where
toString = T.unpack toString = T.unpack
fromString = T.pack fromString = T.pack
indexOf :: Eq a => [a] -> a -> Maybe Int
indexOf [] _ = Nothing
indexOf (x:xs) item
| x == item = Just 0
| otherwise = (1+) <$> xs `indexOf` item
type Generator c = MaybeT (State [c])
pop :: Generator c c
pop = do
cs <- get
if null cs then mzero else put (tail cs) >> return (head cs)
isEmpty :: Generator c Bool
isEmpty = get >>= return . null
takeG :: Int -> Generator c [c]
takeG n = do
cs <- lift get
if length cs >= n
then do
put (drop n cs)
return (take n cs)
else mzero
-- forall is needed to allow scoped type variables
safeToEnum :: forall a. (Enum a, Bounded a) => Int -> Maybe a
safeToEnum n
| maxN < n || minN > n = Nothing
| otherwise = Just $ toEnum n
where maxN = fromEnum (maxBound :: a)
minN = fromEnum (minBound :: a)
+1 -1
View File
@@ -17,7 +17,7 @@
# #
# resolver: ./custom-snapshot.yaml # resolver: ./custom-snapshot.yaml
# resolver: https://example.com/snapshots/2018-01-01.yaml # resolver: https://example.com/snapshots/2018-01-01.yaml
resolver: lts-14.3 resolver: lts-18.18
# User packages to be built. # User packages to be built.
# Various formats can be used as shown in the example below. # Various formats can be used as shown in the example below.