packages feed

swarm-0.7.0.0: src/swarm-topography/Swarm/Game/Scenario/Topography/ProtoCell.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

-- |
-- SPDX-License-Identifier: BSD-3-Clause
module Swarm.Game.Scenario.Topography.ProtoCell (
  SignpostableCell (..),
  StructurePalette (..),
  StructureMarker (..),
) where

import Control.Applicative ((<|>))
import Data.Aeson.Key qualified as K
import Data.Aeson.KeyMap qualified as KM
import Data.Map (Map, fromList, toList)
import Data.Text qualified as T
import Data.Tuple (swap)
import Data.Yaml as Y
import GHC.Generics (Generic)
import Swarm.Game.Scenario.Topography.Navigation.Waypoint (WaypointConfig)
import Swarm.Game.Scenario.Topography.Placement
import Swarm.Game.Scenario.Topography.Structure.Named (StructureName)
import Swarm.Util (quote)
import Swarm.Util.Yaml

newtype StructurePalette e = StructurePalette
  {unPalette :: Map Char (SignpostableCell e)}
  deriving (Eq, Show)

instance (FromJSONE e a) => FromJSONE e (StructurePalette a) where
  parseJSONE =
    withObjectE "palette" $ \v -> do
      m <- mapM parseJSONE v
      -- We swap the tuples twice so we can traverse over the second
      -- element of the tuple in between.
      swappedPairs <- mapM (verifyChar . swap) $ toList $ KM.toMap m
      return . StructurePalette . fromList $ map swap swappedPairs
   where
    verifyChar = traverse $ ensureSingleChar . K.toString
    ensureSingleChar [x] = return x
    ensureSingleChar x =
      fail $
        T.unpack $
          T.unwords
            [ "Palette entry is not a single character:"
            , quote $ T.pack x
            ]

data StructureMarker = StructureMarker
  { name :: StructureName
  , orientation :: Maybe Orientation
  }
  deriving (Eq, Show, Generic, FromJSON)

-- | Supplements a cell with waypoint and/or structure placement information
data SignpostableCell c = SignpostableCell
  { waypointCfg :: Maybe WaypointConfig
  , structureMarker :: Maybe StructureMarker
  , standardCell :: c
  }
  deriving (Eq, Show)

instance (FromJSONE e a) => FromJSONE e (SignpostableCell a) where
  parseJSONE x =
    withObjectE "SignpostableCell" objParse x
      <|> (SignpostableCell Nothing Nothing <$> parseJSONE x)
   where
    objParse v = do
      waypointCfg <- liftE $ v .:? "waypoint"
      structureMarker <- liftE $ v .:? "structure"
      standardCell <- v ..: "cell"
      pure $ SignpostableCell {..}