Adapt the automaton to the latest protocol changes in APILanguage; clean unused code (imports, declarations, various warnings)

This commit is contained in:
Tissevert 2019-11-20 18:33:01 +01:00
parent 4529d19301
commit a9ba5cc47c
4 changed files with 54 additions and 61 deletions

View File

@ -6,14 +6,13 @@ module AI (
import Data.List (sortOn) import Data.List (sortOn)
import Data.Map ((!), delete, findMin) import Data.Map ((!), delete, findMin)
import Data.Ord (Down(..)) import Data.Ord (Down(..))
import Data.Set (Set, member) import Data.Set (member)
import qualified Data.Set as Set (fromList, intersection, unions) import qualified Data.Set as Set (fromList)
import Hanafuda ( import Hanafuda (
Card(..), Flower(..), Pack Card(..), Flower(..), Pack
, cardsOf, cardsOfPack, contains, empty,flower, packOfCards, sameMonth, size, union , cardsOf, cardsOfPack, contains, empty,flower, packOfCards, sameMonth, size, union
) )
import Hanafuda.Player (Player(..), Players(..)) import Hanafuda.KoiKoi (Move(..), PlayerID, Step(..))
import Hanafuda.KoiKoi (Move(..), PlayerID, Step(..), Score)
import Hanafuda.Message (PublicGame(..), PublicState(..), PublicPlayer) import Hanafuda.Message (PublicGame(..), PublicState(..), PublicPlayer)
move :: PlayerID -> PublicGame -> Move move :: PlayerID -> PublicGame -> Move
@ -32,9 +31,11 @@ move me (PublicGame {playerHand, public = PublicState {step = ToPlay, month, pla
move me (PublicGame {public = PublicState {step = Turned card, month, players, river}}) = move me (PublicGame {public = PublicState {step = Turned card, month, players, river}}) =
Choose . bestFor month (players ! me) . cardsOfPack $ sameMonth card river Choose . bestFor month (players ! me) . cardsOfPack $ sameMonth card river
move me (PublicGame {playerHand, public = PublicState {step = Scored}}) = move _ (PublicGame {playerHand, public = PublicState {step = Scored}}) =
KoiKoi $ playerHand /= empty KoiKoi $ playerHand /= empty
move _ _ = error "Nothing to play on ended game"
capture :: Card -> Card -> Pack -> Move capture :: Card -> Card -> Pack -> Move
capture card caught river = capture card caught river =
if size (sameMonth card river) == 1 if size (sameMonth card river) == 1

View File

@ -4,38 +4,23 @@ module Automaton (
start start
) where ) where
import AI (move) import qualified AI (move)
import Control.Concurrent (forkIO, threadDelay) import Control.Concurrent (forkIO, threadDelay)
import Data.Aeson (encode, eitherDecode') import Data.Aeson (encode, eitherDecode')
import Data.ByteString.Lazy.Char8 (ByteString, append, pack, putStrLn) import Data.ByteString.Lazy.Char8 (ByteString, append, pack, putStrLn)
import qualified Data.ByteString.Lazy.Char8 as ByteString (concat) import qualified Data.ByteString.Lazy.Char8 as ByteString (concat)
import Data.Map (Map, empty) import Data.Map ((!))
import Data.Text (Text) import qualified Data.Map as Map (delete, empty, member)
import Control.Concurrent (threadDelay)
import Control.Monad.Reader (ReaderT, ask) import Control.Monad.Reader (ReaderT, ask)
import Control.Monad.State (StateT)
import Control.Monad.Trans (lift) import Control.Monad.Trans (lift)
import Hanafuda.KoiKoi (PlayerID, Step(..)) import Hanafuda.KoiKoi (Step(..))
import qualified Hanafuda.Message as Message (T(..), FromClient(..), PublicGame(..), PublicState(..)) import Hanafuda.Message (T(..), FromClient(..))
import qualified Hanafuda.Message as Message (
T(..), FromClient, PublicGame(..), PublicState(..)
)
import Network.WebSockets (Connection, receiveData, sendTextData) import Network.WebSockets (Connection, receiveData, sendTextData)
import Prelude hiding (error, putStrLn) import Prelude hiding (error, putStrLn)
import System.Exit (exitSuccess) import Session (State(..), initial, store)
data State =
Initial
| Connected {
key :: PlayerID
}
| LoggedIn {
key :: PlayerID
, name :: Text
}
| Playing {
key :: PlayerID
, name :: Text
, against :: PlayerID
}
deriving Show
type App a = ReaderT Connection IO a type App a = ReaderT Connection IO a
@ -61,54 +46,58 @@ receive = do
debug :: ByteString -> App () debug :: ByteString -> App ()
debug message = lift $ putStrLn message debug message = lift $ putStrLn message
answer :: Message.T -> State -> App State answer :: State -> Message.T -> App State
answer welcome@(Message.Welcome {}) Initial = do
send $ Message.LogIn {Message.name = "Hannah"}
return $ Connected {key = Message.key welcome}
answer New (Welcome {key}) = do
lift $ Session.store key
lift $ putStrLn "Stored"
return $ Connected {playerID = key, games = Map.empty}
{-
answer (Message.Relay {Message.from, Message.message = Message.LogIn {Message.name}}) (Connected {key}) answer (Message.Relay {Message.from, Message.message = Message.LogIn {Message.name}}) (Connected {key})
| from == key = return $ LoggedIn {key, name} | from == key = return $ LoggedIn {key, name}
-}
answer (Message.Relay {Message.from, Message.message = Message.Invitation {}}) (LoggedIn {key, name}) = do answer state@(Connected {games}) (Relay {from, message = Invitation {}}) =
send $ Message.Answer {Message.accept = True, Message.to = from} -- policy : one game per player only
return $ Playing {key, name, against = from} send (Answer {accept = not $ Map.member from games, to = from})
>> return state
answer (Message.Relay {Message.from, Message.message = Message.Invitation {}}) state = do answer state@(Connected {playerID, games}) message@(Game {}) = do
send $ Message.Answer {Message.accept = False, Message.to = from}
return state
answer message@(Message.Game {Message.logs}) state@(Playing {key, name}) = do
case Message.step $ Message.public game of case Message.step $ Message.public game of
Over -> send Message.Quit >> return (LoggedIn {key, name}) Over ->
let opponentID = Message.nextPlayer (Message.public game) ! playerID in
return $ state {games = Map.delete opponentID games}
_ -> _ ->
if Message.playing (Message.public game) == key if Message.playing (Message.public game) == playerID
then then
send (Message.Play {Message.move = AI.move key game, Message.onGame = game}) send (Play {move = AI.move playerID game, onGame = game}) >> return state
>> return state
else return state else return state
where where
game = Message.state message game = Message.state message
answer (Message.Relay {Message.from, Message.message = Message.LogOut}) (Playing {key, name, against}) answer state Pong = ping >> return state
| from == against = send Message.Quit >> return (LoggedIn {key, name})
answer (Message.Relay {}) state = return state answer state (Error {error}) = do
answer Message.Pong state = ping >> return state
answer (Message.Error {Message.error}) state = do
debug $ "Received error from server : " `append` pack error debug $ "Received error from server : " `append` pack error
return state return state
answer message state = do {-
- Ignore
-}
answer state@(Connected {}) (Okaeri {}) = return state
answer state (Relay {}) = return state
answer state message = do
debug $ ByteString.concat [ debug $ ByteString.concat [
"Unexpected message : ", encode message, " in state ", pack $ show state "Unexpected message : ", encode message, " in state ", pack $ show state
] ]
return state return state
ping :: App ()
ping = do ping = do
connection <- ask connection <- ask
setTimeout (20*s) (sendIO Message.Ping connection) setTimeout (20*s) (sendIO Ping connection)
where where
setTimeout delay callback = setTimeout delay callback =
const () <$> (lift $ forkIO (threadDelay delay >> callback)) const () <$> (lift $ forkIO (threadDelay delay >> callback))
@ -116,8 +105,12 @@ ping = do
s = 1000 * ms s = 1000 * ms
start :: App () start :: App ()
start = ping >> loop Initial start = do
ping
initialState <- lift Session.initial
case initialState of
New -> send $ Hello {name = "Hannah"}
Connected {playerID} -> send $ Tadaima {myID = playerID, name = "Hannah"}
loop initialState
where where
loop state = do loop state = receive >>= answer state >>= loop
newMessage <- receive
answer newMessage state >>= loop

View File

@ -6,10 +6,10 @@ module Config (
) where ) where
host :: String host :: String
host = "koikoi.menf.in" host = "koikoi.local"
libDir :: FilePath libDir :: FilePath
libDir = "/var/lib/hannah" libDir = "/home/alice/Documents/Atelier/hanafuda/hannah/lib"
path :: String path :: String
path = "/play/" path = "/play/"

View File

@ -2,7 +2,6 @@ module Main where
import Network.WebSockets (ClientApp, runClient) import Network.WebSockets (ClientApp, runClient)
import Control.Monad.Reader (runReaderT) import Control.Monad.Reader (runReaderT)
import Control.Monad.State (runStateT)
import Config (host, port, path) import Config (host, port, path)
import Automaton (start) import Automaton (start)