packages feed

diplomacy-server-0.1.0.0: Resources/Advance.hs

{-|
Module      : Resources.Advance
Description : 
Copyright   : (c) Alexander Vieth, 2015
Licence     : BSD3
Maintainer  : aovieth@gmail.com
Stability   : experimental
Portability : non-portable (GHC only)
-}

{-# LANGUAGE AutoDeriveTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE GADTs #-}

module Resources.Advance (

      resource
    , advance

    ) where

import GHC.Generics
import Data.Typeable
import Control.Monad.Trans.Class
import Control.Monad.Trans.Reader
import Control.Monad.Trans.Except
import Control.Monad.State.Class as SC
import qualified Data.Map as M
import Data.AtLeast
import Data.TypeNat.Vect
import Data.Aeson
import Data.JSON.Schema
import Rest
import Rest.Resource as R
import Diplomacy.Game
import Diplomacy.SupplyCentreDeficit
import Types.Server
import Types.GameId
import Types.Credentials
import Types.GameState

resource :: Resource (ReaderT GameId Server) (ReaderT GameId Server) () Void Void
resource = mkResourceId
    { R.name = "advance"
    , R.schema = singleton () $ unnamedSingle (const ())
    , R.update = Just update
    }
  where

    update :: Handler (ReaderT GameId Server)
    update = secureHandler $ mkInputHandler (jsonO . jsonE . jsonI) $ doUpdate

    doUpdate :: AdvanceInput -> ExceptT (Reason AdvanceError) (ReaderT GameId Server) AdvanceOutput
    doUpdate input = withAdminCredentials creds advanceGame
      where
        AdvanceInput creds = input

    advanceGame :: ExceptT (Reason AdvanceError) (ReaderT GameId Server) AdvanceOutput
    advanceGame = do
        gameId <- lift ask
        modifyGameState gameId modifier
        return GameAdvanced

    modifier :: GameState -> ExceptT (Reason AdvanceError) (ReaderT GameId Server) GameState
    modifier gameState = case gameState of
        GameNotStarted _ _ _ -> throwE (domainReason AdvanceGameNotStarted)
        GameStarted m (AtLeast (VCons someGame VNil) rest) duration duration' elapsed _ -> do
            state <- SC.get
            let nextGame = advance someGame
            return (GameStarted m (AtLeast (VCons nextGame VNil) (someGame : rest)) duration duration' (currentTime state) False)

-- | Advance a game, resolving and then continuing, so that we also go from
--   Unresolved to Unresolved. Retreat and Adjust phases in which there is
--   nothing to do are skipped, but they are not forgotten! They're given
--   in the return value.
advance :: SomeGame -> SomeGame
advance (SomeGame game) = SomeGame (continue (resolve game))
{-
advance (SomeGame game) = case game of
    TypicalGame TypicalRoundOne Unresolved _ _ _ ->
        let resolved = resolve game
            continued = continue resolved
        in  case M.size (gameDislodged continued) of
                -- Automatically skip retreat phases where nobody is
                -- dislodged.
                0 -> advance (SomeGame continued)
                _ -> AtLeast (VCons (SomeGame continued) VNil) []
    TypicalGame TypicalRoundTwo Unresolved _ _ _ ->
        let resolved = resolve game
            continued = continue resolved
        in  case M.size (gameDislodged continued) of
                -- Automatically skip retreat phases where nobody is
                -- dislodged.
                0 -> advance (SomeGame continued)
                _ -> SomeGame continued
    RetreatGame RetreatRoundOne Unresolved _ _ _ _ _ ->
        let resolved = resolve game
            continued = continue resolved
        in  SomeGame continued
    RetreatGame RetreatRoundTwo Unresolved _ _ _ _ _ ->
        let resolved = resolve game
            continued = continue resolved
            occupation = gameOccupation continued
            control = gameControl continued
            deficits = fmap (\greatPower -> supplyCentreDeficit greatPower occupation control) [minBound..maxBound]
        in  if all (== 0) deficits
            -- Automatically skip adjust phases where nobody has a deficit,
            -- positive or negative.
            -- TODO must also check that, in case there's a negative
            -- deficit, this great power actually has a place to build.
            -- Really, we ought to use the order synthesizers to see whether
            -- any player actually has a choice. That goes for typical
            -- phase as well; it's rare, but it could happen, maybe?
            -- No, that would cause infinite loop behaviour in advance.
            then advance (SomeGame continued)
            else SomeGame continued
    AdjustGame _ Unresolved _ _ _ ->
        let resolved = resolve game
            continued = continue resolved
        in  SomeGame continued
-}

data AdvanceError = AdvanceGameNotStarted

instance ToResponseCode AdvanceError where
    toResponseCode _ = 403

deriving instance Generic AdvanceError
deriving instance Typeable AdvanceError
instance FromJSON AdvanceError
instance ToJSON AdvanceError
instance JSONSchema AdvanceError where
    schema = gSchema

newtype AdvanceInput = AdvanceInput Credentials

deriving instance Generic AdvanceInput
deriving instance Typeable AdvanceInput
instance FromJSON AdvanceInput
instance ToJSON AdvanceInput
instance JSONSchema AdvanceInput where
    schema = gSchema

data AdvanceOutput = GameAdvanced

deriving instance Generic AdvanceOutput
deriving instance Typeable AdvanceOutput
instance FromJSON AdvanceOutput
instance ToJSON AdvanceOutput
instance JSONSchema AdvanceOutput where
    schema = gSchema