{-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} module Skat.AI.Online where import Control.Monad.Reader import Control.Concurrent.Chan import Data.Aeson import Data.Maybe import qualified Data.ByteString.Lazy.Char8 as BS import Skat.Player import qualified Skat.Player.Utils as P import Skat.Pile import Skat.Card import Skat.Render import Skat.Preperation import Skat.Bidding class Communicator a where send :: a -> String -> IO () receive :: a -> IO String instance Communicator (Chan String) where send = writeChan receive = readChan class Monad m => MonadClient m where query :: String -> m () response :: m String data OnlineEnv c = OnlineEnv { getTeam :: Team , getHand :: Hand , connection :: c } data PrepOnline c = PrepOnline { prepHand :: Hand , prepConnection :: c , prepCards :: [Card] } instance Show (OnlineEnv c) where show _ = "An online env" instance Show (PrepOnline c) where show _ = "An online prep env" instance Communicator c => Player (OnlineEnv c) where team = getTeam hand = getHand chooseCard p table _ hand = runReaderT (choose table hand) p >>= \c -> return (c, p) onCardPlayed p c = runReaderT (cardPlayed c) p >> return p onGameResults p res = runReaderT (onResults res) 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 instance (Communicator c, MonadIO m) => MonadClient (Online c m) where query s = do conn <- asks connection liftIO $ send conn s response = do conn <- asks connection liftIO $ receive conn instance MonadPlayer m => MonadPlayer (Online a m) where trumpColour = lift $ trumpColour turnColour = lift $ turnColour showSkat = lift . showSkat choose :: HasCard a => (Communicator c, MonadPlayer m) => [CardS Played] -> [a] -> Online c m Card choose table hand' = do let hand = map toCard hand' query (BS.unpack $ encode $ ChooseQuery hand table) r <- response case decode (BS.pack r) of Just (ChosenResponse card) -> do allowed <- P.isAllowed hand card if card `elem` hand && allowed then return card else choose table hand' Nothing -> choose table hand' cardPlayed :: (Communicator c, MonadPlayer m) => CardS Played -> Online c m () cardPlayed card = query (BS.unpack $ encode $ CardPlayedQuery card) onResults :: (Communicator c, MonadIO m) => (Int, Int) -> Online c m () onResults (sgl, tm) = query (BS.unpack $ encode $ GameResultsQuery sgl tm) onStartOnline :: (Communicator c, MonadPlayer m) => Hand -> Online c m () onStartOnline singlePlayer = do trCol <- trumpColour ownHand <- asks getHand query (BS.unpack $ encode $ GameStartQuery trCol ownHand singlePlayer) -- | QUERIES AND RESPONSES data Query = ChooseQuery [Card] [CardS Played] | CardPlayedQuery (CardS Played) | GameResultsQuery Int Int | GameStartQuery Colour Hand Hand | 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 toJSON (ChooseQuery hand table) = object ["query" .= ("choose_card" :: String), "hand" .= hand, "table" .= table] toJSON (CardPlayedQuery card) = object ["query" .= ("card_played" :: String), "card" .= card] toJSON (GameResultsQuery sgl tm) = object ["query" .= ("results" :: String), "single" .= sgl, "team" .= tm] toJSON (GameStartQuery trumps handNo sglPlayer) = object ["query" .= ("start_game" :: String), "trumps" .= show trumps, "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 <$> 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"