| @@ -15,6 +15,8 @@ import qualified Skat.Player.Utils as P | |||||
| import Skat.Pile | import Skat.Pile | ||||
| import Skat.Card | import Skat.Card | ||||
| import Skat.Render | import Skat.Render | ||||
| import Skat.Preperation | |||||
| import Skat.Bidding | |||||
| class Communicator a where | class Communicator a where | ||||
| send :: a -> String -> IO () | send :: a -> String -> IO () | ||||
| @@ -32,16 +34,61 @@ data OnlineEnv c = OnlineEnv { getTeam :: Team | |||||
| , getHand :: Hand | , getHand :: Hand | ||||
| , connection :: c } | , connection :: c } | ||||
| data PrepOnline c = PrepOnline { prepHand :: Hand | |||||
| , prepConnection :: c | |||||
| , prepCards :: [Card] } | |||||
| instance Show (OnlineEnv c) where | instance Show (OnlineEnv c) where | ||||
| show _ = "An online env" | show _ = "An online env" | ||||
| instance Show (PrepOnline c) where | |||||
| show _ = "An online prep env" | |||||
| 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 _ hand = runReaderT (choose table hand) p >>= \c -> return (c, p) | ||||
| onCardPlayed p c = runReaderT (cardPlayed c) p >> return p | onCardPlayed p c = runReaderT (cardPlayed c) p >> return p | ||||
| onGameResults p res = runReaderT (onResults res) p | onGameResults p res = runReaderT (onResults res) p | ||||
| onGameStart p singlePlayer = runReaderT (onStart singlePlayer) p | |||||
| onGameStart p singlePlayer = runReaderT (onStartOnline singlePlayer) p | |||||
| instance Communicator c => Bidder (PrepOnline c) where | |||||
| hand = prepHand | |||||
| askBid p against bid = do | |||||
| liftIO $ send (prepConnection p) (BS.unpack $ encode $ BidQuery against bid) | |||||
| r <- liftIO $ receive (prepConnection p) | |||||
| case decode (BS.pack r) of | |||||
| Just (BidResponse newBid) -> do | |||||
| if newBid > bid then return $ Just newBid else return Nothing | |||||
| Nothing -> askBid p against bid | |||||
| askResponse p bidder bid = do | |||||
| liftIO $ send (prepConnection p) (BS.unpack $ encode $ BidResponseQuery bidder bid) | |||||
| r <- liftIO $ receive (prepConnection p) | |||||
| case decode (BS.pack r) of | |||||
| Just (YesNo value) -> return value | |||||
| Nothing -> askResponse p bidder bid | |||||
| askGame p bid = do | |||||
| liftIO $ send (prepConnection p) (BS.unpack $ encode $ AskGameQuery bid) | |||||
| r <- liftIO $ receive (prepConnection p) | |||||
| case decode (BS.pack r) of | |||||
| Just (GameResponse game) -> return game | |||||
| Nothing -> askGame p bid | |||||
| askHand p bid = do | |||||
| liftIO $ send (prepConnection p) (BS.unpack $ encode $ AskHandQuery) | |||||
| r <- liftIO $ receive (prepConnection p) | |||||
| case decode (BS.pack r) of | |||||
| Just (YesNo value) -> return value | |||||
| Nothing -> askHand p bid | |||||
| askSkat p bid cards = do | |||||
| liftIO $ send (prepConnection p) (BS.unpack $ encode $ AskSkatQuery cards bid) | |||||
| r <- liftIO $ receive (prepConnection p) | |||||
| case decode (BS.pack r) of | |||||
| Just (ChosenCards cards) -> return cards | |||||
| Nothing -> askSkat p bid cards | |||||
| toPlayer p tm = PL $ OnlineEnv tm (prepHand p) (prepConnection p) | |||||
| onStart p = do | |||||
| let cards = prepCards p | |||||
| liftIO $ send (prepConnection p) (BS.unpack $ encode $ CardsQuery cards) | |||||
| type Online a m = ReaderT (OnlineEnv a) m | type Online a m = ReaderT (OnlineEnv a) m | ||||
| @@ -75,18 +122,30 @@ cardPlayed card = query (BS.unpack $ encode $ CardPlayedQuery card) | |||||
| onResults :: (Communicator c, MonadIO m) => (Int, Int) -> Online c m () | onResults :: (Communicator c, MonadIO m) => (Int, Int) -> Online c m () | ||||
| onResults (sgl, tm) = query (BS.unpack $ encode $ GameResultsQuery sgl tm) | onResults (sgl, tm) = query (BS.unpack $ encode $ GameResultsQuery sgl tm) | ||||
| onStart :: (Communicator c, MonadPlayer m) => Hand -> Online c m () | |||||
| onStart singlePlayer = do | |||||
| onStartOnline :: (Communicator c, MonadPlayer m) => Hand -> Online c m () | |||||
| onStartOnline singlePlayer = do | |||||
| trCol <- trumpColour | trCol <- trumpColour | ||||
| ownHand <- asks getHand | ownHand <- asks getHand | ||||
| query (BS.unpack $ encode $ GameStartQuery trCol ownHand singlePlayer) | query (BS.unpack $ encode $ GameStartQuery trCol ownHand singlePlayer) | ||||
| -- | QUERIES AND RESPONSES | |||||
| data Query = ChooseQuery [Card] [CardS Played] | data Query = ChooseQuery [Card] [CardS Played] | ||||
| | CardPlayedQuery (CardS Played) | | CardPlayedQuery (CardS Played) | ||||
| | GameResultsQuery Int Int | | GameResultsQuery Int Int | ||||
| | GameStartQuery Colour Hand Hand | | GameStartQuery Colour Hand Hand | ||||
| data Response = ChosenResponse Card | |||||
| | BidQuery Hand Bid | |||||
| | BidResponseQuery Hand Bid | |||||
| | AskGameQuery Bid | |||||
| | AskHandQuery | |||||
| | AskSkatQuery [Card] Bid | |||||
| | CardsQuery [Card] | |||||
| newtype ChosenResponse = ChosenResponse Card | |||||
| newtype BidResponse = BidResponse Int | |||||
| newtype YesNo = YesNo Bool | |||||
| newtype GameResponse = GameResponse Game | |||||
| newtype ChosenCards = ChosenCards [Card] | |||||
| instance ToJSON Query where | instance ToJSON Query where | ||||
| toJSON (ChooseQuery hand table) = | toJSON (ChooseQuery hand table) = | ||||
| @@ -97,8 +156,34 @@ instance ToJSON Query where | |||||
| object ["query" .= ("results" :: String), "single" .= sgl, "team" .= tm] | object ["query" .= ("results" :: String), "single" .= sgl, "team" .= tm] | ||||
| toJSON (GameStartQuery trumps handNo sglPlayer) = | toJSON (GameStartQuery trumps handNo sglPlayer) = | ||||
| object ["query" .= ("start_game" :: String), "trumps" .= show trumps, | object ["query" .= ("start_game" :: String), "trumps" .= show trumps, | ||||
| "hand" .= toInt handNo, "single" .= toInt sglPlayer] | |||||
| instance FromJSON Response where | |||||
| "hand" .= toInt handNo, "single" .= toInt sglPlayer ] | |||||
| toJSON (BidQuery hand bid) = | |||||
| object ["query" .= ("bid" :: String), "whom" .= show hand ] | |||||
| toJSON (BidResponseQuery hand bid) = | |||||
| object ["query" .= ("bid_response" :: String), "from" .= show hand ] | |||||
| toJSON (AskHandQuery) = | |||||
| object ["query" .= ("play_hand" :: String)] | |||||
| toJSON (AskSkatQuery cards bid) = | |||||
| object ["query" .= ("skat" :: String), "cards" .= cards, "bid" .= bid ] | |||||
| toJSON (CardsQuery cards) = | |||||
| object ["query" .= ("cards" :: String), "cards" .= cards ] | |||||
| instance FromJSON ChosenResponse where | |||||
| parseJSON = withObject "ChosenResponse" $ \v -> ChosenResponse | parseJSON = withObject "ChosenResponse" $ \v -> ChosenResponse | ||||
| <$> v .: "card" | <$> v .: "card" | ||||
| instance FromJSON BidResponse where | |||||
| parseJSON = withObject "BidResponse" $ \v -> BidResponse | |||||
| <$> v .: "bid" | |||||
| instance FromJSON YesNo where | |||||
| parseJSON = withObject "BidYesNo" $ \v -> YesNo | |||||
| <$> v .: "yesno" | |||||
| instance FromJSON GameResponse where | |||||
| parseJSON = withObject "GameResponse" $ \v -> GameResponse | |||||
| <$> v .: "game" | |||||
| instance FromJSON ChosenCards where | |||||
| parseJSON = withObject "ChosenCards" $ \v -> ChosenCards | |||||
| <$> v .: "cards" | |||||
| @@ -3,6 +3,7 @@ module Skat.AI.Stupid where | |||||
| import Skat.Player | import Skat.Player | ||||
| import Skat.Pile | import Skat.Pile | ||||
| import Skat.Card | import Skat.Card | ||||
| import Skat.Preperation | |||||
| data Stupid = Stupid { getTeam :: Team | data Stupid = Stupid { getTeam :: Team | ||||
| , getHand :: Hand } | , getHand :: Hand } | ||||
| @@ -16,3 +17,17 @@ instance Player Stupid where | |||||
| turnCol <- turnColour | turnCol <- turnColour | ||||
| let possible = filter (isAllowed trumpCol turnCol hand) hand | let possible = filter (isAllowed trumpCol turnCol hand) hand | ||||
| return (toCard $ head possible, p) | return (toCard $ head possible, p) | ||||
| newtype NoBidder = NoBidder Hand | |||||
| deriving Show | |||||
| -- | no bidding from that player | |||||
| instance Bidder NoBidder where | |||||
| hand (NoBidder h) = h | |||||
| askBid _ _ _ = return Nothing | |||||
| askResponse _ _ _ = return False | |||||
| askGame _ _ = undefined -- never called | |||||
| askHand _ _ = return False -- never called | |||||
| askSkat _ _ _ = undefined -- never called | |||||
| toPlayer (NoBidder h) team = PL $ Stupid team h | |||||
| onStart _ = return () | |||||
| @@ -1,7 +1,11 @@ | |||||
| {-# LANGUAGE OverloadedStrings #-} | |||||
| module Skat.Bidding ( | module Skat.Bidding ( | ||||
| biddingScore, Game(..), Modifier(..) | biddingScore, Game(..), Modifier(..) | ||||
| ) where | ) where | ||||
| import Data.Aeson hiding (Null) | |||||
| import Skat.Card | import Skat.Card | ||||
| import Data.List (sortOn) | import Data.List (sortOn) | ||||
| import Data.Ord (Down(..)) | import Data.Ord (Down(..)) | ||||
| @@ -15,6 +19,22 @@ data Game = Colour Colour Modifier | |||||
| | NullOuvertHand | | NullOuvertHand | ||||
| deriving (Show, Eq) | deriving (Show, Eq) | ||||
| instance FromJSON Game where | |||||
| parseJSON = withObject "Game" $ \v -> do | |||||
| gamekind <- v .: "game" | |||||
| case (gamekind :: String) of | |||||
| "colour" -> do | |||||
| col <- v .: "colour" | |||||
| mod <- v .: "modifier" | |||||
| return $ Colour (read col) mod | |||||
| "grand" -> do | |||||
| mod <- v .: "modifier" | |||||
| return $ Grand mod | |||||
| "null" -> return Null | |||||
| "nullhand" -> return NullHand | |||||
| "nullouvert" -> return NullOuvert | |||||
| "nullouverthand" -> return NullOuvertHand | |||||
| -- | modifiers for grand and colour games | -- | modifiers for grand and colour games | ||||
| data Modifier = Einfach | data Modifier = Einfach | ||||
| | Schneider | | Schneider | ||||
| @@ -28,6 +48,19 @@ data Modifier = Einfach | |||||
| | Ouvert | | Ouvert | ||||
| deriving (Show, Eq) | deriving (Show, Eq) | ||||
| instance FromJSON Modifier where | |||||
| parseJSON = withObject "Modifier" $ \v -> do | |||||
| hnd <- v .: "hand" | |||||
| if read hnd then do | |||||
| schneider <- v .: "schneider" | |||||
| schwarz <- v .: "schwarz" | |||||
| ouvert <- v .: "ouvert" | |||||
| case (schneider, schwarz, ouvert) of | |||||
| (_, _, True) -> return Ouvert | |||||
| (True, False, _) -> return HandSchneiderAngesagt | |||||
| (_, True, _) -> return HandSchwarzAngesagt | |||||
| else return Einfach | |||||
| -- | 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 | ||||
| biddingScore game@(Grand mod) cards = (spitzen game cards + modifierFactor mod) * 24 | biddingScore game@(Grand mod) cards = (spitzen game cards + modifierFactor mod) * 24 | ||||
| @@ -1,8 +1,9 @@ | |||||
| module Skat.Matches ( | module Skat.Matches ( | ||||
| singleVsBots, pvp | |||||
| singleVsBots, pvp, pvpWithBidding | |||||
| ) where | ) where | ||||
| import Control.Monad.State | import Control.Monad.State | ||||
| import Control.Monad.Reader | |||||
| import System.Random (mkStdGen) | import System.Random (mkStdGen) | ||||
| import Skat | import Skat | ||||
| @@ -10,6 +11,7 @@ import Skat.Operations | |||||
| import Skat.Player | import Skat.Player | ||||
| import Skat.Pile | import Skat.Pile | ||||
| import Skat.Card | import Skat.Card | ||||
| import Skat.Preperation | |||||
| import Skat.AI.Rulebased | import Skat.AI.Rulebased | ||||
| import Skat.AI.Online | import Skat.AI.Online | ||||
| @@ -48,3 +50,21 @@ pvp comm1 comm2 comm3 = do | |||||
| (PL $ OnlineEnv Team Hand3 comm3) | (PL $ OnlineEnv Team Hand3 comm3) | ||||
| env = SkatEnv (distribute cards) Nothing Spades ps Hand1 | env = SkatEnv (distribute cards) Nothing Spades ps Hand1 | ||||
| liftIO $ evalStateT (publishGameStart Hand3 >> turn >>= publishGameResults) env | liftIO $ evalStateT (publishGameStart Hand3 >> turn >>= publishGameResults) env | ||||
| pvpWithBidding :: Communicator c => c -> c -> c -> IO () | |||||
| pvpWithBidding comm1 comm2 comm3 = do | |||||
| cards <- shuffleCards | |||||
| let ps = distribute cards | |||||
| h1 = map toCard $ handCards Hand1 ps | |||||
| h2 = map toCard $ handCards Hand2 ps | |||||
| h3 = map toCard $ handCards Hand3 ps | |||||
| bs = Bidders | |||||
| (BD $ PrepOnline Hand1 comm1 $ h1) | |||||
| (BD $ PrepOnline Hand2 comm2 $ h2) | |||||
| (BD $ PrepOnline Hand3 comm3 $ h3) | |||||
| env = PrepEnv ps bs | |||||
| maySkatEnv <- liftIO $ runReaderT runPreperation env | |||||
| case maySkatEnv of | |||||
| Just skatEnv -> | |||||
| liftIO $ evalStateT (publishGameStart Hand3 >> turn >>= publishGameResults) skatEnv | |||||
| Nothing -> putStrLn "No one wanted to play." | |||||
| @@ -1,11 +1,11 @@ | |||||
| {-# LANGUAGE ExistentialQuantification #-} | {-# LANGUAGE ExistentialQuantification #-} | ||||
| module Skat.Preperation ( | module Skat.Preperation ( | ||||
| Bidder(..), Bid, BD(..), Bidders(..), PrepEnv(..), runPreperation | |||||
| ) where | ) where | ||||
| import Control.Monad.IO.Class | import Control.Monad.IO.Class | ||||
| import Control.Monad.State | |||||
| import Control.Monad.Reader | |||||
| import Skat.Pile | import Skat.Pile | ||||
| import Skat.Card | import Skat.Card | ||||
| @@ -16,17 +16,16 @@ import Skat (SkatEnv, mkSkatEnv) | |||||
| type Bid = Int | type Bid = Int | ||||
| data PrepEnv = PrepEnv { piles :: Piles | data PrepEnv = PrepEnv { piles :: Piles | ||||
| , currentBid :: Bid | |||||
| , currentHand :: Hand | |||||
| , bidders :: Bidders } | , bidders :: Bidders } | ||||
| deriving Show | deriving Show | ||||
| type Preperation = StateT PrepEnv IO | |||||
| type Preperation = ReaderT PrepEnv IO | |||||
| class Bidder a where | class Bidder a where | ||||
| hand :: a -> Hand | hand :: a -> Hand | ||||
| onStart :: MonadIO m => a -> m () | |||||
| askBid :: MonadIO m => a -> Hand -> Bid -> m (Maybe Bid) | askBid :: MonadIO m => a -> Hand -> Bid -> m (Maybe Bid) | ||||
| askResponse :: MonadIO m => a -> Hand -> m Bool | |||||
| askResponse :: MonadIO m => a -> Hand -> Bid -> m Bool | |||||
| askGame :: MonadIO m => a -> Bid -> m Game | askGame :: MonadIO m => a -> Bid -> m Game | ||||
| askHand :: MonadIO m => a -> Bid -> m Bool | askHand :: MonadIO m => a -> Bid -> m Bool | ||||
| askSkat :: MonadIO m => a -> Bid -> [Card] -> m [Card] | askSkat :: MonadIO m => a -> Bid -> [Card] -> m [Card] | ||||
| @@ -46,6 +45,7 @@ instance Bidder BD where | |||||
| askSkat (BD b) = askSkat b | askSkat (BD b) = askSkat b | ||||
| askResponse (BD b) = askResponse b | askResponse (BD b) = askResponse b | ||||
| toPlayer (BD b) = toPlayer b | toPlayer (BD b) = toPlayer b | ||||
| onStart (BD b) = onStart b | |||||
| data Bidders = Bidders BD BD BD | data Bidders = Bidders BD BD BD | ||||
| deriving Show | deriving Show | ||||
| @@ -63,7 +63,10 @@ toPlayers single (Bidders b1 b2 b3) = | |||||
| runPreperation :: Preperation (Maybe SkatEnv) | runPreperation :: Preperation (Maybe SkatEnv) | ||||
| runPreperation = do | runPreperation = do | ||||
| bds <- gets bidders | |||||
| bds <- asks bidders | |||||
| onStart (bidder bds Hand1) | |||||
| onStart (bidder bds Hand2) | |||||
| onStart (bidder bds Hand3) | |||||
| (winner, bid) <- runBidding 0 (bidder bds Hand2) (bidder bds Hand1) | (winner, bid) <- runBidding 0 (bidder bds Hand2) (bidder bds Hand1) | ||||
| (finalWinner, finalBid) <- runBidding 0 (bidder bds Hand3) (bidder bds winner) | (finalWinner, finalBid) <- runBidding 0 (bidder bds Hand3) (bidder bds winner) | ||||
| if finalBid == 0 then do | if finalBid == 0 then do | ||||
| @@ -78,15 +81,15 @@ runBidding startingBid reizer gereizter = do | |||||
| first <- askBid reizer (hand gereizter) startingBid | first <- askBid reizer (hand gereizter) startingBid | ||||
| case first of | case first of | ||||
| Just val -> do | Just val -> do | ||||
| response <- askResponse gereizter (hand reizer) | |||||
| response <- askResponse gereizter (hand reizer) val | |||||
| if response then runBidding val reizer gereizter | if response then runBidding val reizer gereizter | ||||
| else return (hand reizer, val) | else return (hand reizer, val) | ||||
| Nothing -> return (hand gereizter, startingBid) | Nothing -> return (hand gereizter, startingBid) | ||||
| initGame :: Hand -> Bid -> Preperation SkatEnv | initGame :: Hand -> Bid -> Preperation SkatEnv | ||||
| initGame single bid = do | initGame single bid = do | ||||
| ps <- gets piles | |||||
| bds <- gets bidders | |||||
| ps <- asks piles | |||||
| bds <- asks 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 | ||||