hannah/src/Automaton.hs

118 lines
3.4 KiB
Haskell
Raw Normal View History

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NamedFieldPuns #-}
module Automaton (
2019-08-19 18:48:11 +02:00
start
) where
import AI (move)
2019-08-19 18:48:11 +02:00
import Control.Concurrent (forkIO, threadDelay)
import Data.Aeson (encode, eitherDecode')
import Data.ByteString.Lazy.Char8 (ByteString, append, pack, putStrLn)
import qualified Data.ByteString.Lazy.Char8 as ByteString (concat)
import Data.Map (Map, empty)
2019-08-19 18:48:11 +02:00
import Data.Text (Text)
import Control.Concurrent (threadDelay)
import Control.Monad.Reader (ReaderT, ask)
import Control.Monad.State (StateT)
import Control.Monad.Trans (lift)
2019-08-19 18:48:11 +02:00
import Hanafuda.KoiKoi (Game, GameBlueprint(..), PlayerKey)
import qualified Hanafuda.Message as Message (T(..), FromClient(..), PublicGame, Room)
import Network.WebSockets (Connection, receiveData, sendTextData)
import Prelude hiding (error, putStrLn)
data State =
Initial
| Connected {
key :: PlayerKey
, room :: Message.Room
}
| LoggedIn {
key :: PlayerKey
, room :: Message.Room
2019-08-19 18:48:11 +02:00
, name :: Text
}
| Playing {
key :: PlayerKey
, room :: Message.Room
2019-08-19 18:48:11 +02:00
, name :: Text
, game :: Message.PublicGame
}
2019-08-19 18:48:11 +02:00
deriving Show
type App a = ReaderT Connection IO a
2019-08-19 18:48:11 +02:00
sendIO :: Message.FromClient -> Connection -> IO ()
sendIO message connection = do
putStrLn $ ">" `append` encoded
sendTextData connection encoded
where
encoded = encode message
send :: Message.FromClient -> App ()
2019-08-19 18:48:11 +02:00
send message =
ask >>= lift . sendIO message
receive :: App Message.T
2019-08-19 18:48:11 +02:00
receive = do
received <- ask >>= lift . receiveData
debug $ "<" `append` received
case eitherDecode' received of
Left errorMessage -> debug (pack errorMessage) >> receive
Right message -> return message
debug :: ByteString -> App ()
debug message = lift $ putStrLn message
answer :: Message.T -> State -> App State
answer welcome@(Message.Welcome {}) Initial = do
2019-08-19 18:48:11 +02:00
send $ Message.LogIn {Message.name = "Hannah"}
return $ Connected {
key = Message.key welcome
, room = Message.room welcome
}
answer (Message.Relay {Message.from, Message.message = Message.LogIn {Message.name}}) (Connected {key, room})
2019-08-19 18:48:11 +02:00
| from == key = return $ LoggedIn {key, room, name}
answer (Message.Relay {Message.message = Message.Invitation {}}) state@(LoggedIn {key, room, name}) = do
send $ Message.Answer {Message.accept = True}
return $ Playing {key, room, name, game = undefined}
2019-08-19 18:48:11 +02:00
answer (Message.Relay {Message.message = Message.Invitation {}}) state = do
send $ Message.Answer {Message.accept = False}
return state
2019-08-19 18:48:11 +02:00
answer (Message.Game {Message.game, Message.logs}) state@(Playing {key}) = do
if playing game == key
then send $ Message.Play {Message.move = AI.move key game}
else return ()
return $ state {game}
2019-08-19 18:48:11 +02:00
answer Message.Pong state = ping >> return state
2019-08-19 18:48:11 +02:00
answer (Message.Error {Message.error}) state = do
debug $ "Received error from server : " `append` pack error
return state
answer message state = do
2019-08-19 18:48:11 +02:00
debug $ ByteString.concat [
"Unexpected message : ", encode message, " in state ", pack $ show state
]
return state
2019-08-19 18:48:11 +02:00
ping = do
connection <- ask
setTimeout (2*s) (sendIO Message.Ping connection)
where
setTimeout delay callback =
const () <$> (lift $ forkIO (threadDelay delay >> callback))
ms = 1000
s = 1000 * ms
start :: App ()
2019-08-19 18:48:11 +02:00
start = ping >> loop Initial
where
loop state = do
newMessage <- receive
answer newMessage state >>= loop