t3-server-0.2.0: src/T3/Match.hs
module T3.Match
( module T3.Match.Types
, Seconds(..)
, runMatch
, UserInit
, Callback
, StartCallback
, delay
) where
import Control.Monad.Trans.Class
import Control.Monad.Trans.Either
import Control.Monad.State.Strict
import Control.Monad.Conc.ClassTmp
import T3.Game
import T3.Match.Types
type Callback m = Step -> m ()
type StartCallback m = MatchInfo -> Users -> Step -> m ()
newtype Seconds = Seconds Int
deriving (Num, Show, Eq, Ord, Enum)
data MatchData m = MatchData
{ _matchReq :: XO -> m (Loc, Callback m)
, _matchRespX :: Callback m
, _matchRespO :: Callback m
, _matchLog :: [Action] -> Board -> Result -> m ()
, _matchBoard :: Board
, _matchActions :: [Action]
, _matchTimeoutLimit :: Maybe Seconds
}
newtype MatchT m a = MatchT { unMatchT :: StateT (MatchData m) (EitherT (m ()) m) a }
deriving (Functor, Applicative, Monad, MonadState (MatchData m))
instance MonadTrans MatchT where
lift ma = MatchT . StateT $ \md -> do
a <- EitherT (fmap return ma)
return (a, md)
type UserInit m = (Callback m, m (Loc, Callback m))
runMatch :: MonadConc m => Maybe Seconds -> UserInit m -> UserInit m -> ([Action] -> Board -> Result -> m ()) -> m () -> m ()
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 (unMatchT $ run b) matchDat)
either id (const $ return ()) matchResult
done
sendGameState :: MonadConc m => XO -> MatchT m ()
sendGameState xo = do
md <- get
lift $ (respXO xo md) (Step (_matchBoard md) Nothing)
delay :: MonadConc m => Seconds -> m ()
delay (Seconds n) = threadDelay (n * 1000000)
recvAction :: MonadConc m => XO -> MatchT m Loc
recvAction xo = do
md <- get
let req = _matchReq md xo
let timeoutResponse = forfeitIO md (Win $ yinYang xo) (Lose xo)
timeoutOrLoc <- lift $ do
maybe
(fmap Right req)
(\secs -> race (delay secs >> return timeoutResponse) req)
(_matchTimeoutLimit md)
case timeoutOrLoc of
Left timeout -> MatchT $ lift (left timeout)
Right (loc, resp) -> do
updateResp resp
return loc
where
updateResp resp = do
md <- get
put $ case xo of
X -> md{ _matchRespX = resp }
O -> md{ _matchRespO = resp }
sendFinal :: MonadConc m => XO -> Final -> MatchT m ()
sendFinal xo f = do
md <- get
lift $ sendFinalIO md xo f
sendFinalIO :: MonadConc m => MatchData m -> XO -> Final -> m ()
sendFinalIO md xo f = (respXO xo md) (Step (_matchBoard md) (Just f))
tally :: MonadConc m => Result -> MatchT m ()
tally res = do
md <- get
lift $ tallyIO md res
tallyIO :: MonadConc m => MatchData m -> Result -> m ()
tallyIO md res = _matchLog md (_matchActions md) (_matchBoard md) res
forfeitIO :: MonadConc m => MatchData m -> Win XO -> Lose XO -> m ()
forfeitIO s (Win w) (Lose l) = do
tallyIO s (Winner w)
sendFinalIO s w WonByDQ
sendFinalIO s l LossByDQ
updateBoard :: MonadConc m => Board -> MatchT m ()
updateBoard b = do
md <- get
put $ md{ _matchBoard = b }
logAction :: MonadConc m => XO -> Loc -> MatchT m ()
logAction xo loc = do
md <- get
put md{ _matchActions = _matchActions md ++ [Action xo loc] }
respXO :: MonadConc m => XO -> MatchData m -> Callback m
respXO X = _matchRespX
respXO O = _matchRespO
instance MonadConc m => Game (MatchT m) where
move xo = do
sendGameState xo
recvAction xo
forfeit w l = do
md <- get
lift $ forfeitIO md 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