{- | Library for creating simple deterministic games, such as tic-tac-toe. The engine requires a minimal set of actions related to the game, and then will run the game until a terminal state is reached. Simple generic example below. See the specs for a more detailed example. > import Game.Deterministic.GameEngine > > game :: Monad m => GameEngine m [Char] [(Int, Char)] > game = GameEngine gameActions initialState > > gameActions :: Monad m => GameActions m [Char] [(Int, Char)] > gameActions = GameActions { > getPlayer = -- find the next player from a game state, > getMove = -- find a move from the game state, > getResult = -- transitions from a state to another state, > isTerminal = -- determines if the game is terminal, > getScore = -- get score from a terminal state > } > > initialState :: GameState [Char] > initialState = GameState ['x', 'x', 'x'] > > -- run the game engine until a terminal state is reached > playSimple game -} module Game.Deterministic.GameEngine ( GameActions(..), GameState(..), Move(..), Player(..), GameEngine(..), play, playSimple, playIO ) where import Control.Monad.Identity import Game.Deterministic.GameEngine.GameActions import Game.Deterministic.GameEngine.GameState import Game.Deterministic.GameEngine.Move import Game.Deterministic.GameEngine.Player data GameEngine m a b = GameEngine { gameEngineActions :: GameActions m a b, -- ^ Defines how the game will be played gameEngineState :: GameState a -- ^ The current state of the game } -- ^ Holds information about how the game is played, and the current state of the game. play :: Monad m => GameEngine m a b -> m Int -- ^ Run the provided game engine under a monadic context until a terminal state is reached. play (GameEngine actions state) = do isTerm <- isTerminal actions state if isTerm then do player <- getPlayer actions state getScore actions state player else do nextState <- getNextState actions state play $ GameEngine actions nextState playSimple :: GameEngine Identity a b -> Int -- ^ Run the provided game engine without a context until a terminal state is reached. playSimple = runIdentity . play playIO :: GameEngine IO a b -> IO Int -- ^ Run the provided game engine within an IO context until a terminal state is reached. playIO = play getNextState :: Monad m => GameActions m a b -> GameState a -> m (GameState a) getNextState actions state = do nextMove <- getMove actions state getResult actions state nextMove