Skip to content

Commit

Permalink
subworlds
Browse files Browse the repository at this point in the history
  • Loading branch information
kostmo committed Jun 30, 2023
1 parent 6a7063b commit 0136896
Show file tree
Hide file tree
Showing 15 changed files with 288 additions and 18 deletions.
88 changes: 88 additions & 0 deletions data/scenarios/Testing/144-subworlds/subworld.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
version: 1
name: Waypoints for nested structures
description: |
Demonstrate behavior of waypoints across structure overlays
robots:
- name: base
loc: [0, 4]
dir: [1, 0]
known: [tree, flower, sand, bit (0), bit (1)]
world:
upperleft: [-4, 7]
default: [blank]
palette:
'.': [grass]
'*': [stone, flower]
'': [stone, upper left corner]
'': [stone, upper right corner]
'': [stone, lower left corner]
'': [stone, lower right corner]
'': [stone, horizontal wall]
'': [stone, vertical wall]
structures:
- name: bitpair
structure:
palette:
'0': [stone, bit (0)]
'1': [stone, bit (1)]
map: |
1
0
waypoints:
- name: bitpair_bottom
loc: [0, -1]
- name: minibox
structure:
palette:
'.': [stone]
's': [stone, sand]
placements:
- src: bitpair
offset: [1, 0]
waypoints:
- name: minibox_corner
loc: [0, 0]
map: |
s.s
s.s
- name: bigbox
structure:
palette:
'.': [stone]
'T': [stone, tree]
waypoints:
- name: bigbox_middle
loc: [2, -3]
map: |
TTTTTT
T.T.T.
.T.T.T
TTTTTT
placements:
- src: bigbox
offset: [1, -1]
- src: bigbox
offset: [7, -5]
- src: minibox
offset: [1, -7]
waypoints:
- name: meadow
loc: [12, -1]
portals:
- entrance: bitpair_bottom
exitInfo:
exit: bigbox_middle
- entrance: minibox_corner
exitInfo:
exit: meadow
map: |
┌────────────┐
│*..*..*..*..│
│.*..*..*..*.│
│..*..*..*..*│
│*..*..*..*..│
│.*..*..*..*.│
│..*..*..*..*│
│*..*..*..*..│
│.*..*..*..*.│
└────────────┘
1 change: 1 addition & 0 deletions editors/emacs/swarm-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"time"
"scout"
"whereami"
"waypoint"
"detect"
"resonate"
"density"
Expand Down
2 changes: 1 addition & 1 deletion editors/vscode/syntaxes/swarm.tmLanguage.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
},
{
"name": "keyword.other",
"match": "\\b(?i)(self|parent|base|if|inl|inr|case|fst|snd|force|undefined|fail|not|format|chars|split|charat|tochar|key|noop|wait|selfdestruct|move|push|stride|turn|grab|harvest|place|give|equip|unequip|make|has|equipped|count|drill|use|build|salvage|reprogram|say|listen|log|view|appear|create|halt|time|scout|whereami|detect|resonate|density|sniff|chirp|watch|surveil|heading|blocked|scan|upload|ishere|isempty|meet|meetall|whoami|setname|random|run|return|try|swap|atomic|instant|installkeyhandler|teleport|as|robotnamed|robotnumbered|knows)\\b"
"match": "\\b(?i)(self|parent|base|if|inl|inr|case|fst|snd|force|undefined|fail|not|format|chars|split|charat|tochar|key|noop|wait|selfdestruct|move|push|stride|turn|grab|harvest|place|give|equip|unequip|make|has|equipped|count|drill|use|build|salvage|reprogram|say|listen|log|view|appear|create|halt|time|scout|whereami|waypoint|detect|resonate|density|sniff|chirp|watch|surveil|heading|blocked|scan|upload|ishere|isempty|meet|meetall|whoami|setname|random|run|return|try|swap|atomic|instant|installkeyhandler|teleport|as|robotnamed|robotnumbered|knows)\\b"
}
]
},
Expand Down
9 changes: 9 additions & 0 deletions src/Swarm/Game/Scenario.hs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ module Swarm.Game.Scenario (
scenarioEntities,
scenarioRecipes,
scenarioKnown,
secenarioSubworlds,
scenarioWorld,
scenarioRobots,
scenarioObjectives,
Expand Down Expand Up @@ -65,6 +66,7 @@ import Swarm.Game.Scenario.Objective
import Swarm.Game.Scenario.Objective.Validation
import Swarm.Game.Scenario.RobotLookup
import Swarm.Game.Scenario.Style
import Swarm.Game.Scenario.Subworld
import Swarm.Game.Scenario.WorldDescription
import Swarm.Language.Pipeline (ProcessedTerm)
import Swarm.Util (failT)
Expand All @@ -91,6 +93,7 @@ data Scenario = Scenario
, _scenarioEntities :: EntityMap
, _scenarioRecipes :: [Recipe Entity]
, _scenarioKnown :: [Text]
, _secenarioSubworlds :: [Subworld]
, _scenarioWorld :: WorldDescription
, _scenarioRobots :: [TRobot]
, _scenarioObjectives :: [Objective]
Expand All @@ -110,6 +113,8 @@ instance FromJSONE EntityMap Scenario where
Left x -> failT [x]
-- extend ambient EntityMap with custom entities

subworlds <- v ..:? "subworlds" ..!= []

withE em $ do
-- parse 'known' entity names and make sure they exist
known <- liftE (v .:? "known" .!= [])
Expand All @@ -133,6 +138,7 @@ instance FromJSONE EntityMap Scenario where
<*> pure em
<*> v ..:? "recipes" ..!= []
<*> pure known
<*> pure subworlds
<*> localE (,rsMap) (v ..: "world")
<*> pure rs
<*> (liftE (v .:? "objectives" .!= []) >>= validateObjectives)
Expand Down Expand Up @@ -178,6 +184,9 @@ scenarioRecipes :: Lens' Scenario [Recipe Entity]
-- not have to scan them.
scenarioKnown :: Lens' Scenario [Text]

-- | The subworlds of the scenario.
secenarioSubworlds :: Lens' Scenario [Subworld]

-- | The starting world for the scenario.
scenarioWorld :: Lens' Scenario WorldDescription

Expand Down
25 changes: 25 additions & 0 deletions src/Swarm/Game/Scenario/Portal.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-- |
-- SPDX-License-Identifier: BSD-3-Clause
module Swarm.Game.Scenario.Portal where

import Swarm.Game.Scenario.Structure
import Data.Aeson
import Data.Text (Text)
import GHC.Generics (Generic)

-- | Note: The primary overworld shall use
-- the reserved name \"root\".
newtype SubworldName = SubworldName Text
deriving (Show, Eq, Ord, Generic, FromJSON)

data PortalExit = PortalExit {
exit :: WaypointName
-- | Note: 'Nothing' indicates that references a waypoint within the same subworld.
, subworldName :: Maybe SubworldName
} deriving (Show, Eq, Generic, FromJSON)

data Portal = Portal
{ entrance :: WaypointName
, exitInfo :: PortalExit
}
deriving (Show, Eq, Generic, FromJSON)
75 changes: 63 additions & 12 deletions src/Swarm/Game/Scenario/Structure.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
-- SPDX-License-Identifier: BSD-3-Clause
module Swarm.Game.Scenario.Structure where

import Linear (V2 (..))
import Data.Int (Int32)
import Control.Applicative ((<|>))
import Control.Arrow ((&&&))
import Data.Aeson.Key qualified as Key
import Data.Aeson.KeyMap qualified as KeyMap
import Data.Coerce
import Data.List (transpose)
import Data.Map qualified as M
import Data.Maybe (mapMaybe)
Expand All @@ -24,6 +27,41 @@ import Swarm.Language.Syntax (AbsoluteDir (..))
import Swarm.Util.Yaml
import Witch (into)

newtype WaypointName = WaypointName Text
deriving (Show, Eq, Ord, Generic, FromJSON)

-- | Indicates which structure something came from
-- for debugging purposes.
data Originated a = Originated
{ parent :: Maybe Placement
, value :: a
}
deriving (Show, Eq, Functor)

-- |
-- A parent world shouldn't have to know the exact layout of a subworld
-- to specify where exactly a portal will deliver a robot to within the subworld.
-- Therefore, we define named waypoints in the subworld and the parent world
-- must reference them by name, rather than by coordinate.
data Waypoint = Waypoint
{ wpName :: WaypointName
-- | Enforce global uniqueness of this waypoint
, wpUnique :: Bool
, wpLoc :: Location
}
deriving (Show, Eq)

instance FromJSON Waypoint where
parseJSON = withObject "Waypoint" $ \v ->
Waypoint
<$> v .: "name"
<*> v .:? "unique" .!= False
<*> v .: "loc"

offsetWaypoint :: V2 Int32
-> Waypoint -> Waypoint
offsetWaypoint locOffset (Waypoint n u originalLoc) = Waypoint n u $ originalLoc .+^ locOffset

newtype StructureName = StructureName Text
deriving (Eq, Ord, Show, Generic, FromJSON)

Expand All @@ -46,10 +84,11 @@ data PStructure c = Structure
-- ^ structure definitions from parents shall be accessible by children
, placements :: [Placement]
-- ^ earlier placements will be overlaid on top of later placements in the YAML file
, waypoints :: [Waypoint]
}
deriving (Eq, Show)

