Skip to content

Commit

Permalink
Add subworlds
Browse files Browse the repository at this point in the history
  • Loading branch information
kostmo committed Jul 6, 2023
1 parent efb70df commit cfacc66
Show file tree
Hide file tree
Showing 28 changed files with 689 additions and 254 deletions.
1 change: 1 addition & 0 deletions data/scenarios/Testing/00-ORDER.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@
1295-density-command.yaml
1138-structures
1356-portals
144-subworlds
1 change: 1 addition & 0 deletions data/scenarios/Testing/144-subworlds/00-ORDER.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
basic-subworld.yaml
90 changes: 90 additions & 0 deletions data/scenarios/Testing/144-subworlds/basic-subworld.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
version: 1
name: Subworlds demo
description: |
Surface and underground with portals.
attrs:
- name: portal_in
fg: "#ff9a00"
bg: "#ff5d00"
- name: portal_out
fg: "#00a2ff"
bg: "#0065ff"
entities:
- name: telepad entrance
display:
attr: portal_in
char: "o"
description:
- Portal entrance
properties: [known]
- name: telepad exit
display:
attr: portal_out
char: "o"
description:
- Portal exit
properties: [known]
robots:
- name: base
dir: [1, 0]
devices:
- ADT calculator
- branch predictor
- comparator
- compass
- dictionary
- GPS receiver
- grabber
- lambda
- lodestone
- logger
- strange loop
- treads
known: [flower]
subworlds:
- name: underground
default: [blank]
palette:
'.': [dirt]
'f': [dirt, flower]
'p':
cell: [dirt, telepad exit]
waypoint:
name: portal_out2
'P':
cell: [dirt, telepad entrance]
waypoint:
name: portal_in2
portals:
- entrance: portal_in2
exitInfo:
exit: portal_out1
subworldName: root
upperleft: [-1, 1]
map: |
f..f..f..f
.p......P.
f..f..f..f
world:
default: [blank]
palette:
'.': [grass]
'B': [grass, null, base]
'p':
cell: [grass, telepad exit]
waypoint:
name: portal_out1
'P':
cell: [grass, telepad entrance]
waypoint:
name: portal_in1
upperleft: [-1, 1]
portals:
- entrance: portal_in1
exitInfo:
exit: portal_out2
subworldName: underground
map: |
..........
.p.B....P.
..........
12 changes: 8 additions & 4 deletions src/Swarm/Doc/Gen.hs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import Data.Containers.ListUtils (nubOrd)
import Data.Either.Extra (eitherToMaybe)
import Data.Foldable (find, toList)
import Data.List (transpose)
import Data.List.NonEmpty qualified as NE
import Data.Map.Lazy (Map)
import Data.Map.Lazy qualified as Map
import Data.Maybe (fromMaybe, isJust)
Expand All @@ -51,8 +52,8 @@ import Swarm.Game.Failure qualified as F
import Swarm.Game.Failure.Render qualified as F
import Swarm.Game.Recipe (Recipe, loadRecipes, recipeInputs, recipeOutputs, recipeRequirements, recipeTime, recipeWeight)
import Swarm.Game.ResourceLoading (getDataFileNameSafe)
import Swarm.Game.Robot (equippedDevices, instantiateRobot, robotInventory)
import Swarm.Game.Scenario (Scenario, loadScenario, scenarioRobots)
import Swarm.Game.Robot (Robot, equippedDevices, instantiateRobot, robotInventory)
import Swarm.Game.Scenario (Scenario, loadScenario, scenarioRobots, scenarioWorlds, worldName)
import Swarm.Game.WorldGen (testWorld2Entites)
import Swarm.Language.Capability (Capability)
import Swarm.Language.Capability qualified as Capability
Expand Down Expand Up @@ -550,11 +551,14 @@ classicScenario = do
entities <- loadEntities >>= guardRight "load entities"
fst <$> loadScenario "data/scenarios/classic.yaml" entities

startingHelper :: Scenario -> Robot
startingHelper s = instantiateRobot (worldName $ NE.head $ view scenarioWorlds s) 0 . head . view scenarioRobots $ s

startingDevices :: Scenario -> Set Entity
startingDevices = Set.fromList . map snd . E.elems . view equippedDevices . instantiateRobot 0 . head . view scenarioRobots
startingDevices = Set.fromList . map snd . E.elems . view equippedDevices . startingHelper

