1 {-# LANGUAGE FlexibleContexts #-}
6 import Control.Monad.State
7 import Control.Monad.Reader
8 import Control.Monad.Writer
16 data Hint = PartyAt Place
21 data PlayerInformation = PlayerInformation
23 , piHints :: [(Day,Hint)]
26 data Score = NoScore | ToFew | Scored
28 data IncomingMessage = InternalMessage InternalMessage
29 | DayMessage DayMessage
30 | NightMessage NightMessage
32 -- Events that we want to send to the user code and that
33 -- can be reacted upon
34 data DayMessage = DayStarts
35 -- Events that we want to send to the user code and that
36 -- can NOT be reacted upon
37 data NightMessage = Initialize
39 -- Pure bookkeeping events
40 data InternalMessage = MyPlayerId Player
45 | PlayerEnter Player Place
49 | PlayerJoined Player -- the game
51 | PlayerEnters Player Place
52 | PlayerLeaves Player Place
55 | PlayerScore [(Player, Int)]
56 | PlayerNames [(Player, String)]
66 | PlayerPosition Player Place -- wann bekommt man das?
72 data ClientMessage = Tell Player Hint
75 type DayCallback d = DayMessage -> DayMonad d ()
76 type NightCallback d = NightMessage -> NightMonad d ()
78 data ParttyState = ParttyState
81 , psPlayerInfo :: Map Player PlayerInformation
82 , psPartyHistory:: [(Day,Place)]
84 , psPlayers :: [Player]
87 type DayMonad d = WriterT [ClientMessage] (ReaderT ParttyState (StateT d IO))
88 type NightMonad d = ReaderT ParttyState (StateT d IO)
93 -- Hostname, Port, Playername, Password, Callback
94 runPartty :: String -> Int -> String -> String -> DayCallback d -> NightCallback d -> d -> IO ()
95 runPartty host port username password dcb ncb initialData = do
96 h <- connectTo host (PortNumber (fromIntegral port))
97 InternalMessage Welcome <- parseIncomingMessage `liftM` hGetLine h
98 hPutStrLn h $ unwords ["login", username, password]
99 InternalMessage (PlayerJoined myName) <- parseIncomingMessage `liftM` hGetLine h
100 when (myName /= username) $ error "Server returned a different name for me"
102 -- login has been successful, wait for the server start event
104 msg <- parseIncomingMessage `liftM` hGetLine h
106 (InternalMessage GameStarting) -> return ()
108 putStrLn "Waiting for game start..."
111 -- We expect two more fixed events. If the server changes, this breaks!
113 InternalMessage (MyIdIs myId) <- parseIncomingMessage `liftM` hGetLine h
114 InternalMessage (PlayerEnter player startPlace) <- parseIncomingMessage `liftM` hGetLine h
115 when (player /= myId) $ error "Server did not tell us where we start"
117 let initialState = ParttyState
120 , psPlayerInfo = empty
122 , psPlace = startPlace
126 -- The rest of the events are handled in the regular loop. Run the initial event
127 -- and call the runner
129 d' <- undefined {- run -} ncb initialState initialData Initialize
131 runner h dcb ncb initialState d'
134 runner :: Handle -> DayCallback d -> NightCallback d -> ParttyState -> d -> IO ()
135 runner h dcb ncb = fix $ \loop ps d -> do
136 (e,ps') <- getNextUserMessage h ps
138 Left dm -> do (client_messages, d') <- undefined {- run -} dcb dm
139 undefined -- send_client_messages client_messages
141 Right nm -> undefined {- run -} ncb nm
144 getNextUserMessage :: Handle -> ParttyState -> IO (Either DayMessage NightMessage, ParttyState)
145 getNextUserMessage h ps = do
147 e <- undefined {- parseIncomingMessage -} line
148 let ps' = undefined {- updateState -} e ps
150 InternalMessage ue -> getNextUserMessage h ps'
151 DayMessage dm -> return (Left dm, ps')
152 NightMessage nm -> return (Right nm, ps')
154 parseIncomingMessage :: String -> IncomingMessage
155 parseIncomingMessage = undefined
157 -- Convenience functions for accessing the state
159 whoIsHere :: (MonadReader ParttyState m) => m [Player]
160 whoIsHere = asks psPlayers
162 gameDay :: (MonadReader ParttyState m) => m Day
165 -- Interaction: use “tell”