newtype MergedStructure c = MergedStructure [[c]]
data MergedStructure c = MergedStructure [[c]] [Originated Waypoint]

data Orientation = Orientation
{ up :: AbsoluteDir
Expand Down Expand Up @@ -77,12 +116,17 @@ overlaySingleStructure ::
MergedStructure (Maybe a)
overlaySingleStructure
inheritedStrucDefs
(Placement _ (Location colOffset rowOffset) orientation, struc)
(MergedStructure inputArea) =
MergedStructure $ zipWithPad mergeSingleRow inputArea paddedOverlayRows
(p@(Placement _ loc@(Location colOffset rowOffset) orientation), struc)
(MergedStructure inputArea inputWaypoints) =
MergedStructure mergedArea mergedWaypoints
where
mergedArea = zipWithPad mergeSingleRow inputArea paddedOverlayRows

mergedWaypoints = inputWaypoints ++ map (fmap $ offsetWaypoint $ coerce loc) overlayWaypoints

zipWithPad f a b = zipWith f a $ b <> repeat Nothing
MergedStructure overlayArea = mergeStructures inheritedStrucDefs struc

MergedStructure overlayArea overlayWaypoints = mergeStructures inheritedStrucDefs (Just p) struc
affineTransformedOverlay = getTransform orientation overlayArea

mergeSingleRow inputRow maybeOverlayRow =
Expand All @@ -99,12 +143,18 @@ overlaySingleStructure
then (replicate integralOffset Nothing <>)
else drop $ abs integralOffset

-- | Overlays all of the "child placements", such that the
-- earlier children supersede the later ones (due to use of "foldr" instead of "foldl").
mergeStructures :: M.Map StructureName (PStructure (Maybe a)) -> PStructure (Maybe a) -> MergedStructure (Maybe a)
mergeStructures inheritedStrucDefs (Structure origArea subStructures subPlacements) =
foldr (overlaySingleStructure structureMap) (MergedStructure origArea) overlays
-- | Overlays all of the "child placements", such that the children encountered earlier
-- in the YAML file supersede the later ones (due to use of "foldr" instead of "foldl").
mergeStructures ::
M.Map StructureName (PStructure (Maybe a)) ->
Maybe Placement ->
PStructure (Maybe a) ->
MergedStructure (Maybe a)
mergeStructures inheritedStrucDefs parentPlacement (Structure origArea subStructures subPlacements subWaypoints) =
foldr (overlaySingleStructure structureMap) (MergedStructure origArea originatedWaypoints) overlays
where
originatedWaypoints = map (Originated parentPlacement) subWaypoints

-- deeper definitions override the outer (toplevel) ones
structureMap = M.union (M.fromList $ map (name &&& structure) subStructures) inheritedStrucDefs
overlays = mapMaybe g subPlacements
Expand All @@ -116,9 +166,10 @@ instance FromJSONE (EntityMap, RobotMap) (PStructure (Maybe (PCell Entity))) whe
pal <- v ..:? "palette" ..!= WorldPalette mempty
structureDefs <- v ..:? "structures" ..!= []
placementDefs <- liftE $ v .:? "placements" .!= []
waypointDefs <- liftE $ v .:? "waypoints" .!= []
maybeMaskChar <- liftE $ v .:? "mask"
maskedArea <- liftE $ (v .:? "map" .!= "") >>= paintMap maybeMaskChar pal
return $ Structure maskedArea structureDefs placementDefs
return $ Structure maskedArea structureDefs placementDefs waypointDefs

-- | affine transformation
getTransform :: Orientation -> ([[a]] -> [[a]])
Expand All @@ -138,7 +189,7 @@ data Placement = Placement
, offset :: Location
, orient :: Orientation
}
deriving (Eq, Show)
deriving (Show, Eq)

instance FromJSON Placement where
parseJSON = withObject "structure placement" $ \v -> do
Expand Down
27 changes: 27 additions & 0 deletions src/Swarm/Game/Scenario/Subworld.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{-# LANGUAGE OverloadedStrings #-}

-- |
-- SPDX-License-Identifier: BSD-3-Clause
module Swarm.Game.Scenario.Subworld where

import Data.Aeson
import Swarm.Game.Entity
import Swarm.Game.Scenario.RobotLookup
import Swarm.Game.Scenario.WorldDescription
import Swarm.Util.Yaml
import Swarm.Game.Scenario.Portal

data Subworld = Subworld
{ name :: SubworldName
, portals :: [Portal]
, world :: WorldDescription
}
deriving (Eq, Show)

instance FromJSONE EntityMap Subworld where
parseJSONE = withObjectE "subworld" $ \v -> do
n <- liftE (v .: "name")
c <- liftE (v .: "connectivity")
let rsMap = buildRobotMap []
w <- localE (,rsMap) (v ..: "world")
return $ Subworld n c w
Loading

0 comments on commit 0136896

Please sign in to comment.