{- |
Haskell 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 GameEngine
>
> game :: GameEngine Int Int
> game = GameEngine gameActions initialState
>
> gameActions :: GameActions Int Int
> 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 Int
> initialState = GameState 0
>
> -- run the game engine until a terminal state is reached
> playSimple game
-}
{-# LANGUAGE RankNTypes #-}
module Game.Deterministic.GameEngine (
Symbol,
GameState(..),
Player(..),
Move(..),
GameActions(..),
GameEngine(..),
GameEngineSimple,
GameEngineIO,
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 {
actions :: GameActions m a b,
-- ^ Defines how the game will be played
state :: GameState a
-- ^ The current state of the game
}
-- ^ Holds information about how the game is played, and the current state of the game.
type GameEngineSimple a b = GameEngine Identity a b
type GameEngineIO a b = GameEngine IO a b
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 engine = do
isTerm <- isTerminal gameActions gameState
if isTerm then do
player <- getPlayer gameActions gameState
getScore gameActions gameState player
else do
nextState <- getNextState engine
play $ GameEngine (actions engine) nextState
where
gameActions = actions engine
gameState = state engine
playSimple :: GameEngineSimple a b -> Int
-- ^ Run the provided game engine without a context until a terminal state is reached.
playSimple = runIdentity . play
playIO :: GameEngineIO a b -> IO Int
-- ^ Run the provided game engine within an IO context until a terminal state is reached.
playIO = play
getNextState :: Monad m => GameEngine m a b -> m (GameState a)
getNextState engine = do
nextMove <- getMove gameActions gameState
getResult gameActions gameState nextMove
where
gameActions = actions engine
gameState = state engine