Implement basic Automaton
This commit is contained in:
parent
84251b648b
commit
00d88dcc83
4 changed files with 61 additions and 31 deletions
|
@ -29,6 +29,7 @@ executable hannah
|
||||||
, hanafuda
|
, hanafuda
|
||||||
, hanafuda-APILanguage
|
, hanafuda-APILanguage
|
||||||
, mtl
|
, mtl
|
||||||
|
, text
|
||||||
, websockets
|
, websockets
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
|
@ -2,7 +2,8 @@ module AI (
|
||||||
move
|
move
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Hanafuda.KoiKoi (Game(..), Move(..), PlayerKey)
|
import Hanafuda.KoiKoi (Move(..), PlayerKey)
|
||||||
|
import Hanafuda.Message (PublicGame)
|
||||||
|
|
||||||
move :: PlayerKey -> Game -> Move
|
move :: PlayerKey -> PublicGame -> Move
|
||||||
move = undefined
|
move = undefined
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE NamedFieldPuns #-}
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
module Automaton (
|
module Automaton (
|
||||||
initialState
|
start
|
||||||
, start
|
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import AI (move)
|
import AI (move)
|
||||||
import Network.WebSockets (Connection, receiveData, sendTextData)
|
import Control.Concurrent (forkIO, threadDelay)
|
||||||
--import Data.Aeson
|
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)
|
import Data.Map (Map, empty)
|
||||||
import Data.ByteString.Lazy.Char8 (ByteString, pack, putStrLn)
|
import Data.Text (Text)
|
||||||
import Control.Concurrent (threadDelay)
|
import Control.Concurrent (threadDelay)
|
||||||
import Control.Monad.Reader (ReaderT, ask)
|
import Control.Monad.Reader (ReaderT, ask)
|
||||||
import Control.Monad.State (StateT)
|
import Control.Monad.State (StateT)
|
||||||
import Control.Monad.Trans (lift)
|
import Control.Monad.Trans (lift)
|
||||||
import Hanafuda.KoiKoi (Game(..), PlayerKey)
|
import Hanafuda.KoiKoi (Game, GameBlueprint(..), PlayerKey)
|
||||||
import qualified Hanafuda.Message as Message (T(..), FromClient(..), Room)
|
import qualified Hanafuda.Message as Message (T(..), FromClient(..), PublicGame, Room)
|
||||||
|
import Network.WebSockets (Connection, receiveData, sendTextData)
|
||||||
import Prelude hiding (error, putStrLn)
|
import Prelude hiding (error, putStrLn)
|
||||||
|
|
||||||
data State =
|
data State =
|
||||||
|
@ -27,61 +29,88 @@ data State =
|
||||||
| LoggedIn {
|
| LoggedIn {
|
||||||
key :: PlayerKey
|
key :: PlayerKey
|
||||||
, room :: Message.Room
|
, room :: Message.Room
|
||||||
, name :: String
|
, name :: Text
|
||||||
}
|
}
|
||||||
| Playing {
|
| Playing {
|
||||||
key :: PlayerKey
|
key :: PlayerKey
|
||||||
, room :: Message.Room
|
, room :: Message.Room
|
||||||
, name :: String
|
, name :: Text
|
||||||
, game :: Game
|
, game :: Message.PublicGame
|
||||||
}
|
}
|
||||||
|
deriving Show
|
||||||
|
|
||||||
type App a = ReaderT Connection IO a
|
type App a = ReaderT Connection IO a
|
||||||
|
|
||||||
|
sendIO :: Message.FromClient -> Connection -> IO ()
|
||||||
|
sendIO message connection = do
|
||||||
|
putStrLn $ ">" `append` encoded
|
||||||
|
sendTextData connection encoded
|
||||||
|
where
|
||||||
|
encoded = encode message
|
||||||
|
|
||||||
send :: Message.FromClient -> App ()
|
send :: Message.FromClient -> App ()
|
||||||
send = undefined
|
send message =
|
||||||
|
ask >>= lift . sendIO message
|
||||||
|
|
||||||
receive :: App Message.T
|
receive :: App Message.T
|
||||||
receive = undefined
|
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 :: ByteString -> App ()
|
||||||
debug message = lift $ putStrLn message
|
debug message = lift $ putStrLn message
|
||||||
|
|
||||||
answer :: Message.T -> State -> App State
|
answer :: Message.T -> State -> App State
|
||||||
answer welcome@(Message.Welcome {}) Initial = do
|
answer welcome@(Message.Welcome {}) Initial = do
|
||||||
send $ Message.LogIn {name = "Hannah"}
|
send $ Message.LogIn {Message.name = "Hannah"}
|
||||||
return $ Connected {
|
return $ Connected {
|
||||||
key = Message.key welcome
|
key = Message.key welcome
|
||||||
, room = Message.room welcome
|
, room = Message.room welcome
|
||||||
}
|
}
|
||||||
|
|
||||||
answer (Message.Relay {Message.from, Message.message = Message.LogIn {Message.name}}) (Connected {key, room})
|
answer (Message.Relay {Message.from, Message.message = Message.LogIn {Message.name}}) (Connected {key, room})
|
||||||
| from == key = return $ LoggedIn {key, room, name = Message.name}
|
| from == key = return $ LoggedIn {key, room, name}
|
||||||
|
|
||||||
answer (Message.Relay {Message.message = Message.Invitation {}}) state@(LoggedIn {key, room, name}) = do
|
answer (Message.Relay {Message.message = Message.Invitation {}}) state@(LoggedIn {key, room, name}) = do
|
||||||
send $ Message.Answer {Message.accept = True}
|
send $ Message.Answer {Message.accept = True}
|
||||||
return $ Playing {key, room, name, game = undefined}
|
return $ Playing {key, room, name, game = undefined}
|
||||||
|
|
||||||
answer (Message.Relay {Message.message = Message.Invitation {}}) _ =
|
answer (Message.Relay {Message.message = Message.Invitation {}}) state = do
|
||||||
send $ Message.Answer {Message.accept = False} >> return state
|
send $ Message.Answer {Message.accept = False}
|
||||||
|
return state
|
||||||
|
|
||||||
answer game@(Message.Game {Message.game, Message.logs}) state@(Playing {key}) =
|
answer (Message.Game {Message.game, Message.logs}) state@(Playing {key}) = do
|
||||||
if playing == key
|
if playing game == key
|
||||||
then send $ AI.move key game
|
then send $ Message.Play {Message.move = AI.move key game}
|
||||||
else return ()
|
else return ()
|
||||||
return $ state {game}
|
return $ state {game}
|
||||||
|
|
||||||
--answer Pong =
|
answer Message.Pong state = ping >> return state
|
||||||
|
|
||||||
answer (Message.Error {Message.error}) state =
|
answer (Message.Error {Message.error}) state = do
|
||||||
debug $ "Received error from server : " <> pack error >> return state
|
debug $ "Received error from server : " `append` pack error
|
||||||
|
|
||||||
answer message state = do
|
|
||||||
debug $ "Unexpected message : " <> encode message <> " in state " <> show state
|
|
||||||
return state
|
return state
|
||||||
|
|
||||||
|
answer message state = do
|
||||||
|
debug $ ByteString.concat [
|
||||||
|
"Unexpected message : ", encode message, " in state ", pack $ show state
|
||||||
|
]
|
||||||
|
return state
|
||||||
|
|
||||||
|
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 ()
|
start :: App ()
|
||||||
start = loop Initial
|
start = ping >> loop Initial
|
||||||
where
|
where
|
||||||
loop state = do
|
loop state = do
|
||||||
newMessage <- receive
|
newMessage <- receive
|
||||||
|
|
|
@ -4,11 +4,10 @@ import Network.WebSockets (ClientApp, runClient)
|
||||||
import Control.Monad.Reader (runReaderT)
|
import Control.Monad.Reader (runReaderT)
|
||||||
import Control.Monad.State (runStateT)
|
import Control.Monad.State (runStateT)
|
||||||
import Config (host, port, path)
|
import Config (host, port, path)
|
||||||
import Automaton (initialState, start)
|
import Automaton (start)
|
||||||
|
|
||||||
bot :: ClientApp ()
|
bot :: ClientApp ()
|
||||||
bot connection =
|
bot connection = runReaderT Automaton.start connection
|
||||||
fst <$> runReaderT Automaton.start connection
|
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main =
|
main =
|
||||||
|
|
Loading…
Reference in a new issue