startingInventory :: Scenario -> Map Entity Int
startingInventory = Map.fromList . map swap . E.elems . view robotInventory . instantiateRobot 0 . head . view scenarioRobots
startingInventory = Map.fromList . map swap . E.elems . view robotInventory . startingHelper

-- | Ignore utility entities that are just used for tutorials and challenges.
ignoredEntities :: Set Text
Expand Down
6 changes: 5 additions & 1 deletion src/Swarm/Game/Log.hs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import Data.Text (Text)
import GHC.Generics (Generic)
import Swarm.Game.CESK (TickNumber)
import Swarm.Game.Location (Location)
import Swarm.Game.Universe (Cosmo)

-- | Severity of the error - critical errors are bugs
-- and should be reported as Issues.
Expand Down Expand Up @@ -61,8 +62,11 @@ data LogEntry = LogEntry
-- ^ The name of the robot that generated the entry.
, _leRobotID :: Int
-- ^ The ID of the robot that generated the entry.
, _leLocation :: Location
, _leLocation :: Maybe (Cosmo Location)
-- ^ Location of the robot at log entry creation.
-- "Nothing" represents omnipresence for the purpose of proximity.
-- TODO: Define a type isomorphic to Maybe that makes this explict.
-- C.f. "cosmoMeasure" which will have its own type that means the opposite.
, _leText :: Text
-- ^ The text of the log entry.
}
Expand Down
13 changes: 7 additions & 6 deletions src/Swarm/Game/Robot.hs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ import Swarm.Game.Display (Display, curOrientation, defaultRobotDisplay, invisib
import Swarm.Game.Entity hiding (empty)
import Swarm.Game.Location (Heading, Location, toDirection)
import Swarm.Game.Log
import Swarm.Game.Universe
import Swarm.Language.Capability (Capability)
import Swarm.Language.Context qualified as Ctx
import Swarm.Language.Requirement (ReqCtx)
Expand Down Expand Up @@ -167,7 +168,7 @@ data RobotPhase
-- concrete robot we must have a location.
type family RobotLocation (phase :: RobotPhase) :: * where
RobotLocation 'TemplateRobot = Maybe Location
RobotLocation 'ConcreteRobot = Location
RobotLocation 'ConcreteRobot = Cosmo Location

-- | Robot templates have no ID; concrete robots definitely do.
type family RobotID (phase :: RobotPhase) :: * where
Expand Down Expand Up @@ -269,13 +270,13 @@ robotDisplay = lens getDisplay setDisplay
-- a getter, since when changing a robot's location we must remember
-- to update the 'robotsByLocation' map as well. You can use the
-- 'updateRobotLocation' function for this purpose.
robotLocation :: Getter Robot Location
robotLocation :: Getter Robot (Cosmo Location)

-- | Set a robot's location. This is unsafe and should never be
-- called directly except by the 'updateRobotLocation' function.
-- The reason is that we need to make sure the 'robotsByLocation'
-- map stays in sync.
unsafeSetRobotLocation :: Location -> Robot -> Robot
unsafeSetRobotLocation :: Cosmo Location -> Robot -> Robot
unsafeSetRobotLocation loc r = r {_robotLocation = loc}

-- | A template robot's location. Unlike 'robotLocation', this is a
Expand Down Expand Up @@ -308,11 +309,11 @@ robotID :: Getter Robot RID
-- if the robot template didn't have a location already, just set
-- the location to (0,0) by default. If you want a different location,
-- set it via 'trobotLocation' before calling 'instantiateRobot'.
instantiateRobot :: RID -> TRobot -> Robot
instantiateRobot i r =
instantiateRobot :: SubworldName -> RID -> TRobot -> Robot
instantiateRobot swName i r =
r
{ _robotID = i
, _robotLocation = fromMaybe zero (_robotLocation r)
, _robotLocation = Cosmo swName $ fromMaybe zero $ _robotLocation r
}

-- | The ID number of the robot's parent, that is, the robot that
Expand Down
41 changes: 36 additions & 5 deletions src/Swarm/Game/Scenario.hs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ module Swarm.Game.Scenario (
scenarioEntities,
scenarioRecipes,
scenarioKnown,
scenarioWorld,
scenarioWorlds,
scenarioNavigation,
scenarioRobots,
scenarioObjectives,
scenarioSolution,
Expand All @@ -45,18 +46,23 @@ module Swarm.Game.Scenario (
getScenarioPath,
) where

import Control.Arrow ((&&&))
import Control.Lens hiding (from, (.=), (<.>))
import Control.Monad (filterM)
import Control.Monad.Except (ExceptT (..), MonadIO, liftIO, runExceptT, withExceptT)
import Control.Monad.Trans.Except (except)
import Data.Aeson
import Data.Either.Extra (eitherToMaybe, maybeToEither)
import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.List.NonEmpty qualified as NE
import Data.Map qualified as M
import Data.Maybe (catMaybes, isNothing, listToMaybe)
import Data.Text (Text)
import Data.Text qualified as T
import Swarm.Game.Entity
import Swarm.Game.Failure
import Swarm.Game.Failure.Render
import Swarm.Game.Location
import Swarm.Game.Recipe
import Swarm.Game.ResourceLoading (getDataFileNameSafe)
import Swarm.Game.Robot (TRobot)
Expand All @@ -65,7 +71,9 @@ import Swarm.Game.Scenario.Objective.Validation
import Swarm.Game.Scenario.RobotLookup
import Swarm.Game.Scenario.Style
import Swarm.Game.Scenario.Topography.Cell
import Swarm.Game.Scenario.Topography.Navigation.Portal
import Swarm.Game.Scenario.Topography.WorldDescription
import Swarm.Game.Universe
import Swarm.Language.Pipeline (ProcessedTerm)
import Swarm.Util (failT)
import Swarm.Util.Lens (makeLensesNoSigs)
Expand All @@ -91,7 +99,8 @@ data Scenario = Scenario
, _scenarioEntities :: EntityMap
, _scenarioRecipes :: [Recipe Entity]
, _scenarioKnown :: [Text]
, _scenarioWorld :: WorldDescription
, _scenarioWorlds :: NonEmpty WorldDescription
, _scenarioNavigation :: Navigation (M.Map SubworldName) Location
, _scenarioRobots :: [TRobot]
, _scenarioObjectives :: [Objective]
, _scenarioSolution :: Maybe ProcessedTerm
Expand Down Expand Up @@ -122,6 +131,24 @@ instance FromJSONE EntityMap Scenario where
rs <- v ..: "robots"
let rsMap = buildRobotMap rs

rootWorld <- localE (,rsMap) (v ..: "world")
subworlds <- localE (,rsMap) (v ..:? "subworlds" ..!= [])

let allWorlds = rootWorld :| subworlds
mergedWaypoints =
M.fromList $
map (worldName &&& runIdentity . waypoints . navigation) $
NE.toList allWorlds

mergedPortals <-
validatePortals
. Navigation mergedWaypoints
. M.unions
$ map (portals . navigation)
$ NE.toList allWorlds

let mergedNavigation = Navigation mergedWaypoints mergedPortals

Scenario
<$> liftE (v .: "version")
<*> liftE (v .: "name")
Expand All @@ -133,7 +160,8 @@ instance FromJSONE EntityMap Scenario where
<*> pure em
<*> v ..:? "recipes" ..!= []
<*> pure known
<*> localE (,rsMap) (v ..: "world")
<*> pure allWorlds
<*> pure mergedNavigation
<*> pure rs
<*> (liftE (v .:? "objectives" .!= []) >>= validateObjectives)
<*> liftE (v .:? "solution")
Expand Down Expand Up @@ -178,8 +206,11 @@ scenarioRecipes :: Lens' Scenario [Recipe Entity]
-- not have to scan them.
scenarioKnown :: Lens' Scenario [Text]

-- | The starting world for the scenario.
scenarioWorld :: Lens' Scenario WorldDescription
-- | The subworlds of the scenario.
scenarioWorlds :: Lens' Scenario (NonEmpty WorldDescription)

-- | Waypoints and inter-world portals
scenarioNavigation :: Lens' Scenario (Navigation (M.Map SubworldName) Location)

-- | The starting robots for the scenario. Note this should
-- include the base.
Expand Down
Loading

0 comments on commit cfacc66

Please sign in to comment.