module GameEngine (
    Symbol,
    GameState(..),
    Player(..),
    Move(..),
    GameActions(..),
    GameEngine(..),
    play
  ) where


type Symbol = Char


data GameState a = GameState a


data Player = Player Char


data Move a = Move a


data GameActions a b = GameActions {
    getPlayer  :: GameState a -> Player,
    getMoves   :: GameState a -> [Move b],
    getResult  :: GameState a -> Move b -> GameState a,
    isTerminal :: GameState a -> Bool,
    getScore   :: GameState a -> Player -> Int
  }


data GameEngine a b = GameEngine {
    gameActions :: GameActions a b,
    state       :: GameState a
  }


play :: GameEngine a b -> Int
play engine
  | performWithState isTerminal engine = performWithState getScore engine $ performWithState getPlayer engine
  | otherwise = play $ GameEngine (gameActions engine) (getNextState engine)


getNextState :: GameEngine a b -> GameState a
getNextState engine = performWithState getResult engine . head $ performWithState getMoves engine


performWithState :: (GameActions a b -> GameState a -> c) -> GameEngine a b -> c
performWithState f engine = f (gameActions engine) $ state engine