module Problem1a where

class Monad m => GameMonad m where
  extraLife  :: m ()
  getLives   :: m Int
  checkPoint :: m a -> m a
  die        :: m a

newtype Game a = Game { unGame :: Int -> Result a }

type Result a = Either Int (a, Int)
-- Alternative definitions:
-- type Result a = (Maybe a, Int)
-- data Result a = GameOver | Died Int | Survived a Int

instance Monad Game where
  return x = Game $ \n -> Right (x, n)
  m >>= f  = Game $ \n -> case unGame m n of
    Left n       -> Left n
    Right (x, n) -> unGame (f x) n

instance GameMonad Game where
  extraLife    = Game $ \n -> Right ((), n + 1)
  getLives     = Game $ \n -> Right (n, n)
  die          = Game $ \n -> Left (n - 1)
  checkPoint m = Game $ \n -> case unGame m n of
    Left n | n > 0     -> unGame (checkPoint m) n
           | otherwise -> Left 0
    Right (x, n)       -> Right (x, n)

runGame :: Game a -> Int -> Maybe (a, Int)
runGame m n = case unGame m n of
  Left _  -> Nothing
  Right r -> Just r