motor-0.3.0: test/Motor/FSMSpec/Game.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module Motor.FSMSpec.Game where
import Prelude hiding (log, (>>))
import Control.Monad.Indexed
import Control.Monad.IO.Class
import Data.Row.Records
import Motor.FSM
import Motor.FSM.Logging
-- * Game Protocol/Machine
data Standing
data Jumping
class MonadFSM m => Game (m :: Row * -> Row * -> * -> *) where
type State m :: * -> *
spawn
:: KnownSymbol n
=> Name n
-> Actions m '[n !+ State m Standing] r ()
jump
:: KnownSymbol n
=> Name n
-> Actions m '[n := State m Standing !--> State m Jumping] r ()
land
:: KnownSymbol n
=> Name n
-> Actions m '[n := State m Jumping !--> State m Standing] r ()
perish
:: KnownSymbol n
=> Name n
-> Actions m '[n !- State m Standing] r ()
-- * Game Implemention
newtype GameImpl m (i :: Row *) (o :: Row *) a =
GameImpl { runGameImpl :: FSM m i o a }
deriving (IxFunctor, IxPointed, IxApplicative, IxMonad, MonadFSM)
deriving instance Monad m => Functor (GameImpl m i i)
deriving instance Monad m => Applicative (GameImpl m i i)
deriving instance Monad m => Monad (GameImpl m i i)
instance (MonadIO m) => MonadIO (GameImpl m i i) where
liftIO = GameImpl . liftIO
data GameState s where
Standing :: GameState Standing
Jumping :: GameState Jumping
instance (MonadIO m, Monad m) => Game (GameImpl m) where
type State (GameImpl m) = GameState
spawn n =
log n "Spawning player."
>>>= const (new n Standing)
jump n =
log n "Huuuhhh!"
>>>= const (enter n Jumping)
land n =
log n "Back on safe ground."
>>>= const (enter n Standing)
perish n =
log n "Aaaaarhhh..."
>>>= const (delete n)
hero1 :: Name "hero1"
hero1 = Name
hero2 :: Name "hero2"
hero2 = Name
testTwoAdds ::
Game m
=> Actions m '[ "hero1" !+ State m Standing
, "hero2" !+ State m Standing
] r ()
testTwoAdds =
spawn hero1 >>>= \_ -> spawn hero2
testTwoDeletes ::
Game m
=> Actions m '[ "hero1" !- State m Standing
, "hero2" !- State m Standing
] r ()
testTwoDeletes =
perish hero1 >>>= \_ -> perish hero2
testTwoAddDeletes ::
Game m
=> Actions m '[ "hero1" !+ State m Standing
, "hero2" !+ State m Standing
, "hero1" !- State m Standing
, "hero2" !- State m Standing
] r ()
testTwoAddDeletes = do
spawn hero1
spawn hero2
perish hero1
perish hero2
where
(>>) a = (>>>=) a . const
testGame :: Game m => OnlyActions m '[] ()
testGame = testTwoAdds >> testTwoDeletes >> testTwoAddDeletes
where
(>>) a = (>>>=) a . const
runIO :: IO ()
runIO = runFSM (runGameImpl testGame)