swarm-0.6.0.0: src/swarm-engine/Swarm/Game/Scenario/Objective/WinCheck.hs
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Utilities to check whether conditions are met for a game win/loss.
module Swarm.Game.Scenario.Objective.WinCheck where
import Control.Lens (andOf, view, (^.), (^..))
import Data.Aeson (ToJSON)
import Data.BoolExpr qualified as BE
import Data.BoolExpr.Simplify qualified as Simplify
import Data.List (partition)
import Data.Map qualified as M
import Data.Set (Set)
import Data.Set qualified as Set
import GHC.Generics (Generic)
import Servant.Docs (ToSample)
import Servant.Docs qualified as SD
import Swarm.Game.Scenario.Objective
import Swarm.Game.Scenario.Objective.Graph (getDistinctConstants)
import Swarm.Game.Scenario.Objective.Logic as L
import Swarm.Util.Lens (concatFold)
-- | We have "won" if all of the "unwinnable" or remaining "incomplete" objectives are "optional".
didWin :: ObjectiveCompletion -> Bool
didWin = andOf ((incompleteObjectives `concatFold` unwinnableObjectives) . objectiveOptional)
-- | We have "lost" if any of the "unwinnable" objectives are not "optional".
didLose :: ObjectiveCompletion -> Bool
didLose = not . andOf (unwinnableObjectives . objectiveOptional)
isPrereqsSatisfied :: ObjectiveCompletion -> Objective -> Bool
isPrereqsSatisfied completions =
maybe True f . view objectivePrerequisite
where
f = BE.evalBoolExpr getTruth . L.toBoolExpr . logic
getTruth :: ObjectiveLabel -> Bool
getTruth label = Set.member label $ completions ^. completedIDs
isUnwinnablePrereq :: Set ObjectiveLabel -> Prerequisite ObjectiveLabel -> Bool
isUnwinnablePrereq completed =
Simplify.cannotBeTrue . Simplify.replace boolMap . L.toBoolExpr
where
boolMap = M.fromList . map (,True) . Set.toList $ completed
isUnwinnable :: ObjectiveCompletion -> Objective -> Bool
isUnwinnable completions obj =
maybe False (isUnwinnablePrereq (completions ^. completedIDs) . logic) $ obj ^. objectivePrerequisite
-- | The first element of the returned tuple consists of "active" objectives,
-- the second element "inactive".
partitionActiveObjectives :: ObjectiveCompletion -> ([Objective], [Objective])
partitionActiveObjectives oc =
partition (isPrereqsSatisfied oc) $ oc ^.. incompleteObjectives
getActiveObjectives :: ObjectiveCompletion -> [Objective]
getActiveObjectives =
fst . partitionActiveObjectives
-- | For debugging only (via Web API)
data PrereqSatisfaction = PrereqSatisfaction
{ objective :: Objective
, deps :: Set (BE.Signed ObjectiveLabel)
, prereqsSatisfied :: Bool
}
deriving (Generic, ToJSON)
instance ToSample PrereqSatisfaction where
toSamples _ = SD.noSamples
-- | Used only by the web interface for debugging
getSatisfaction :: ObjectiveCompletion -> [PrereqSatisfaction]
getSatisfaction oc = map f $ oc ^.. allObjectives
where
f y =
PrereqSatisfaction
y
(maybe mempty (getDistinctConstants . logic) $ y ^. objectivePrerequisite)
(isPrereqsSatisfied oc y)