swarm-0.5.0.0: src/Swarm/Language/Direction.hs
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Types and helper functions for working with directions
module Swarm.Language.Direction (
-- * Directions
Direction (..),
AbsoluteDir (..),
RelativeDir (..),
PlanarRelativeDir (..),
directionSyntax,
isCardinal,
allDirs,
) where
import Data.Aeson.Types hiding (Key)
import Data.Char qualified as C (toLower)
import Data.Data (Data)
import Data.Hashable (Hashable)
import Data.List qualified as L (tail)
import Data.Text hiding (filter, length, map)
import Data.Text qualified as T
import GHC.Generics (Generic)
import Swarm.Util qualified as Util
import Witch.From (from)
------------------------------------------------------------
-- Directions
------------------------------------------------------------
-- | An absolute direction is one which is defined with respect to an
-- external frame of reference; robots need a compass in order to
-- use them.
--
-- NOTE: These values are ordered by increasing angle according to
-- the standard mathematical convention.
-- That is, the right-pointing direction, East, is considered
-- the "reference angle" and the order proceeds counter-clockwise.
-- See https://en.wikipedia.org/wiki/Polar_coordinate_system#Conventions
--
-- Do not alter this ordering, as there exist functions that depend on it
-- (e.g. 'Swarm.Game.Location.nearestDirection' and 'Swarm.Game.Location.relativeTo').
data AbsoluteDir = DEast | DNorth | DWest | DSouth
deriving (Eq, Ord, Show, Read, Generic, Data, Hashable, Enum, Bounded)
directionJsonModifier :: String -> String
directionJsonModifier = map C.toLower . L.tail
directionJsonOptions :: Options
directionJsonOptions =
defaultOptions
{ constructorTagModifier = directionJsonModifier
}
instance FromJSON AbsoluteDir where
parseJSON = genericParseJSON directionJsonOptions
instance ToJSON AbsoluteDir where
toJSON = genericToJSON directionJsonOptions
cardinalDirectionKeyOptions :: JSONKeyOptions
cardinalDirectionKeyOptions =
defaultJSONKeyOptions
{ keyModifier = directionJsonModifier
}
instance ToJSONKey AbsoluteDir where
toJSONKey = genericToJSONKey cardinalDirectionKeyOptions
instance FromJSONKey AbsoluteDir where
fromJSONKey = genericFromJSONKey cardinalDirectionKeyOptions
-- | A relative direction is one which is defined with respect to the
-- robot's frame of reference; no special capability is needed to
-- use them.
data RelativeDir = DPlanar PlanarRelativeDir | DDown
deriving (Eq, Ord, Show, Read, Generic, Data, Hashable, ToJSON, FromJSON)
-- | Caution: Do not alter this ordering, as there exist functions that depend on it
-- (e.g. 'Swarm.Game.Location.nearestDirection' and 'Swarm.Game.Location.relativeTo').
data PlanarRelativeDir = DForward | DLeft | DBack | DRight
deriving (Eq, Ord, Show, Read, Generic, Data, Hashable, Enum, Bounded)
instance FromJSON PlanarRelativeDir where
parseJSON = genericParseJSON directionJsonOptions
instance ToJSON PlanarRelativeDir where
toJSON = genericToJSON directionJsonOptions
-- | The type of directions. Used /e.g./ to indicate which way a robot
-- will turn.
data Direction = DAbsolute AbsoluteDir | DRelative RelativeDir
deriving (Eq, Ord, Show, Read, Generic, Data, Hashable, ToJSON, FromJSON)
-- | Direction name is generated from the deepest nested data constructor
-- e.g. 'DLeft' becomes "left"
directionSyntax :: Direction -> Text
directionSyntax d = toLower . T.tail . from $ case d of
DAbsolute x -> show x
DRelative x -> case x of
DPlanar y -> show y
_ -> show x
-- | Check if the direction is absolute (e.g. 'Swarm.Game.Location.north' or 'Swarm.Game.Location.south').
isCardinal :: Direction -> Bool
isCardinal = \case
DAbsolute _ -> True
_ -> False
allDirs :: [Direction]
allDirs = map DAbsolute Util.listEnums <> map DRelative (DDown : map DPlanar Util.listEnums)