t3-server-0.1.0: src/T3/Match.hs
module T3.Match
( module T3.Match.Types
, Seconds(..)
, runMatch
, UserInit
, Callback
, StartCallback
, delay
) where
import Prelude
import T3.Match.Types
import T3.Game
import Control.Concurrent
import Control.Concurrent.Async
import Control.Monad.Trans.Either
import Control.Monad.State.Strict
type Callback = Step -> IO ()
type StartCallback = MatchInfo -> Users -> Step -> IO ()
newtype Seconds = Seconds Int
deriving (Num, Show, Eq, Ord, Enum)
data MatchData = MatchData
{ matchReq :: XO -> IO (Loc, Callback)
, matchRespX :: Callback
, matchRespO :: Callback
, matchLog :: [Action] -> Board -> Result -> IO ()
, matchBoard :: Board
, matchActions :: [Action]
, matchTimeoutLimit :: Maybe Seconds
}
newtype Match a = Match { unMatch :: StateT MatchData (EitherT (IO ()) IO) a }
deriving (Functor, Applicative, Monad, MonadIO, MonadState MatchData)
type UserInit = (Callback, IO (Loc, Callback))
runMatch :: Maybe Seconds -> UserInit -> UserInit -> ([Action] -> Board -> Result -> IO ()) -> IO () -> IO ()
runMatch timeoutLimit (xCB, xReq) (oCB, oReq) logger done = do
let req X = xReq
req O = oReq
let cb X = xCB
cb O = oCB
let b = emptyBoard
let matchDat = MatchData req (cb X) (cb O) logger b [] timeoutLimit
matchResult <- runEitherT (evalStateT (unMatch $ run b) matchDat)
either id (const $ return ()) matchResult
done
sendGameState :: XO -> Match ()
sendGameState xo = do
s <- get
liftIO $ (respXO xo s) (Step (matchBoard s) Nothing)
delay :: Seconds -> IO ()
delay (Seconds n) = threadDelay (n * 1000000)
recvAction :: XO -> Match Loc
recvAction xo = do
req <- gets (flip matchReq xo)
s <- get
let timeoutResponse = forfeitIO s (Win $ yinYang xo) (Lose xo)
timeoutOrLoc <- liftIO $
maybe
(fmap Right req)
(\secs -> race (delay secs >> return timeoutResponse) req)
(matchTimeoutLimit s)
case timeoutOrLoc of
Left timeout -> Match (lift $ left timeout)
Right (loc, resp) -> do
updateResp resp
return loc
where
updateResp resp = do
match <- get
put $ case xo of
X -> match { matchRespX = resp }
O -> match { matchRespO = resp }
sendFinal :: XO -> Final -> Match ()
sendFinal xo final = do
s <- get
liftIO $ sendFinalIO s xo final
sendFinalIO :: MatchData -> XO -> Final -> IO ()
sendFinalIO s xo final = liftIO $ (respXO xo s) (Step (matchBoard s) (Just final))
tally :: Result -> Match ()
tally res = do
s <- get
liftIO $ matchLog s (matchActions s) (matchBoard s) res
tallyIO :: MatchData -> Result -> IO ()
tallyIO s res = matchLog s (matchActions s) (matchBoard s) res
forfeitIO :: MatchData -> Win XO -> Lose XO -> IO ()
forfeitIO s (Win w) (Lose l) = do
tallyIO s (Winner w)
sendFinalIO s w WonByDQ
sendFinalIO s l LossByDQ
updateBoard :: Board -> Match ()
updateBoard b = do
match <- get
put $ match { matchBoard = b }
logAction :: XO -> Loc -> Match ()
logAction xo loc = do
match <- get
put $ match { matchActions = matchActions match ++ [Action xo loc] }
respXO :: XO -> MatchData -> Callback
respXO X = matchRespX
respXO O = matchRespO
instance Game Match where
move xo = do
sendGameState xo
recvAction xo
forfeit w l = do
s <- get
liftIO $ forfeitIO s w l
end (Win w) (Lose l) = do
tally (Winner w)
sendFinal w Won
sendFinal l Loss
tie = do
tally Tie
sendFinal X Tied
sendFinal O Tied
step b xo loc = do
logAction xo loc
updateBoard b