module Diplomacy.Game (
Game(..)
, Round(..)
, RoundStatus(..)
, Status(..)
, TypicalRound(..)
, RetreatRound(..)
, AdjustRound(..)
, NextRound
, RoundPhase
, RoundOrderConstructor
, roundToInt
, nextRound
, prevRound
, gameZonedOrders
, gameZonedResolvedOrders
, gameOccupation
, gameDislodged
, gameControl
, gameTurn
, gameRound
, gameSeason
, issueOrders
, removeBuildOrders
, resolve
, continue
, newGame
, showGame
) where
import Control.Applicative
import qualified Data.Map as M
import qualified Data.Set as S
import Data.List (sortBy, intersperse)
import Diplomacy.Turn
import Diplomacy.Season
import Diplomacy.GreatPower
import Diplomacy.Aligned
import Diplomacy.Unit
import Diplomacy.Order
import Diplomacy.OrderObject
import Diplomacy.Phase
import Diplomacy.Province
import Diplomacy.Zone
import Diplomacy.Occupation
import Diplomacy.Dislodgement
import Diplomacy.Control
import Diplomacy.Subject
import Diplomacy.SupplyCentreDeficit
import Diplomacy.OrderResolution
import Diplomacy.OrderValidation
data Round where
RoundOne :: Round
RoundTwo :: Round
RoundThree :: Round
RoundFour :: Round
RoundFive :: Round
deriving instance Show Round
deriving instance Enum Round
deriving instance Bounded Round
deriving instance Eq Round
deriving instance Ord Round
roundToInt :: Round -> Int
roundToInt = fromEnum
nextRound :: Round -> Round
nextRound round = case round of
RoundOne -> RoundTwo
RoundTwo -> RoundThree
RoundThree -> RoundFour
RoundFour -> RoundFive
RoundFive -> RoundOne
prevRound :: Round -> Round
prevRound round = case round of
RoundOne -> RoundFive
RoundTwo -> RoundOne
RoundThree -> RoundTwo
RoundFour -> RoundThree
RoundFive -> RoundFour
data RoundStatus where
RoundUnresolved :: RoundStatus
RoundResolved :: RoundStatus
deriving instance Show RoundStatus
data Status (roundStatus :: RoundStatus) where
Unresolved :: Status RoundUnresolved
Resolved :: Status RoundResolved
type family RoundOrderConstructor (roundStatus :: RoundStatus) :: Phase -> * where
RoundOrderConstructor RoundUnresolved = SomeOrderObject
RoundOrderConstructor RoundResolved = SomeResolved OrderObject
data TypicalRound (round :: Round) where
TypicalRoundOne :: TypicalRound RoundOne
TypicalRoundTwo :: TypicalRound RoundThree
deriving instance Show (TypicalRound round)
nextRetreatRound :: TypicalRound round -> RetreatRound (NextRound round)
nextRetreatRound typicalRound = case typicalRound of
TypicalRoundOne -> RetreatRoundOne
TypicalRoundTwo -> RetreatRoundTwo
data RetreatRound (round :: Round) where
RetreatRoundOne :: RetreatRound RoundTwo
RetreatRoundTwo :: RetreatRound RoundFour
deriving instance Show (RetreatRound round)
data AdjustRound (round :: Round) where
AdjustRound :: AdjustRound RoundFive
deriving instance Show (AdjustRound round)
type family NextRound (round :: Round) :: Round where
NextRound RoundOne = RoundTwo
NextRound RoundTwo = RoundThree
NextRound RoundThree = RoundFour
NextRound RoundFour = RoundFive
NextRound RoundFive = RoundOne
type family RoundPhase (round :: Round) :: Phase where
RoundPhase RoundOne = Typical
RoundPhase RoundTwo = Retreat
RoundPhase RoundThree = Typical
RoundPhase RoundFour = Retreat
RoundPhase RoundFive = Adjust
data Game (round :: Round) (roundStatus :: RoundStatus) where
TypicalGame
:: TypicalRound round
-> Status roundStatus
-> Turn
-> M.Map Zone (Aligned Unit, RoundOrderConstructor roundStatus Typical)
-> Control
-> Game round roundStatus
RetreatGame
:: RetreatRound round
-> Status roundStatus
-> Turn
-> Resolution Typical
-> M.Map Zone (Aligned Unit, RoundOrderConstructor roundStatus Retreat)
-> Occupation
-> Control
-> Game round roundStatus
AdjustGame
:: AdjustRound round
-> Status roundStatus
-> Turn
-> M.Map Zone (Aligned Unit, RoundOrderConstructor roundStatus Adjust)
-> Control
-> Game round roundStatus
newGame :: Game RoundOne RoundUnresolved
newGame = TypicalGame TypicalRoundOne Unresolved firstTurn zonedOrders thisControl
where
zonedOrders = M.mapWithKey giveDefaultOrder thisOccupation
giveDefaultOrder
:: Zone
-> Aligned Unit
-> (Aligned Unit, SomeOrderObject Typical)
giveDefaultOrder zone aunit = (aunit, SomeOrderObject (MoveObject (zoneProvinceTarget zone)))
thisOccupation =
occupy (Normal London) (Just (align Fleet England))
. occupy (Normal Edinburgh) (Just (align Fleet England))
. occupy (Normal Liverpool) (Just (align Army England))
. occupy (Normal Brest) (Just (align Fleet France))
. occupy (Normal Paris) (Just (align Army France))
. occupy (Normal Marseilles) (Just (align Army France))
. occupy (Normal Venice) (Just (align Army Italy))
. occupy (Normal Rome) (Just (align Army Italy))
. occupy (Normal Naples) (Just (align Fleet Italy))
. occupy (Normal Kiel) (Just (align Fleet Germany))
. occupy (Normal Berlin) (Just (align Army Germany))
. occupy (Normal Munich) (Just (align Army Germany))
. occupy (Normal Vienna) (Just (align Army Austria))
. occupy (Normal Budapest) (Just (align Army Austria))
. occupy (Normal Trieste) (Just (align Fleet Austria))
. occupy (Normal Warsaw) (Just (align Army Russia))
. occupy (Normal Moscow) (Just (align Army Russia))
. occupy (Special StPetersburgSouth) (Just (align Fleet Russia))
. occupy (Normal Sevastopol) (Just (align Fleet Russia))
. occupy (Normal Constantinople) (Just (align Army Turkey))
. occupy (Normal Smyrna) (Just (align Army Turkey))
. occupy (Normal Ankara) (Just (align Fleet Turkey))
$ emptyOccupation
thisControl :: Control
thisControl = foldr (\(power, province) -> control province (Just power)) emptyControl controlList
where
controlList :: [(GreatPower, Province)]
controlList = [ (power, province) | power <- greatPowers, province <- filter (isHome power) supplyCentres ]
greatPowers :: [GreatPower]
greatPowers = [minBound..maxBound]
showGame :: Game round roundStatus -> String
showGame game = concat . intersperse "\n" $ [
showGameMetadata game
, "****"
, middle
, "****"
, showControl (gameControl game)
]
where
middle = case game of
TypicalGame _ Unresolved _ _ _ -> showZonedOrders (gameZonedOrders game)
RetreatGame _ Unresolved _ _ _ _ _ -> showZonedOrders (gameZonedOrders game)
AdjustGame _ Unresolved _ _ _ -> showZonedOrders (gameZonedOrders game)
TypicalGame _ Resolved _ _ _ -> showZonedResolvedOrders (gameZonedResolvedOrders game)
RetreatGame _ Resolved _ _ _ _ _ -> showZonedResolvedOrders (gameZonedResolvedOrders game)
AdjustGame _ Resolved _ _ _ -> showZonedResolvedOrders (gameZonedResolvedOrders game)
showGameMetadata :: Game round roundStatus -> String
showGameMetadata game = concat . intersperse "\n" $ [
"Year: " ++ show year
, "Season: " ++ show season
, "Phase: " ++ show phase
]
where
year = 1900 + turnToInt (gameTurn game)
season = gameSeason game
phase = gamePhase game
showOccupation :: Occupation -> String
showOccupation = concat . intersperse "\n" . M.foldWithKey foldShowAlignedUnit []
where
foldShowAlignedUnit zone aunit b =
concat [show provinceTarget, ": ", show greatPower, " ", show unit] : b
where
provinceTarget = zoneProvinceTarget zone
greatPower = alignedGreatPower aunit
unit = alignedThing aunit
showZonedOrders :: M.Map Zone (Aligned Unit, SomeOrderObject phase) -> String
showZonedOrders = concat . intersperse "\n" . M.foldWithKey foldShowOrder []
where
foldShowOrder zone (aunit, SomeOrderObject object) b =
concat [show provinceTarget, ": ", show greatPower, " ", show unit, " ", objectString] : b
where
provinceTarget = zoneProvinceTarget zone
greatPower = alignedGreatPower aunit
unit = alignedThing aunit
objectString = case object of
MoveObject pt ->
if pt == zoneProvinceTarget zone
then "hold"
else "move to " ++ show pt
SupportObject subj pt -> concat ["support ", show supportedUnit, " at ", show supportedPt, " into ", show pt]
where
supportedUnit = subjectUnit subj
supportedPt = subjectProvinceTarget subj
ConvoyObject subj pt -> concat ["convoy ", show convoyedUnit, " from ", show convoyedFrom, " to ", show pt]
where
convoyedUnit = subjectUnit subj
convoyedFrom = subjectProvinceTarget subj
SurrenderObject -> "surrender"
WithdrawObject pt -> "withdraw to " ++ show pt
DisbandObject -> "disband"
BuildObject -> "build"
ContinueObject -> "continue"
showZonedResolvedOrders :: M.Map Zone (Aligned Unit, SomeResolved OrderObject phase) -> String
showZonedResolvedOrders = concat . intersperse "\n" . M.foldWithKey foldShowResolvedOrder []
where
foldShowResolvedOrder
:: Zone
-> (Aligned Unit, SomeResolved OrderObject phase)
-> [String]
-> [String]
foldShowResolvedOrder zone (aunit, SomeResolved (object, resolution)) b =
concat [show provinceTarget, ": ", show greatPower, " ", show unit, " ", objectString, " ", resolutionString] : b
where
provinceTarget = zoneProvinceTarget zone
greatPower = alignedGreatPower aunit
unit = alignedThing aunit
objectString = case object of
MoveObject pt ->
if pt == zoneProvinceTarget zone
then "hold"
else "move to " ++ show pt
SupportObject subj pt -> concat ["support ", show supportedUnit, " at ", show supportedPt, " into ", show pt]
where
supportedUnit = subjectUnit subj
supportedPt = subjectProvinceTarget subj
ConvoyObject subj pt -> concat ["convoy ", show convoyedUnit, " from ", show convoyedFrom, " to ", show pt]
where
convoyedUnit = subjectUnit subj
convoyedFrom = subjectProvinceTarget subj
SurrenderObject -> "surrender"
WithdrawObject pt -> "withdraw to " ++ show pt
DisbandObject -> "disband"
BuildObject -> "build"
ContinueObject -> "continue"
resolutionString = case resolution of
Nothing -> "✓"
Just reason -> "✗ " ++ show reason
showControl :: Control -> String
showControl = concat . intersperse "\n" . M.foldWithKey foldShowControl []
where
foldShowControl province greatPower b = concat [show province, ": ", show greatPower] : b
gameStatus :: Game round roundStatus -> Status roundStatus
gameStatus game = case game of
TypicalGame _ x _ _ _ -> x
RetreatGame _ x _ _ _ _ _ -> x
AdjustGame _ x _ _ _ -> x
gameZonedOrders
:: Game round RoundUnresolved
-> M.Map Zone (Aligned Unit, SomeOrderObject (RoundPhase round))
gameZonedOrders game = case game of
TypicalGame TypicalRoundOne _ _ x _ -> x
TypicalGame TypicalRoundTwo _ _ x _ -> x
RetreatGame RetreatRoundOne _ _ _ x _ _ -> x
RetreatGame RetreatRoundTwo _ _ _ x _ _ -> x
AdjustGame AdjustRound _ _ x _ -> x
gameZonedResolvedOrders
:: Game round RoundResolved
-> M.Map Zone (Aligned Unit, SomeResolved OrderObject (RoundPhase round))
gameZonedResolvedOrders game = case game of
TypicalGame TypicalRoundOne _ _ x _ -> x
TypicalGame TypicalRoundTwo _ _ x _ -> x
RetreatGame RetreatRoundOne _ _ _ x _ _ -> x
RetreatGame RetreatRoundTwo _ _ _ x _ _ -> x
AdjustGame AdjustRound _ _ x _ -> x
gameOccupation :: Game round roundStatus -> Occupation
gameOccupation game = case game of
TypicalGame _ _ _ zonedOrders _ -> M.map fst zonedOrders
RetreatGame _ _ _ _ _ x _ -> x
AdjustGame _ Unresolved _ zonedOrders _ -> M.mapMaybe selectDisbandOrContinue zonedOrders
where
selectDisbandOrContinue :: (Aligned Unit, SomeOrderObject Adjust) -> Maybe (Aligned Unit)
selectDisbandOrContinue (aunit, SomeOrderObject object) = case object of
DisbandObject -> Just aunit
ContinueObject -> Just aunit
_ -> Nothing
AdjustGame _ Resolved _ zonedOrders _ -> M.mapMaybe selectBuildOrContinue zonedOrders
where
selectBuildOrContinue :: (Aligned Unit, SomeResolved OrderObject Adjust) -> Maybe (Aligned Unit)
selectBuildOrContinue (aunit, SomeResolved (object, _)) = case object of
BuildObject -> Just aunit
ContinueObject -> Just aunit
_ -> Nothing
gameDislodged
:: (RoundPhase round ~ Retreat)
=> Game round RoundUnresolved
-> M.Map Zone (Aligned Unit)
gameDislodged game = case game of
RetreatGame _ Unresolved _ _ zonedOrders _ _ -> M.map fst zonedOrders
gameResolved
:: (RoundPhase round ~ Retreat)
=> Game round RoundUnresolved
-> M.Map Zone (Aligned Unit, SomeResolved OrderObject Typical)
gameResolved game = case game of
RetreatGame _ _ _ x _ _ _ -> x
gameControl :: Game round roundStatus -> Control
gameControl game = case game of
TypicalGame _ _ _ _ c -> c
RetreatGame _ _ _ _ _ _ c -> c
AdjustGame _ _ _ _ c -> c
gameTurn :: Game round roundStatus -> Turn
gameTurn game = case game of
TypicalGame _ _ t _ _ -> t
RetreatGame _ _ t _ _ _ _ -> t
AdjustGame _ _ t _ _ -> t
gameRound :: Game round roundStatus -> Round
gameRound game = case game of
TypicalGame TypicalRoundOne _ _ _ _ -> RoundOne
TypicalGame TypicalRoundTwo _ _ _ _ -> RoundThree
RetreatGame RetreatRoundOne _ _ _ _ _ _ -> RoundTwo
RetreatGame RetreatRoundTwo _ _ _ _ _ _ -> RoundFour
AdjustGame AdjustRound _ _ _ _ -> RoundFive
gameSeason :: Game round roundStatus -> Season
gameSeason game = case game of
TypicalGame TypicalRoundOne _ _ _ _ -> Spring
RetreatGame RetreatRoundOne _ _ _ _ _ _ -> Spring
TypicalGame TypicalRoundTwo _ _ _ _ -> Fall
RetreatGame RetreatRoundTwo _ _ _ _ _ _ -> Fall
AdjustGame _ _ _ _ _ -> Winter
gamePhase :: Game round roundStatus -> Phase
gamePhase game = case game of
TypicalGame _ _ _ _ _ -> Typical
RetreatGame _ _ _ _ _ _ _ -> Retreat
AdjustGame _ _ _ _ _ -> Adjust
type family ValidateOrdersOutput (phase :: Phase) :: * where
ValidateOrdersOutput Typical = M.Map Zone (Aligned Unit, SomeOrderObject Typical, S.Set (SomeValidityCriterion Typical))
ValidateOrdersOutput Retreat = M.Map Zone (Aligned Unit, SomeOrderObject Retreat, S.Set (SomeValidityCriterion Retreat))
ValidateOrdersOutput Adjust = (M.Map Zone (Aligned Unit, SomeOrderObject Adjust, S.Set (SomeValidityCriterion Adjust)), M.Map GreatPower (S.Set AdjustSetValidityCriterion))
issueOrders
:: forall round .
M.Map Zone (Aligned Unit, SomeOrderObject (RoundPhase round))
-> Game round RoundUnresolved
-> (ValidateOrdersOutput (RoundPhase round), Game round RoundUnresolved)
issueOrders orders game =
let nextGame = case game of
AdjustGame AdjustRound _ _ _ _ -> issueOrdersUnsafe orders (removeBuildOrders greatPowers game)
where
greatPowers :: S.Set GreatPower
greatPowers = M.fold pickGreatPower S.empty orders
pickGreatPower :: (Aligned Unit, t) -> S.Set GreatPower -> S.Set GreatPower
pickGreatPower (aunit, _) = S.insert (alignedGreatPower aunit)
_ -> issueOrdersUnsafe orders game
validation :: ValidateOrdersOutput (RoundPhase round)
allValid :: Bool
(validation, allValid) = case game of
TypicalGame TypicalRoundOne _ _ _ _ ->
let validation = validateOrders orders game
invalids = M.fold pickInvalids S.empty validation
in (validation, S.null invalids)
TypicalGame TypicalRoundTwo _ _ _ _ ->
let validation = validateOrders orders game
invalids = M.fold pickInvalids S.empty validation
in (validation, S.null invalids)
RetreatGame RetreatRoundOne _ _ _ _ _ _ ->
let validation = validateOrders orders game
invalids = M.fold pickInvalids S.empty validation
in (validation, S.null invalids)
RetreatGame RetreatRoundTwo _ _ _ _ _ _ ->
let validation = validateOrders orders game
invalids = M.fold pickInvalids S.empty validation
in (validation, S.null invalids)
AdjustGame AdjustRound _ _ _ _ ->
let validation = validateOrders orders game
invalids = M.fold pickInvalids S.empty (fst validation)
adjustSetInvalids = M.fold S.union S.empty (snd validation)
in (validation, S.null invalids && S.null adjustSetInvalids)
in if allValid
then (validation, nextGame)
else (validation, game)
where
pickInvalids
:: (Aligned Unit, SomeOrderObject phase, S.Set (SomeValidityCriterion phase))
-> S.Set (SomeValidityCriterion phase)
-> S.Set (SomeValidityCriterion phase)
pickInvalids (_, _, x) = S.union x
validateOrders
:: forall round .
M.Map Zone (Aligned Unit, SomeOrderObject (RoundPhase round))
-> Game round RoundUnresolved
-> ValidateOrdersOutput (RoundPhase round)
validateOrders orders game = case game of
TypicalGame TypicalRoundOne _ _ _ _ -> M.mapWithKey (validateOrderTypical game) orders
TypicalGame TypicalRoundTwo _ _ _ _ -> M.mapWithKey (validateOrderTypical game) orders
RetreatGame RetreatRoundOne _ _ _ _ _ _ -> M.mapWithKey (validateOrderRetreat game) orders
RetreatGame RetreatRoundTwo _ _ _ _ _ _ -> M.mapWithKey (validateOrderRetreat game) orders
AdjustGame AdjustRound _ _ _ _ ->
let independent = M.mapWithKey (validateOrderSubjectAdjust game) orders
ensemble = validateOrdersAdjust game orders
in (independent, ensemble)
where
validateOrderTypical
:: forall round .
( RoundPhase round ~ Typical )
=> Game round RoundUnresolved
-> Zone
-> (Aligned Unit, SomeOrderObject (RoundPhase round))
-> (Aligned Unit, SomeOrderObject (RoundPhase round), S.Set (SomeValidityCriterion Typical))
validateOrderTypical game zone (aunit, SomeOrderObject object) =
(aunit, SomeOrderObject object, validation)
where
validation = case object of
MoveObject _ -> analyze snd (S.singleton . SomeValidityCriterion . fst) S.empty S.union (moveVOC greatPower occupation) (Order (subject, object))
SupportObject _ _ -> analyze snd (S.singleton . SomeValidityCriterion . fst) S.empty S.union (supportVOC greatPower occupation) (Order (subject, object))
ConvoyObject _ _ -> analyze snd (S.singleton . SomeValidityCriterion . fst) S.empty S.union (convoyVOC greatPower occupation) (Order (subject, object))
occupation = gameOccupation game
greatPower = alignedGreatPower aunit
unit = alignedThing aunit
subject = (unit, zoneProvinceTarget zone)
validateOrderRetreat
:: forall round .
( RoundPhase round ~ Retreat )
=> Game round RoundUnresolved
-> Zone
-> (Aligned Unit, SomeOrderObject (RoundPhase round))
-> (Aligned Unit, SomeOrderObject (RoundPhase round), S.Set (SomeValidityCriterion Retreat))
validateOrderRetreat game zone (aunit, SomeOrderObject object) =
(aunit, SomeOrderObject object, validation)
where
validation = case object of
SurrenderObject -> analyze snd (S.singleton . SomeValidityCriterion . fst) S.empty S.union (surrenderVOC greatPower dislodgement) (Order (subject, object))
WithdrawObject _ -> analyze snd (S.singleton . SomeValidityCriterion . fst) S.empty S.union (withdrawVOC greatPower resolved) (Order (subject, object))
occupation = gameOccupation game
resolved = gameResolved game
dislodgement = gameDislodged game
greatPower = alignedGreatPower aunit
unit = alignedThing aunit
subject = (unit, zoneProvinceTarget zone)
validateOrderSubjectAdjust
:: forall round .
( RoundPhase round ~ Adjust )
=> Game round RoundUnresolved
-> Zone
-> (Aligned Unit, SomeOrderObject (RoundPhase round))
-> (Aligned Unit, SomeOrderObject (RoundPhase round), S.Set (SomeValidityCriterion Adjust))
validateOrderSubjectAdjust game zone (aunit, SomeOrderObject object) =
(aunit, SomeOrderObject object, validation)
where
validation = case object of
ContinueObject -> analyze snd (S.singleton . SomeValidityCriterion . fst) S.empty S.union (continueSubjectVOC greatPower occupation) subject
DisbandObject -> analyze snd (S.singleton . SomeValidityCriterion . fst) S.empty S.union (disbandSubjectVOC greatPower occupation) subject
BuildObject -> analyze snd (S.singleton . SomeValidityCriterion . fst) S.empty S.union (buildSubjectVOC greatPower occupation control) subject
occupation = gameOccupation game
control = gameControl game
greatPower = alignedGreatPower aunit
unit = alignedThing aunit
subject = (unit, zoneProvinceTarget zone)
validateOrdersAdjust
:: forall round .
( RoundPhase round ~ Adjust )
=> Game round RoundUnresolved
-> M.Map Zone (Aligned Unit, SomeOrderObject (RoundPhase round))
-> M.Map GreatPower (S.Set AdjustSetValidityCriterion)
validateOrdersAdjust game orders = M.mapWithKey validation adjustSetsByGreatPower
where
validation
:: GreatPower
-> AdjustSubjects
-> S.Set AdjustSetValidityCriterion
validation greatPower subjects = analyze snd (S.singleton . fst) S.empty S.union (adjustSubjectsVOC greatPower occupation control subjects) subjects
adjustSetsByGreatPower :: M.Map GreatPower AdjustSubjects
adjustSetsByGreatPower = M.foldWithKey pickSubject M.empty orders
pickSubject
:: Zone
-> (Aligned Unit, SomeOrderObject (RoundPhase round))
-> M.Map GreatPower AdjustSubjects
-> M.Map GreatPower AdjustSubjects
pickSubject zone (aunit, SomeOrderObject object) = case object of
ContinueObject -> M.alter (alterContinue subject) greatPower
BuildObject -> M.alter (alterBuild subject) greatPower
DisbandObject -> M.alter (alterDisband subject) greatPower
where
subject = (alignedThing aunit, zoneProvinceTarget zone)
greatPower = alignedGreatPower aunit
alterContinue
:: Subject
-> Maybe AdjustSubjects
-> Maybe AdjustSubjects
alterContinue subject x = Just $ case x of
Nothing -> AdjustSubjects S.empty S.empty (S.singleton subject)
Just x' -> x' { continueSubjects = S.insert subject (continueSubjects x') }
alterBuild
:: Subject
-> Maybe AdjustSubjects
-> Maybe AdjustSubjects
alterBuild subject x = Just $ case x of
Nothing -> AdjustSubjects (S.singleton subject) S.empty S.empty
Just x' -> x' { buildSubjects = S.insert subject (buildSubjects x') }
alterDisband
:: Subject
-> Maybe AdjustSubjects
-> Maybe AdjustSubjects
alterDisband subject x = Just $ case x of
Nothing -> AdjustSubjects S.empty (S.singleton subject) S.empty
Just x' -> x' { disbandSubjects = S.insert subject (disbandSubjects x') }
occupation = gameOccupation game
control = gameControl game
issueOrdersUnsafe
:: forall round .
M.Map Zone (Aligned Unit, SomeOrderObject (RoundPhase round))
-> Game round RoundUnresolved
-> Game round RoundUnresolved
issueOrdersUnsafe validOrders game = M.foldWithKey issueOrderUnsafe game validOrders
where
issueOrderUnsafe
:: forall round .
Zone
-> (Aligned Unit, SomeOrderObject (RoundPhase round))
-> Game round RoundUnresolved
-> Game round RoundUnresolved
issueOrderUnsafe zone (aunit, someObject) game = case game of
TypicalGame TypicalRoundOne s t zonedOrders v -> TypicalGame TypicalRoundOne s t (insertOrder zonedOrders) v
TypicalGame TypicalRoundTwo s t zonedOrders v -> TypicalGame TypicalRoundTwo s t (insertOrder zonedOrders) v
RetreatGame RetreatRoundOne s t res zonedOrders o c -> RetreatGame RetreatRoundOne s t res (insertOrder zonedOrders) o c
RetreatGame RetreatRoundTwo s t res zonedOrders o c -> RetreatGame RetreatRoundTwo s t res (insertOrder zonedOrders) o c
AdjustGame AdjustRound s t zonedOrders c -> AdjustGame AdjustRound s t (insertOrder zonedOrders) c
where
insertOrder
:: M.Map Zone (Aligned Unit, SomeOrderObject (RoundPhase round))
-> M.Map Zone (Aligned Unit, SomeOrderObject (RoundPhase round))
insertOrder = M.alter (const (Just (aunit, someObject))) zone
removeBuildOrders
:: (RoundPhase round ~ Adjust)
=> S.Set GreatPower
-> Game round RoundUnresolved
-> Game round RoundUnresolved
removeBuildOrders greatPowers game = case game of
AdjustGame AdjustRound s t zonedOrders c ->
let zonedOrders' = M.filter (not . shouldRemove) zonedOrders
in AdjustGame AdjustRound s t zonedOrders' c
where
shouldRemove :: (Aligned Unit, SomeOrderObject Adjust) -> Bool
shouldRemove (aunit, SomeOrderObject object) = case (S.member greatPower greatPowers, object) of
(True, BuildObject) -> True
_ -> False
where
greatPower = alignedGreatPower aunit
resolve
:: Game round RoundUnresolved
-> Game round RoundResolved
resolve game = case game of
TypicalGame round _ turn zonedOrders control ->
TypicalGame round Resolved turn (typicalResolution zonedOrders) control
RetreatGame round _ turn previousResolution zonedOrders occupation control ->
RetreatGame round Resolved turn previousResolution (retreatResolution zonedOrders) occupation control
AdjustGame round _ turn zonedOrders control ->
AdjustGame round Resolved turn (adjustResolution zonedOrders) control
continue
:: Game round RoundResolved
-> Game (NextRound round) RoundUnresolved
continue game = case game of
TypicalGame round _ turn zonedResolvedOrders thisControl ->
RetreatGame (nextRetreatRound round) Unresolved turn zonedResolvedOrders nextZonedOrders occupation thisControl
where
nextZonedOrders :: M.Map Zone (Aligned Unit, SomeOrderObject Retreat)
nextZonedOrders = M.map giveDefaultRetreatOrder dislodgement
giveDefaultRetreatOrder
:: Aligned Unit
-> (Aligned Unit, SomeOrderObject Retreat)
giveDefaultRetreatOrder aunit = (aunit, SomeOrderObject object)
where
object = SurrenderObject
(dislodgement, occupation) = dislodgementAndOccupation zonedResolvedOrders
RetreatGame RetreatRoundOne _ turn _ zonedResolvedOrders occupation thisControl ->
TypicalGame TypicalRoundTwo Unresolved turn nextZonedOrders thisControl
where
nextZonedOrders :: M.Map Zone (Aligned Unit, SomeOrderObject Typical)
nextZonedOrders = M.mapWithKey giveDefaultTypicalOrder nextOccupation
giveDefaultTypicalOrder
:: Zone
-> Aligned Unit
-> (Aligned Unit, SomeOrderObject Typical)
giveDefaultTypicalOrder zone aunit = (aunit, SomeOrderObject object)
where
object = MoveObject (zoneProvinceTarget zone)
nextOccupation :: Occupation
nextOccupation = M.foldWithKey occupationFold occupation zonedResolvedOrders
occupationFold
:: Zone
-> (Aligned Unit, SomeResolved OrderObject Retreat)
-> Occupation
-> Occupation
occupationFold zone (aunit, SomeResolved (object, res)) =
case (object, res) of
(WithdrawObject withdrawingTo, Nothing) -> occupy withdrawingTo (Just aunit)
_ -> id
RetreatGame RetreatRoundTwo _ turn _ zonedResolvedOrders occupation thisControl ->
AdjustGame AdjustRound Unresolved turn nextZonedOrders nextControl
where
nextZonedOrders :: M.Map Zone (Aligned Unit, SomeOrderObject Adjust)
nextZonedOrders = M.mapWithKey giveDefaultAdjustOrder nextOccupation
zonesByDistance :: M.Map GreatPower [Zone]
zonesByDistance =
M.mapWithKey
(\k -> sortWith (distanceFromHomeSupplyCentre k . ptProvince . zoneProvinceTarget))
(M.foldWithKey foldZonesByDistance M.empty occupation)
sortWith f = sortBy (\x y -> f x `compare` f y)
foldZonesByDistance
:: Zone
-> Aligned Unit
-> M.Map GreatPower [Zone]
-> M.Map GreatPower [Zone]
foldZonesByDistance zone aunit = M.alter alteration (alignedGreatPower aunit)
where
alteration m = case m of
Nothing -> Just [zone]
Just zs -> Just (zone : zs)
disbands :: S.Set Zone
disbands = M.foldWithKey foldDisbands S.empty zonesByDistance
foldDisbands
:: GreatPower
-> [Zone]
-> S.Set Zone
-> S.Set Zone
foldDisbands greatPower zones = S.union (S.fromList (take deficit zones))
where
deficit = supplyCentreDeficit greatPower nextOccupation nextControl
giveDefaultAdjustOrder
:: Zone
-> Aligned Unit
-> (Aligned Unit, SomeOrderObject Adjust)
giveDefaultAdjustOrder zone aunit = case S.member zone disbands of
True -> (aunit, SomeOrderObject DisbandObject)
False -> (aunit, SomeOrderObject ContinueObject)
nextOccupation :: Occupation
nextOccupation = M.foldWithKey occupationFold occupation zonedResolvedOrders
occupationFold
:: Zone
-> (Aligned Unit, SomeResolved OrderObject Retreat)
-> Occupation
-> Occupation
occupationFold zone (aunit, SomeResolved (object, res)) =
case (object, res) of
(WithdrawObject withdrawingTo, Nothing) -> occupy withdrawingTo (Just aunit)
_ -> id
nextControl :: Control
nextControl = M.foldWithKey controlFold thisControl nextOccupation
controlFold
:: Zone
-> Aligned Unit
-> Control
-> Control
controlFold zone aunit = control (ptProvince (zoneProvinceTarget zone)) (Just (alignedGreatPower aunit))
AdjustGame AdjustRound _ turn zonedResolvedOrders thisControl ->
TypicalGame TypicalRoundOne Unresolved (nextTurn turn) nextZonedOrders thisControl
where
nextZonedOrders :: M.Map Zone (Aligned Unit, SomeOrderObject Typical)
nextZonedOrders = M.mapWithKey giveDefaultTypicalOrder nextOccupation
giveDefaultTypicalOrder
:: Zone
-> Aligned Unit
-> (Aligned Unit, SomeOrderObject Typical)
giveDefaultTypicalOrder zone aunit = (aunit, SomeOrderObject object)
where
object = MoveObject (zoneProvinceTarget zone)
nextOccupation :: Occupation
nextOccupation = M.mapMaybe mapOccupation zonedResolvedOrders
mapOccupation
:: (Aligned Unit, SomeResolved OrderObject Adjust)
-> Maybe (Aligned Unit)
mapOccupation (aunit, SomeResolved (object, resolution)) =
case (object, resolution) of
(DisbandObject, Nothing) -> Nothing
(BuildObject, Nothing) -> Just aunit
(ContinueObject, Nothing) -> Just aunit