packages feed

swarm-0.6.0.0: src/swarm-engine/Swarm/Game/Step/Path/Cache/DistanceLimit.hs

-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Handles cache invalidation if the distance
-- limit is modified between invocations of
-- the 'Path' command.
module Swarm.Game.Step.Path.Cache.DistanceLimit (
  getDistanceLimitInvalidation,
  withinDistance,
) where

import Control.Monad (unless)
import Data.List.NonEmpty (NonEmpty)
import Data.List.NonEmpty qualified as NE
import Swarm.Game.Location
import Swarm.Game.Step.Path.Type

-- |
-- A greater distance limit might yield a shorter path
-- if there was a better route that just needed to venture outside
-- of the allowed radius.
--
-- On the other hand, a smaller distance limit /will not/ invalidate
-- the cache so long as all cells on the path are within the new limit.
getDistanceLimitInvalidation ::
  -- | current robot location
  Location ->
  -- | original path
  NonEmpty Location ->
  -- | current limit
  Maybe Integer ->
  -- | previous limit
  Maybe Integer ->
  Either DistanceLimitChange ()
-- Limit unchanged:
getDistanceLimitInvalidation _ _ Nothing Nothing = return ()
-- Limit was increased to infinity:
getDistanceLimitInvalidation _ _ Nothing (Just _) = Left LimitIncreased
-- Limit was decreased from infinity:
getDistanceLimitInvalidation robotLoc pathCells (Just currLimit) Nothing =
  handleLimitDecreased robotLoc pathCells currLimit
getDistanceLimitInvalidation robotLoc pathCells (Just currLimit) (Just prevLimit)
  | currLimit < prevLimit = handleLimitDecreased robotLoc pathCells currLimit
  | currLimit > prevLimit = Left LimitIncreased
  | otherwise = return () -- Limit unchanged

handleLimitDecreased ::
  Location ->
  NonEmpty Location ->
  Integer ->
  Either DistanceLimitChange ()
handleLimitDecreased robotLoc pathCells currLimit =
  unless (all (withinDistance currLimit robotLoc) $ NE.tail pathCells) $
    Left PathExceededLimit

-- * Utility functions

-- | This function is shared between path computation logic
-- and patch cache invalidation logic to ensure that
-- the choice of inequality operator is consistent (e.g. @<@ vs. @<=@).
withinDistance ::
  -- | distance limit
  Integer ->
  -- | current robot location
  Location ->
  -- | target location
  Location ->
  Bool
withinDistance distLimit robotLoc = (<= distLimit) . fromIntegral . manhattan robotLoc