split scenario construction into separate sublibrary (#1719)

Towards #1715 and #1043

Creates a new `swarm-scenario` sublibrary intended for scenario data that is independent of game state.

# Planned follow-ups

This PR is already pretty large, but there is still more that can be done regarding sublibrary reorganization/splitting.

* May want to pare-down a sublibrary exclusively for world-generation without all the other baggage of scenarios.
* `Swarm.Game.ScenarioInfo`, `Swarm.Game.Tick`, and `Swarm.Game.Scenario.Status` could probably be moved from `swarm-scenario` to `swarm-engine`.
This commit is contained in:
Karl Ostmo 2024-01-24 13:11:14 -08:00 committed by GitHub
parent cf5f064828
commit 05613dfba4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
76 changed files with 380 additions and 261 deletions

View File

@ -4,76 +4,88 @@
<!-- Generated by graphviz version 2.43.0 (0)
-->
<!-- Title: plan Pages: 1 -->
<svg width="254pt" height="264pt"
viewBox="0.00 0.00 254.00 264.02" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 260.02)">
<svg width="266pt" height="308pt"
viewBox="0.00 0.00 266.00 308.03" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 304.03)">
<title>plan</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-260.02 250,-260.02 250,4 -4,4"/>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-304.03 262,-304.03 262,4 -4,4"/>
<!-- swarm&#45;0.5.0.0:exe:swarm -->
<g id="node1" class="node">
<title>swarm&#45;0.5.0.0:exe:swarm</title>
<path fill="none" stroke="brown" stroke-width="2" d="M211,-36C211,-36 35,-36 35,-36 29,-36 23,-30 23,-24 23,-24 23,-12 23,-12 23,-6 29,0 35,0 35,0 211,0 211,0 217,0 223,-6 223,-12 223,-12 223,-24 223,-24 223,-30 217,-36 211,-36"/>
<text text-anchor="middle" x="123" y="-14.3" font-family="Times,serif" font-size="14.00">swarm&#45;0.5.0.0:exe:swarm</text>
<path fill="none" stroke="brown" stroke-width="2" d="M217,-36C217,-36 41,-36 41,-36 35,-36 29,-30 29,-24 29,-24 29,-12 29,-12 29,-6 35,0 41,0 41,0 217,0 217,0 223,0 229,-6 229,-12 229,-12 229,-24 229,-24 229,-30 223,-36 217,-36"/>
<text text-anchor="middle" x="129" y="-14.3" font-family="Times,serif" font-size="14.00">swarm&#45;0.5.0.0:exe:swarm</text>
</g>
<!-- swarm&#45;0.5.0.0:lib:swarm&#45;web -->
<g id="node2" class="node">
<title>swarm&#45;0.5.0.0:lib:swarm&#45;web</title>
<path fill="none" stroke="gray" stroke-width="2" d="M224.5,-80C224.5,-80 21.5,-80 21.5,-80 15.5,-80 9.5,-74 9.5,-68 9.5,-68 9.5,-56 9.5,-56 9.5,-50 15.5,-44 21.5,-44 21.5,-44 224.5,-44 224.5,-44 230.5,-44 236.5,-50 236.5,-56 236.5,-56 236.5,-68 236.5,-68 236.5,-74 230.5,-80 224.5,-80"/>
<text text-anchor="middle" x="123" y="-58.3" font-family="Times,serif" font-size="14.00">swarm&#45;0.5.0.0:lib:swarm&#45;web</text>
<path fill="none" stroke="gray" stroke-width="2" d="M230.5,-80C230.5,-80 27.5,-80 27.5,-80 21.5,-80 15.5,-74 15.5,-68 15.5,-68 15.5,-56 15.5,-56 15.5,-50 21.5,-44 27.5,-44 27.5,-44 230.5,-44 230.5,-44 236.5,-44 242.5,-50 242.5,-56 242.5,-56 242.5,-68 242.5,-68 242.5,-74 236.5,-80 230.5,-80"/>
<text text-anchor="middle" x="129" y="-58.3" font-family="Times,serif" font-size="14.00">swarm&#45;0.5.0.0:lib:swarm&#45;web</text>
</g>
<!-- swarm&#45;0.5.0.0:exe:swarm&#45;&gt;swarm&#45;0.5.0.0:lib:swarm&#45;web -->
<g id="edge1" class="edge">
<title>swarm&#45;0.5.0.0:exe:swarm&#45;&gt;swarm&#45;0.5.0.0:lib:swarm&#45;web</title>
<path fill="none" stroke="brown" d="M123,-36.15C123,-36.31 123,-36.47 123,-36.63"/>
<polygon fill="brown" stroke="brown" points="119.5,-33.89 123,-43.89 126.5,-33.89 119.5,-33.89"/>
<path fill="none" stroke="brown" d="M129,-36.15C129,-36.31 129,-36.47 129,-36.63"/>
<polygon fill="brown" stroke="brown" points="125.5,-33.89 129,-43.89 132.5,-33.89 125.5,-33.89"/>
</g>
<!-- swarm&#45;0.5.0.0 -->
<g id="node3" class="node">
<title>swarm&#45;0.5.0.0</title>
<path fill="none" stroke="blue" stroke-width="2" d="M170.5,-124.01C170.5,-124.01 75.5,-124.01 75.5,-124.01 69.5,-124.01 63.5,-118.01 63.5,-112.01 63.5,-112.01 63.5,-100.01 63.5,-100.01 63.5,-94.01 69.5,-88.01 75.5,-88.01 75.5,-88.01 170.5,-88.01 170.5,-88.01 176.5,-88.01 182.5,-94.01 182.5,-100.01 182.5,-100.01 182.5,-112.01 182.5,-112.01 182.5,-118.01 176.5,-124.01 170.5,-124.01"/>
<text text-anchor="middle" x="123" y="-102.31" font-family="Times,serif" font-size="14.00">swarm&#45;0.5.0.0</text>
<path fill="none" stroke="blue" stroke-width="2" d="M176.5,-124.01C176.5,-124.01 81.5,-124.01 81.5,-124.01 75.5,-124.01 69.5,-118.01 69.5,-112.01 69.5,-112.01 69.5,-100.01 69.5,-100.01 69.5,-94.01 75.5,-88.01 81.5,-88.01 81.5,-88.01 176.5,-88.01 176.5,-88.01 182.5,-88.01 188.5,-94.01 188.5,-100.01 188.5,-100.01 188.5,-112.01 188.5,-112.01 188.5,-118.01 182.5,-124.01 176.5,-124.01"/>
<text text-anchor="middle" x="129" y="-102.31" font-family="Times,serif" font-size="14.00">swarm&#45;0.5.0.0</text>
</g>
<!-- swarm&#45;0.5.0.0:lib:swarm&#45;web&#45;&gt;swarm&#45;0.5.0.0 -->
<g id="edge2" class="edge">
<title>swarm&#45;0.5.0.0:lib:swarm&#45;web&#45;&gt;swarm&#45;0.5.0.0</title>
<path fill="none" stroke="gray" d="M123,-80.16C123,-80.32 123,-80.48 123,-80.64"/>
<polygon fill="gray" stroke="gray" points="119.5,-77.9 123,-87.9 126.5,-77.9 119.5,-77.9"/>
<path fill="none" stroke="gray" d="M129,-80.16C129,-80.32 129,-80.48 129,-80.64"/>
<polygon fill="gray" stroke="gray" points="125.5,-77.9 129,-87.9 132.5,-77.9 125.5,-77.9"/>
</g>
<!-- swarm&#45;0.5.0.0:lib:swarm&#45;engine -->
<g id="node4" class="node">
<title>swarm&#45;0.5.0.0:lib:swarm&#45;engine</title>
<path fill="none" stroke="gray" stroke-width="2" d="M234,-168.01C234,-168.01 12,-168.01 12,-168.01 6,-168.01 0,-162.01 0,-156.01 0,-156.01 0,-144.01 0,-144.01 0,-138.01 6,-132.01 12,-132.01 12,-132.01 234,-132.01 234,-132.01 240,-132.01 246,-138.01 246,-144.01 246,-144.01 246,-156.01 246,-156.01 246,-162.01 240,-168.01 234,-168.01"/>
<text text-anchor="middle" x="123" y="-146.31" font-family="Times,serif" font-size="14.00">swarm&#45;0.5.0.0:lib:swarm&#45;engine</text>
<path fill="none" stroke="gray" stroke-width="2" d="M240,-168.01C240,-168.01 18,-168.01 18,-168.01 12,-168.01 6,-162.01 6,-156.01 6,-156.01 6,-144.01 6,-144.01 6,-138.01 12,-132.01 18,-132.01 18,-132.01 240,-132.01 240,-132.01 246,-132.01 252,-138.01 252,-144.01 252,-144.01 252,-156.01 252,-156.01 252,-162.01 246,-168.01 240,-168.01"/>
<text text-anchor="middle" x="129" y="-146.31" font-family="Times,serif" font-size="14.00">swarm&#45;0.5.0.0:lib:swarm&#45;engine</text>
</g>
<!-- swarm&#45;0.5.0.0&#45;&gt;swarm&#45;0.5.0.0:lib:swarm&#45;engine -->
<g id="edge3" class="edge">
<title>swarm&#45;0.5.0.0&#45;&gt;swarm&#45;0.5.0.0:lib:swarm&#45;engine</title>
<path fill="none" stroke="blue" d="M123,-124.16C123,-124.32 123,-124.48 123,-124.64"/>
<polygon fill="blue" stroke="blue" points="119.5,-121.9 123,-131.9 126.5,-121.9 119.5,-121.9"/>
<path fill="none" stroke="blue" d="M129,-124.16C129,-124.32 129,-124.48 129,-124.64"/>
<polygon fill="blue" stroke="blue" points="125.5,-121.9 129,-131.9 132.5,-121.9 125.5,-121.9"/>
</g>
<!-- swarm&#45;0.5.0.0:lib:swarm&#45;scenario -->
<g id="node5" class="node">
<title>swarm&#45;0.5.0.0:lib:swarm&#45;scenario</title>
<path fill="none" stroke="gray" stroke-width="2" d="M246,-212.02C246,-212.02 12,-212.02 12,-212.02 6,-212.02 0,-206.02 0,-200.02 0,-200.02 0,-188.02 0,-188.02 0,-182.02 6,-176.02 12,-176.02 12,-176.02 246,-176.02 246,-176.02 252,-176.02 258,-182.02 258,-188.02 258,-188.02 258,-200.02 258,-200.02 258,-206.02 252,-212.02 246,-212.02"/>
<text text-anchor="middle" x="129" y="-190.32" font-family="Times,serif" font-size="14.00">swarm&#45;0.5.0.0:lib:swarm&#45;scenario</text>
</g>
<!-- swarm&#45;0.5.0.0:lib:swarm&#45;engine&#45;&gt;swarm&#45;0.5.0.0:lib:swarm&#45;scenario -->
<g id="edge4" class="edge">
<title>swarm&#45;0.5.0.0:lib:swarm&#45;engine&#45;&gt;swarm&#45;0.5.0.0:lib:swarm&#45;scenario</title>
<path fill="none" stroke="gray" d="M129,-168.17C129,-168.33 129,-168.49 129,-168.65"/>
<polygon fill="gray" stroke="gray" points="125.5,-165.91 129,-175.91 132.5,-165.91 125.5,-165.91"/>
</g>
<!-- swarm&#45;0.5.0.0:lib:swarm&#45;lang -->
<g id="node5" class="node">
<g id="node6" class="node">
<title>swarm&#45;0.5.0.0:lib:swarm&#45;lang</title>
<path fill="none" stroke="gray" stroke-width="2" d="M225.5,-212.02C225.5,-212.02 20.5,-212.02 20.5,-212.02 14.5,-212.02 8.5,-206.02 8.5,-200.02 8.5,-200.02 8.5,-188.02 8.5,-188.02 8.5,-182.02 14.5,-176.02 20.5,-176.02 20.5,-176.02 225.5,-176.02 225.5,-176.02 231.5,-176.02 237.5,-182.02 237.5,-188.02 237.5,-188.02 237.5,-200.02 237.5,-200.02 237.5,-206.02 231.5,-212.02 225.5,-212.02"/>
<text text-anchor="middle" x="123" y="-190.32" font-family="Times,serif" font-size="14.00">swarm&#45;0.5.0.0:lib:swarm&#45;lang</text>
<path fill="none" stroke="gray" stroke-width="2" d="M231.5,-256.02C231.5,-256.02 26.5,-256.02 26.5,-256.02 20.5,-256.02 14.5,-250.02 14.5,-244.02 14.5,-244.02 14.5,-232.02 14.5,-232.02 14.5,-226.02 20.5,-220.02 26.5,-220.02 26.5,-220.02 231.5,-220.02 231.5,-220.02 237.5,-220.02 243.5,-226.02 243.5,-232.02 243.5,-232.02 243.5,-244.02 243.5,-244.02 243.5,-250.02 237.5,-256.02 231.5,-256.02"/>
<text text-anchor="middle" x="129" y="-234.32" font-family="Times,serif" font-size="14.00">swarm&#45;0.5.0.0:lib:swarm&#45;lang</text>
</g>
<!-- swarm&#45;0.5.0.0:lib:swarm&#45;engine&#45;&gt;swarm&#45;0.5.0.0:lib:swarm&#45;lang -->
<g id="edge4" class="edge">
<title>swarm&#45;0.5.0.0:lib:swarm&#45;engine&#45;&gt;swarm&#45;0.5.0.0:lib:swarm&#45;lang</title>
<path fill="none" stroke="gray" d="M123,-168.17C123,-168.33 123,-168.49 123,-168.65"/>
<polygon fill="gray" stroke="gray" points="119.5,-165.91 123,-175.91 126.5,-165.91 119.5,-165.91"/>
<!-- swarm&#45;0.5.0.0:lib:swarm&#45;scenario&#45;&gt;swarm&#45;0.5.0.0:lib:swarm&#45;lang -->
<g id="edge5" class="edge">
<title>swarm&#45;0.5.0.0:lib:swarm&#45;scenario&#45;&gt;swarm&#45;0.5.0.0:lib:swarm&#45;lang</title>
<path fill="none" stroke="gray" d="M129,-212.17C129,-212.33 129,-212.49 129,-212.65"/>
<polygon fill="gray" stroke="gray" points="125.5,-209.91 129,-219.91 132.5,-209.91 125.5,-209.91"/>
</g>
<!-- swarm&#45;0.5.0.0:lib:swarm&#45;util -->
<g id="node6" class="node">
<g id="node7" class="node">
<title>swarm&#45;0.5.0.0:lib:swarm&#45;util</title>
<path fill="none" stroke="gray" stroke-width="2" d="M222,-256.02C222,-256.02 24,-256.02 24,-256.02 18,-256.02 12,-250.02 12,-244.02 12,-244.02 12,-232.02 12,-232.02 12,-226.02 18,-220.02 24,-220.02 24,-220.02 222,-220.02 222,-220.02 228,-220.02 234,-226.02 234,-232.02 234,-232.02 234,-244.02 234,-244.02 234,-250.02 228,-256.02 222,-256.02"/>
<text text-anchor="middle" x="123" y="-234.32" font-family="Times,serif" font-size="14.00">swarm&#45;0.5.0.0:lib:swarm&#45;util</text>
<path fill="none" stroke="gray" stroke-width="2" d="M228,-300.03C228,-300.03 30,-300.03 30,-300.03 24,-300.03 18,-294.03 18,-288.03 18,-288.03 18,-276.03 18,-276.03 18,-270.03 24,-264.03 30,-264.03 30,-264.03 228,-264.03 228,-264.03 234,-264.03 240,-270.03 240,-276.03 240,-276.03 240,-288.03 240,-288.03 240,-294.03 234,-300.03 228,-300.03"/>
<text text-anchor="middle" x="129" y="-278.33" font-family="Times,serif" font-size="14.00">swarm&#45;0.5.0.0:lib:swarm&#45;util</text>
</g>
<!-- swarm&#45;0.5.0.0:lib:swarm&#45;lang&#45;&gt;swarm&#45;0.5.0.0:lib:swarm&#45;util -->
<g id="edge5" class="edge">
<g id="edge6" class="edge">
<title>swarm&#45;0.5.0.0:lib:swarm&#45;lang&#45;&gt;swarm&#45;0.5.0.0:lib:swarm&#45;util</title>
<path fill="none" stroke="gray" d="M123,-212.17C123,-212.33 123,-212.49 123,-212.65"/>
<polygon fill="gray" stroke="gray" points="119.5,-209.91 123,-219.91 126.5,-209.91 119.5,-209.91"/>
<path fill="none" stroke="gray" d="M129,-256.17C129,-256.33 129,-256.49 129,-256.65"/>
<polygon fill="gray" stroke="gray" points="125.5,-253.92 129,-263.92 132.5,-253.92 125.5,-253.92"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -13,7 +13,8 @@ import Data.Maybe (listToMaybe)
import Data.Text (Text)
import Data.Text qualified as T
import Swarm.Game.Failure (SystemFailure (CustomFailure))
import Swarm.Game.Robot (Robot, instantiateRobot)
import Swarm.Game.Robot (Robot)
import Swarm.Game.Robot.Concrete (instantiateRobot)
import Swarm.Game.Scenario (Scenario, scenarioRobots)
import Swarm.Language.Syntax (Const (..))
import Swarm.Language.Syntax qualified as Syntax

View File

@ -78,6 +78,8 @@ import Swarm.Game.Entity hiding (empty)
import Swarm.Game.Location
import Swarm.Game.ResourceLoading (getSwarmHistoryPath)
import Swarm.Game.Robot
import Swarm.Game.Robot.Concrete
import Swarm.Game.Robot.Context
import Swarm.Game.Scenario.Topography.Structure.Recognition (automatons)
import Swarm.Game.Scenario.Topography.Structure.Recognition.Type (originalStructureDefinitions)
import Swarm.Game.ScenarioInfo

View File

@ -136,16 +136,18 @@ import Data.Vector qualified as V
import GitHash (GitInfo)
import Graphics.Vty (ColorMode (..))
import Network.Wai.Handler.Warp (Port)
import Swarm.Game.CESK (TickNumber (..))
import Swarm.Game.Entity as E
import Swarm.Game.Failure
import Swarm.Game.Recipe (Recipe, loadRecipes)
import Swarm.Game.ResourceLoading (NameGenerator, initNameGenerator, readAppData)
import Swarm.Game.Robot
import Swarm.Game.Robot.Concrete
import Swarm.Game.Robot.Context
import Swarm.Game.Scenario.Status
import Swarm.Game.ScenarioInfo (ScenarioCollection, loadScenarios, _SISingle)
import Swarm.Game.State
import Swarm.Game.State.Substate
import Swarm.Game.Tick (TickNumber (..))
import Swarm.Game.World.Load (loadWorlds)
import Swarm.Game.World.Typecheck (WorldMap)
import Swarm.Log

View File

@ -72,12 +72,13 @@ import Linear
import Network.Wai.Handler.Warp (Port)
import Numeric (showFFloat)
import Swarm.Constant
import Swarm.Game.CESK (CESK (..), TickNumber (..), addTicks)
import Swarm.Game.CESK (CESK (..))
import Swarm.Game.Display
import Swarm.Game.Entity as E
import Swarm.Game.Location
import Swarm.Game.Recipe
import Swarm.Game.Robot
import Swarm.Game.Robot.Concrete
import Swarm.Game.Scenario (
scenarioAuthor,
scenarioCreative,
@ -102,6 +103,7 @@ import Swarm.Game.ScenarioInfo (
import Swarm.Game.State
import Swarm.Game.State.Robot
import Swarm.Game.State.Substate
import Swarm.Game.Tick (TickNumber (..), addTicks)
import Swarm.Game.Universe
import Swarm.Game.World qualified as W
import Swarm.Language.Capability (Capability (..), constCaps)

View File

@ -20,7 +20,6 @@ import Data.Tagged (unTagged)
import Data.Word (Word32)
import Graphics.Vty qualified as V
import Linear.Affine ((.-.))
import Swarm.Game.CESK (TickNumber (..))
import Swarm.Game.Display (
Attribute (AEntity),
Display,
@ -39,6 +38,7 @@ import Swarm.Game.State
import Swarm.Game.State.Robot
import Swarm.Game.State.Substate
import Swarm.Game.Terrain
import Swarm.Game.Tick (TickNumber (..))
import Swarm.Game.Universe
import Swarm.Game.World qualified as W
import Swarm.TUI.Editor.Masking

View File

@ -77,20 +77,18 @@ module Swarm.Game.CESK (
-- ** Extracting information
finalValue,
TickNumber (..),
addTicks,
) where
import Control.Lens ((^.))
import Control.Lens.Combinators (pattern Empty)
import Data.Aeson (FromJSON, ToJSON)
import Data.Int (Int64)
import Data.IntMap.Strict (IntMap)
import Data.IntMap.Strict qualified as IM
import GHC.Generics (Generic)
import Prettyprinter (Doc, Pretty (..), encloseSep, hsep, (<+>))
import Swarm.Game.Entity (Count, Entity)
import Swarm.Game.Exception
import Swarm.Game.Tick
import Swarm.Game.World (WorldUpdate (..))
import Swarm.Language.Context
import Swarm.Language.Module
@ -100,22 +98,6 @@ import Swarm.Language.Requirement (ReqCtx)
import Swarm.Language.Syntax
import Swarm.Language.Types
import Swarm.Language.Value as V
import Swarm.Util.WindowedCounter (Offsettable (..))
-- | A newtype representing a count of ticks (typically since the
-- start of a game).
newtype TickNumber = TickNumber {getTickNumber :: Int64}
deriving (Eq, Ord, Show, Read, Generic, FromJSON, ToJSON)
-- | Add an offset to a 'TickNumber'.
addTicks :: Int -> TickNumber -> TickNumber
addTicks i (TickNumber n) = TickNumber $ n + fromIntegral i
instance Offsettable TickNumber where
offsetBy = addTicks
instance Pretty TickNumber where
pretty (TickNumber i) = pretty i
------------------------------------------------------------
-- Frames and continuations

View File

@ -0,0 +1,156 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Support for instantiated robots.
module Swarm.Game.Robot.Concrete (
-- * Lenses
robotContext,
machine,
-- * Query
waitingUntil,
getResult,
isActive,
wantsToStep,
-- * Utilities
instantiateRobot,
) where
import Control.Lens hiding (Const, contains)
import Data.Aeson qualified as Ae (Key, KeyValue, ToJSON (..), object, (.=))
import Data.Maybe (catMaybes, fromMaybe, isNothing)
import Linear
import Servant.Docs (ToSample)
import Servant.Docs qualified as SD
import Swarm.Game.CESK qualified as C
import Swarm.Game.Display (defaultRobotDisplay, invisible)
import Swarm.Game.Entity hiding (empty)
import Swarm.Game.Robot
import Swarm.Game.Robot.Context
import Swarm.Game.Tick
import Swarm.Game.Universe
import Swarm.Language.Pipeline (ProcessedTerm)
import Swarm.Language.Pipeline.QQ (tmQ)
import Swarm.Language.Value as V
type instance RobotContextMember 'ConcreteRobot = RobotContext
type instance RobotMachine 'ConcreteRobot = C.CESK
robotContext :: Lens' Robot RobotContext
robotContext = lens _robotContext (\r x -> r {_robotContext = x})
machine :: Lens' Robot C.CESK
machine = lens _machine (\r x -> r {_machine = x})
instance ToSample Robot where
toSamples _ = SD.singleSample sampleBase
where
sampleBase :: Robot
sampleBase =
mkRobot
0
emptyRobotContext
Nothing
"base"
"The starting robot."
defaultCosmicLocation
zero
defaultRobotDisplay
(C.initMachine [tmQ| move |] mempty C.emptyStore)
[]
[]
False
False
mempty
0
mkMachine :: Maybe ProcessedTerm -> C.CESK
mkMachine Nothing = C.Out VUnit C.emptyStore []
mkMachine (Just pt) = C.initMachine pt mempty C.emptyStore
-- | Instantiate a robot template to make it into a concrete robot, by
-- providing a robot ID. Concrete robots also require a location;
-- 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'.
--
-- If a machine is not supplied (i.e. 'Nothing'), will fallback to any
-- program specified in the template robot.
instantiateRobot :: Maybe C.CESK -> RID -> TRobot -> Robot
instantiateRobot maybeMachine i r =
r
{ _robotID = i
, _robotLocation = fromMaybe defaultCosmicLocation $ _robotLocation r
, _machine = fromMaybe (mkMachine $ _machine r) maybeMachine
, _robotContext = emptyRobotContext
}
(.=?) :: (Ae.KeyValue a, Ae.ToJSON v, Eq v) => Ae.Key -> v -> v -> Maybe a
(.=?) n v defaultVal = if defaultVal /= v then Just $ n Ae..= v else Nothing
(.==) :: (Ae.KeyValue a, Ae.ToJSON v) => Ae.Key -> v -> Maybe a
(.==) n v = Just $ n Ae..= v
instance Ae.ToJSON Robot where
toJSON r =
Ae.object $
catMaybes
[ "id" .== (r ^. robotID)
, "name" .== (r ^. robotEntity . entityDisplay)
, "description" .=? (r ^. robotEntity . entityDescription) $ mempty
, "loc" .== (r ^. robotLocation)
, "dir" .=? (r ^. robotEntity . entityOrientation) $ zero
, "display" .=? (r ^. robotDisplay) $ (defaultRobotDisplay & invisible .~ sys)
, "program" .== (r ^. machine)
, "devices" .=? (map (^. _2 . entityName) . elems $ r ^. equippedDevices) $ []
, "inventory" .=? (map (_2 %~ view entityName) . elems $ r ^. robotInventory) $ []
, "system" .=? sys $ False
, "heavy" .=? (r ^. robotHeavy) $ False
, "log" .=? (r ^. robotLog) $ mempty
, -- debug
"capabilities" .=? (r ^. robotCapabilities) $ mempty
, "logUpdated" .=? (r ^. robotLogUpdated) $ False
, "context" .=? (r ^. robotContext) $ emptyRobotContext
, "parent" .=? (r ^. robotParentID) $ Nothing
, "createdAt" .=? (r ^. robotCreatedAt) $ 0
, "selfDestruct" .=? (r ^. selfDestruct) $ False
, "activity" .=? (r ^. activityCounts) $ emptyActivityCount
, "runningAtomic" .=? (r ^. runningAtomic) $ False
]
where
sys = r ^. systemRobot
-- | The time until which the robot is waiting, if any.
waitingUntil :: Robot -> Maybe TickNumber
waitingUntil robot =
case _machine robot of
C.Waiting time _ -> Just time
_ -> Nothing
-- | Get the result of the robot's computation if it is finished.
getResult :: Robot -> Maybe (Value, C.Store)
{-# INLINE getResult #-}
getResult = C.finalValue . view machine
-- | Is the robot actively in the middle of a computation?
isActive :: Robot -> Bool
{-# INLINE isActive #-}
isActive = isNothing . getResult
-- | "Active" robots include robots that are waiting; 'wantsToStep' is
-- true if the robot actually wants to take another step right now
-- (this is a /subset/ of active robots).
wantsToStep :: TickNumber -> Robot -> Bool
wantsToStep now robot
| not (isActive robot) = False
| otherwise = maybe True (now >=) (waitingUntil robot)

View File

@ -17,6 +17,7 @@ import GHC.Generics (Generic)
import Servant.Docs (ToSample)
import Servant.Docs qualified as SD
import Swarm.Game.Scenario.Objective
import Swarm.Game.Scenario.Objective.Graph (getDistinctConstants)
import Swarm.Game.Scenario.Objective.Logic as L
-- | We have "won" if all of the "unwinnable" or remaining "incomplete" objectives are "optional".
@ -65,9 +66,6 @@ getActiveObjectives :: ObjectiveCompletion -> [Objective]
getActiveObjectives =
fst . partitionActiveObjectives
deriving instance Generic (BE.Signed ObjectiveLabel)
deriving instance ToJSON (BE.Signed ObjectiveLabel)
-- | For debugging only (via Web API)
data PrereqSatisfaction = PrereqSatisfaction
{ objective :: Objective
@ -91,6 +89,3 @@ getSatisfaction oc =
y
(maybe mempty (getDistinctConstants . logic) $ _objectivePrerequisite y)
(isPrereqsSatisfied oc y)
getDistinctConstants :: (Ord a) => Prerequisite a -> Set (BE.Signed a)
getDistinctConstants = Set.fromList . BE.constants . toBoolExpr

View File

@ -101,7 +101,7 @@ import Data.Text.IO qualified as TIO
import Data.Text.Lazy qualified as TL
import Data.Text.Lazy.Encoding qualified as TL
import Linear (V2 (..))
import Swarm.Game.CESK (addTicks, emptyStore, finalValue, initMachine)
import Swarm.Game.CESK (emptyStore, finalValue, initMachine)
import Swarm.Game.Entity
import Swarm.Game.Failure (SystemFailure (..))
import Swarm.Game.Location
@ -111,19 +111,20 @@ import Swarm.Game.Recipe (
outRecipeMap,
)
import Swarm.Game.Robot
import Swarm.Game.Robot.Concrete
import Swarm.Game.Scenario.Objective
import Swarm.Game.Scenario.Status
import Swarm.Game.Scenario.Topography.Structure qualified as Structure
import Swarm.Game.Scenario.Topography.Structure.Recognition
import Swarm.Game.Scenario.Topography.Structure.Recognition.Log
import Swarm.Game.Scenario.Topography.Structure.Recognition.Precompute
import Swarm.Game.Scenario.Topography.Structure.Recognition.Type
import Swarm.Game.ScenarioInfo
import Swarm.Game.State.Robot
import Swarm.Game.State.Substate
import Swarm.Game.Step.Path.Type
import Swarm.Game.Terrain (TerrainType (..))
import Swarm.Game.Tick (addTicks)
import Swarm.Game.Universe as U
import Swarm.Game.World (Coords (..), WorldFun (..), locToCoords, worldFunFromArray)
import Swarm.Game.World qualified as W

View File

@ -67,11 +67,14 @@ import Data.Maybe (fromMaybe, mapMaybe)
import Data.Set qualified as S
import Data.Tuple (swap)
import GHC.Generics (Generic)
import Swarm.Game.CESK (CESK (Waiting), TickNumber (..), addTicks)
import Swarm.Game.CESK (CESK (Waiting))
import Swarm.Game.Location
import Swarm.Game.ResourceLoading (NameGenerator)
import Swarm.Game.Robot
import Swarm.Game.Robot.Concrete
import Swarm.Game.Robot.Context
import Swarm.Game.State.Config
import Swarm.Game.Tick
import Swarm.Game.Universe as U
import Swarm.Util (binTuples, surfaceEmpty, (<+=), (<<.=))
import Swarm.Util.Lens (makeLensesExcluding)

View File

@ -102,7 +102,6 @@ import Servant.Docs (ToSample)
import Servant.Docs qualified as SD
import Swarm.Game.Achievement.Attainment
import Swarm.Game.Achievement.Definitions
import Swarm.Game.CESK (TickNumber (..))
import Swarm.Game.Entity
import Swarm.Game.Location
import Swarm.Game.Recipe (
@ -118,6 +117,7 @@ import Swarm.Game.Scenario.Topography.Structure.Recognition
import Swarm.Game.Scenario.Topography.Structure.Recognition.Registry (emptyFoundStructures)
import Swarm.Game.Scenario.Topography.Structure.Recognition.Type (RecognizerAutomatons (..))
import Swarm.Game.State.Config
import Swarm.Game.Tick (TickNumber (..))
import Swarm.Game.Universe as U
import Swarm.Game.World qualified as W
import Swarm.Game.World.Gen (Seed)

View File

@ -49,6 +49,8 @@ import Swarm.Game.Display
import Swarm.Game.Entity hiding (empty, lookup, singleton, union)
import Swarm.Game.Exception
import Swarm.Game.Robot
import Swarm.Game.Robot.Concrete
import Swarm.Game.Robot.Context
import Swarm.Game.Scenario.Objective qualified as OB
import Swarm.Game.Scenario.Objective.WinCheck qualified as WC
import Swarm.Game.State
@ -58,6 +60,7 @@ import Swarm.Game.Step.Const
import Swarm.Game.Step.RobotStepState
import Swarm.Game.Step.Util
import Swarm.Game.Step.Util.Command
import Swarm.Game.Tick
import Swarm.Game.Universe
import Swarm.Language.Capability
import Swarm.Language.Context hiding (delete)

View File

@ -56,6 +56,8 @@ import Swarm.Game.Location
import Swarm.Game.Recipe
import Swarm.Game.ResourceLoading (getDataFileNameSafe)
import Swarm.Game.Robot
import Swarm.Game.Robot.Concrete
import Swarm.Game.Robot.Context
import Swarm.Game.Scenario.Topography.Area (getAreaDimensions)
import Swarm.Game.Scenario.Topography.Navigation.Portal (Navigation (..))
import Swarm.Game.Scenario.Topography.Navigation.Util
@ -76,6 +78,7 @@ import Swarm.Game.Step.RobotStepState
import Swarm.Game.Step.Util
import Swarm.Game.Step.Util.Command
import Swarm.Game.Step.Util.Inspect
import Swarm.Game.Tick
import Swarm.Game.Universe
import Swarm.Game.Value
import Swarm.Game.World (locToCoords)

View File

@ -12,6 +12,13 @@
-- A data type to represent robots.
module Swarm.Game.Robot (
-- * Robots data
RobotContextMember,
_robotID,
_robotLocation,
_robotContext,
RobotMachine,
_machine,
emptyActivityCount,
-- * Robots
RobotPhase (..),
@ -20,16 +27,7 @@ module Swarm.Game.Robot (
Robot,
TRobot,
-- ** Runtime robot update
C.RobotUpdate (..),
-- * Robot context
RobotContext,
defTypes,
defReqs,
defVals,
defStore,
emptyRobotContext,
WalkabilityContext (..),
-- ** Lenses
@ -50,11 +48,9 @@ module Swarm.Game.Robot (
inventoryHash,
robotCapabilities,
walkabilityContext,
robotContext,
robotID,
robotParentID,
robotHeavy,
machine,
systemRobot,
selfDestruct,
runningAtomic,
@ -67,14 +63,9 @@ module Swarm.Game.Robot (
-- ** Creation & instantiation
mkRobot,
instantiateRobot,
-- ** Query
robotKnows,
isActive,
wantsToStep,
waitingUntil,
getResult,
-- ** Constants
hearingDistance,
@ -82,11 +73,10 @@ module Swarm.Game.Robot (
import Control.Applicative ((<|>))
import Control.Lens hiding (Const, contains)
import Data.Aeson qualified as Ae (FromJSON, Key, KeyValue, ToJSON (..), object, (.=))
import Data.Aeson qualified as Ae (FromJSON, ToJSON (..))
import Data.Hashable (hashWithSalt)
import Data.Kind qualified
import Data.Map (Map)
import Data.Maybe (catMaybes, fromMaybe, isNothing)
import Data.Sequence (Seq)
import Data.Sequence qualified as Seq
import Data.Set (Set)
@ -94,20 +84,15 @@ import Data.Text (Text)
import Data.Yaml (FromJSON (parseJSON), (.!=), (.:), (.:?))
import GHC.Generics (Generic)
import Linear
import Servant.Docs (ToSample)
import Servant.Docs qualified as SD
import Swarm.Game.CESK qualified as C
import Swarm.Game.Display (Display, curOrientation, defaultRobotDisplay, invisible)
import Swarm.Game.Entity hiding (empty)
import Swarm.Game.Location (Heading, Location, toDirection, toHeading)
import Swarm.Game.Robot.Context
import Swarm.Game.Tick
import Swarm.Game.Universe
import Swarm.Language.Capability (Capability)
import Swarm.Language.Pipeline (ProcessedTerm)
import Swarm.Language.Pipeline.QQ (tmQ)
import Swarm.Language.Syntax (Const, Syntax)
import Swarm.Language.Text.Markdown (Document)
import Swarm.Language.Value as V
import Swarm.Log
import Swarm.Util.Lens (makeLensesExcluding, makeLensesNoSigs)
import Swarm.Util.WindowedCounter
@ -131,7 +116,7 @@ data ActivityCounts = ActivityCounts
, _tangibleCommandCount :: Int
, _commandsHistogram :: Map Const Int
, _lifetimeStepCount :: Int
, _activityWindow :: WindowedCounter C.TickNumber
, _activityWindow :: WindowedCounter TickNumber
}
deriving (Eq, Show, Generic, Ae.FromJSON, Ae.ToJSON)
@ -203,7 +188,7 @@ commandsHistogram :: Lens' ActivityCounts (Map Const Int)
lifetimeStepCount :: Lens' ActivityCounts Int
-- | Sliding window over a span of ticks indicating ratio of activity
activityWindow :: Lens' ActivityCounts (WindowedCounter C.TickNumber)
activityWindow :: Lens' ActivityCounts (WindowedCounter TickNumber)
-- | With a robot template, we may or may not have a location. With a
-- concrete robot we must have a location.
@ -218,11 +203,9 @@ type family RobotID (phase :: RobotPhase) :: Data.Kind.Type where
type family RobotMachine (phase :: RobotPhase) :: Data.Kind.Type
type instance RobotMachine 'TemplateRobot = Maybe ProcessedTerm
type instance RobotMachine 'ConcreteRobot = C.CESK
type family RobotContextMember (phase :: RobotPhase) :: Data.Kind.Type
type instance RobotContextMember 'TemplateRobot = ()
type instance RobotContextMember 'ConcreteRobot = RobotContext
-- | A value of type 'RobotR' is a record representing the state of a
-- single robot. The @f@ parameter is for tracking whether or not
@ -256,7 +239,7 @@ deriving instance (Eq (RobotLocation phase), Eq (RobotID phase), Eq (RobotMachin
-- See https://byorgey.wordpress.com/2021/09/17/automatically-updated-cached-views-with-lens/
-- for the approach used here with lenses.
makeLensesExcluding ['_robotCapabilities, '_equippedDevices, '_robotLog] ''RobotR
makeLensesExcluding ['_robotCapabilities, '_equippedDevices, '_robotLog, '_robotContext, '_machine] ''RobotR
-- | A template robot, i.e. a template robot record without a unique ID number,
-- and possibly without a location.
@ -265,28 +248,6 @@ type TRobot = RobotR 'TemplateRobot
-- | A concrete robot, with a unique ID number and a specific location.
type Robot = RobotR 'ConcreteRobot
instance ToSample Robot where
toSamples _ = SD.singleSample sampleBase
where
sampleBase :: Robot
sampleBase =
mkRobot
0
emptyRobotContext
Nothing
"base"
"The starting robot."
defaultCosmicLocation
zero
defaultRobotDisplay
(C.initMachine [tmQ| move |] mempty C.emptyStore)
[]
[]
False
False
mempty
0
-- In theory we could make all these lenses over (RobotR phase), but
-- that leads to lots of type ambiguity problems later. In practice
-- we only need lenses for Robots.
@ -364,30 +325,10 @@ robotOrientation = robotEntity . entityOrientation
robotInventory :: Lens' Robot Inventory
robotInventory = robotEntity . entityInventory
-- | The robot's context.
robotContext :: Lens' Robot RobotContext
-- | The (unique) ID number of the robot. This is only a Getter since
-- the robot ID is immutable.
robotID :: Getter Robot RID
-- | Instantiate a robot template to make it into a concrete robot, by
-- providing a robot ID. Concrete robots also require a location;
-- 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'.
--
-- If a machine is not supplied (i.e. 'Nothing'), will fallback to any
-- program specified in the template robot.
instantiateRobot :: Maybe C.CESK -> RID -> TRobot -> Robot
instantiateRobot maybeMachine i r =
r
{ _robotID = i
, _robotLocation = fromMaybe defaultCosmicLocation $ _robotLocation r
, _machine = fromMaybe (mkMachine $ _machine r) maybeMachine
, _robotContext = emptyRobotContext
}
-- | The ID number of the robot's parent, that is, the robot that
-- built (or most recently reprogrammed) this robot, if there is
-- one.
@ -454,9 +395,6 @@ robotKnows r e = contains0plus e (r ^. robotInventory) || contains0plus e (r ^.
robotCapabilities :: Getter Robot (Set Capability)
robotCapabilities = to _robotCapabilities
-- | The robot's current CEK machine state.
machine :: Lens' Robot C.CESK
-- | Is this robot a "system robot"? System robots are generated by
-- the system (as opposed to created by the user) and are not
-- subject to the usual capability restrictions.
@ -483,10 +421,6 @@ walkabilityContext :: Getter Robot WalkabilityContext
walkabilityContext = to $
\x -> WalkabilityContext (_robotCapabilities x) (_unwalkableEntities x)
mkMachine :: Maybe ProcessedTerm -> C.CESK
mkMachine Nothing = C.Out VUnit C.emptyStore []
mkMachine (Just pt) = C.initMachine pt mempty C.emptyStore
-- | A general function for creating robots.
mkRobot ::
-- | ID number of the robot.
@ -577,65 +511,5 @@ instance FromJSONE EntityMap TRobot where
<*> liftE (v .:? "unwalkable" ..!= mempty)
<*> pure 0
(.=?) :: (Ae.KeyValue a, Ae.ToJSON v, Eq v) => Ae.Key -> v -> v -> Maybe a
(.=?) n v defaultVal = if defaultVal /= v then Just $ n Ae..= v else Nothing
(.==) :: (Ae.KeyValue a, Ae.ToJSON v) => Ae.Key -> v -> Maybe a
(.==) n v = Just $ n Ae..= v
instance Ae.ToJSON Robot where
toJSON r =
Ae.object $
catMaybes
[ "id" .== (r ^. robotID)
, "name" .== (r ^. robotEntity . entityDisplay)
, "description" .=? (r ^. robotEntity . entityDescription) $ mempty
, "loc" .== (r ^. robotLocation)
, "dir" .=? (r ^. robotEntity . entityOrientation) $ zero
, "display" .=? (r ^. robotDisplay) $ (defaultRobotDisplay & invisible .~ sys)
, "program" .== (r ^. machine)
, "devices" .=? (map (^. _2 . entityName) . elems $ r ^. equippedDevices) $ []
, "inventory" .=? (map (_2 %~ view entityName) . elems $ r ^. robotInventory) $ []
, "system" .=? sys $ False
, "heavy" .=? (r ^. robotHeavy) $ False
, "log" .=? (r ^. robotLog) $ mempty
, -- debug
"capabilities" .=? (r ^. robotCapabilities) $ mempty
, "logUpdated" .=? (r ^. robotLogUpdated) $ False
, "context" .=? (r ^. robotContext) $ emptyRobotContext
, "parent" .=? (r ^. robotParentID) $ Nothing
, "createdAt" .=? (r ^. robotCreatedAt) $ 0
, "selfDestruct" .=? (r ^. selfDestruct) $ False
, "activity" .=? (r ^. activityCounts) $ emptyActivityCount
, "runningAtomic" .=? (r ^. runningAtomic) $ False
]
where
sys = r ^. systemRobot
-- | Is the robot actively in the middle of a computation?
isActive :: Robot -> Bool
{-# INLINE isActive #-}
isActive = isNothing . getResult
-- | "Active" robots include robots that are waiting; 'wantsToStep' is
-- true if the robot actually wants to take another step right now
-- (this is a /subset/ of active robots).
wantsToStep :: C.TickNumber -> Robot -> Bool
wantsToStep now robot
| not (isActive robot) = False
| otherwise = maybe True (now >=) (waitingUntil robot)
-- | The time until which the robot is waiting, if any.
waitingUntil :: Robot -> Maybe C.TickNumber
waitingUntil robot =
case _machine robot of
C.Waiting time _ -> Just time
_ -> Nothing
-- | Get the result of the robot's computation if it is finished.
getResult :: Robot -> Maybe (Value, C.Store)
{-# INLINE getResult #-}
getResult = C.finalValue . view machine
hearingDistance :: (Num i) => i
hearingDistance = 32

View File

@ -15,7 +15,7 @@ import Data.Text (Text)
import GHC.Generics (Generic)
import Servant.Docs (ToSample)
import Servant.Docs qualified as SD
import Swarm.Game.Achievement.Definitions
import Swarm.Game.Achievement.Definitions qualified as AD
import Swarm.Game.Scenario.Objective.Logic as L
import Swarm.Language.Pipeline (ProcessedTerm)
import Swarm.Language.Syntax (Syntax)
@ -74,7 +74,7 @@ data Objective = Objective
, _objectiveOptional :: Bool
, _objectivePrerequisite :: Maybe PrerequisiteConfig
, _objectiveHidden :: Bool
, _objectiveAchievement :: Maybe AchievementInfo
, _objectiveAchievement :: Maybe AD.AchievementInfo
}
deriving (Eq, Show, Generic, ToJSON)
@ -118,7 +118,7 @@ objectiveHidden :: Lens' Objective Bool
-- | An optional achievement that is to be registered globally
-- when this objective is completed.
objectiveAchievement :: Lens' Objective (Maybe AchievementInfo)
objectiveAchievement :: Lens' Objective (Maybe AD.AchievementInfo)
instance FromJSON Objective where
parseJSON = withObject "objective" $ \v ->

View File

@ -14,6 +14,7 @@ import Data.Graph (Graph, SCC (AcyclicSCC), graphFromEdges, stronglyConnComp)
import Data.Map (Map)
import Data.Map.Strict qualified as M
import Data.Maybe (mapMaybe)
import Data.Set (Set)
import Data.Set qualified as Set
import Data.Text qualified as T
import Data.Tuple (swap)
@ -22,7 +23,6 @@ import Servant.Docs (ToSample)
import Servant.Docs qualified as SD
import Swarm.Game.Scenario.Objective
import Swarm.Game.Scenario.Objective.Logic as L
import Swarm.Game.Scenario.Objective.WinCheck
-- | This is only needed for constructing a Graph,
-- which requires all nodes to have a key.
@ -49,11 +49,17 @@ instance ToJSON Graph where
instance ToSample GraphInfo where
toSamples _ = SD.noSamples
deriving instance Generic (BE.Signed ObjectiveLabel)
deriving instance ToJSON (BE.Signed ObjectiveLabel)
getConstFromSigned :: BE.Signed a -> a
getConstFromSigned = \case
BE.Positive x -> x
BE.Negative x -> x
getDistinctConstants :: (Ord a) => Prerequisite a -> Set (BE.Signed a)
getDistinctConstants = Set.fromList . BE.constants . toBoolExpr
-- | Collect all of the constants that have a negation.
-- This is necessary for enumerating all of the distinct
-- nodes when constructing a Graph, as we treat a constant

View File

@ -11,8 +11,8 @@ import Data.Aeson
import Data.Char (toLower)
import Data.Time (NominalDiffTime)
import GHC.Generics (Generic)
import Swarm.Game.CESK (TickNumber (..))
import Swarm.Game.Scenario.Scoring.CodeSize
import Swarm.Game.Tick (TickNumber (..))
scenarioOptions :: Options
scenarioOptions =

View File

@ -19,12 +19,12 @@ import Data.Function (on)
import Data.Time (ZonedTime, diffUTCTime, zonedTimeToUTC)
import Data.Yaml as Y
import GHC.Generics (Generic)
import Swarm.Game.CESK (TickNumber)
import Swarm.Game.Scenario
import Swarm.Game.Scenario.Scoring.Best
import Swarm.Game.Scenario.Scoring.CodeSize
import Swarm.Game.Scenario.Scoring.ConcreteMetrics
import Swarm.Game.Scenario.Scoring.GenericMetrics
import Swarm.Game.Tick (TickNumber)
import Swarm.Game.World.Gen (Seed)
import Swarm.Util.Lens (makeLensesNoSigs)

View File

@ -0,0 +1,29 @@
{-# LANGUAGE DeriveGeneric #-}
-- |
-- SPDX-License-Identifier: BSD-3-Clause
module Swarm.Game.Tick (
TickNumber (..),
addTicks,
) where
import Data.Aeson (FromJSON, ToJSON)
import Data.Int (Int64)
import GHC.Generics (Generic)
import Prettyprinter (Pretty (..))
import Swarm.Util.WindowedCounter (Offsettable (..))
-- | A newtype representing a count of ticks (typically since the
-- start of a game).
newtype TickNumber = TickNumber {getTickNumber :: Int64}
deriving (Eq, Ord, Show, Read, Generic, FromJSON, ToJSON)
-- | Add an offset to a 'TickNumber'.
addTicks :: Int -> TickNumber -> TickNumber
addTicks i (TickNumber n) = TickNumber $ n + fromIntegral i
instance Offsettable TickNumber where
offsetBy = addTicks
instance Pretty TickNumber where
pretty (TickNumber i) = pretty i

View File

@ -26,7 +26,7 @@ import Data.Text (Text)
import Data.Text qualified as T
import Prettyprinter
import Swarm.Game.Entity (Entity, entityName)
import Swarm.Game.Robot (Robot, robotName)
import Swarm.Game.Robot (TRobot, trobotName)
import Swarm.Game.Terrain
import Swarm.Game.World.Coords
import Swarm.Language.Pretty
@ -53,7 +53,7 @@ prettyRawCellItem :: (Maybe CellTag, Text) -> Doc ann
prettyRawCellItem (Nothing, t) = pretty t
prettyRawCellItem (Just tag, t) = pretty (T.toLower . T.drop 4 . showT $ tag) <> ":" <> pretty t
data CellVal = CellVal TerrainType (Erasable (Last Entity)) [Robot]
data CellVal = CellVal TerrainType (Erasable (Last Entity)) [TRobot]
deriving (Eq, Show)
instance PrettyPrec CellVal where
@ -63,7 +63,7 @@ instance PrettyPrec CellVal where
items =
[(Just CellTerrain, getTerrainWord terr) | terr /= BlankT]
++ [(Just CellEntity, e ^. entityName) | EJust (Last e) <- [ent]]
++ map ((Just CellRobot,) . view robotName) rs
++ map ((Just CellRobot,) . view trobotName) rs
data Rot = Rot0 | Rot90 | Rot180 | Rot270
deriving (Eq, Ord, Show, Bounded, Enum)

View File

@ -22,8 +22,8 @@ import Control.Lens (makeLenses)
import Data.Aeson (FromJSON, ToJSON)
import Data.Text (Text)
import GHC.Generics (Generic)
import Swarm.Game.CESK (TickNumber)
import Swarm.Game.Location (Location)
import Swarm.Game.Tick (TickNumber)
import Swarm.Game.Universe (Cosmic)
-- | Severity of the error - critical errors are bugs

View File

@ -18,7 +18,8 @@ description: Swarm is a 2D programming and resource gathering
* utilities
* swarm language
* swarm game
* swarm scenario
* swarm game engine
* swarm TUI
* swarm app
@ -154,35 +155,26 @@ library swarm-lang
-- See discussion in #415
StrictData
library swarm-engine
library swarm-scenario
import: stan-config, common, ghc2021-extensions
visibility: public
exposed-modules: Swarm.Constant
Swarm.Effect
Swarm.Effect.Time
Swarm.Game.Failure
Swarm.Game.Achievement.Attainment
Swarm.Game.Achievement.Definitions
Swarm.Game.Achievement.Description
Swarm.Game.Achievement.Persistence
Swarm.Game.CESK
Swarm.Game.Display
Swarm.Game.Entity
Swarm.Game.Entity.Cosmetic
Swarm.Game.Entity.Cosmetic.Assignment
Swarm.Game.Exception
Swarm.Game.Failure
Swarm.Game.Location
Swarm.Game.Recipe
Swarm.Game.ResourceLoading
Swarm.Game.Robot
Swarm.Game.Robot.Context
Swarm.Game.Scenario
Swarm.Game.Universe
Swarm.Game.Scenario.Objective
Swarm.Game.Scenario.Objective.Graph
Swarm.Game.Scenario.Objective.Logic
Swarm.Game.Scenario.Objective.Validation
Swarm.Game.Scenario.Objective.WinCheck
Swarm.Game.Scenario.RobotLookup
Swarm.Game.Scenario.Scoring.Best
Swarm.Game.Scenario.Scoring.CodeSize
@ -192,10 +184,8 @@ library swarm-engine
Swarm.Game.Scenario.Style
Swarm.Game.Scenario.Topography.Area
Swarm.Game.Scenario.Topography.Cell
Swarm.Game.Scenario.Topography.Center
Swarm.Game.Scenario.Topography.EntityFacade
Swarm.Game.Scenario.Topography.Navigation.Portal
Swarm.Game.Scenario.Topography.Navigation.Util
Swarm.Game.Scenario.Topography.Navigation.Waypoint
Swarm.Game.Scenario.Topography.Placement
Swarm.Game.Scenario.Topography.Structure
@ -204,11 +194,90 @@ library swarm-engine
Swarm.Game.Scenario.Topography.Structure.Recognition.Precompute
Swarm.Game.Scenario.Topography.Structure.Recognition.Registry
Swarm.Game.Scenario.Topography.Structure.Recognition.Symmetry
Swarm.Game.Scenario.Topography.Structure.Recognition.Tracking
Swarm.Game.Scenario.Topography.Structure.Recognition.Type
Swarm.Game.Scenario.Topography.WorldDescription
Swarm.Game.Scenario.Topography.WorldPalette
Swarm.Game.ScenarioInfo
Swarm.Game.Terrain
Swarm.Game.Tick
Swarm.Game.World
Swarm.Game.World.Abstract
Swarm.Game.World.Compile
Swarm.Game.World.Coords
Swarm.Game.World.Eval
Swarm.Game.World.Gen
Swarm.Game.World.Interpret
Swarm.Game.World.Load
Swarm.Game.World.Modify
Swarm.Game.World.Parse
Swarm.Game.World.Syntax
Swarm.Game.World.Typecheck
Swarm.Log
Swarm.Util.Content
Swarm.Util.Effect
other-modules: Paths_swarm
autogen-modules: Paths_swarm
build-depends: base >= 4.14 && < 4.19,
AhoCorasick >= 0.0.4 && < 0.0.5,
aeson >= 2 && < 2.2,
array >= 0.5.4 && < 0.6,
boolexpr >= 0.2 && < 0.3,
bytestring >= 0.10 && < 0.12,
clock >= 0.8.2 && < 0.9,
colour >= 2.3.6 && < 2.4,
containers >= 0.6.2 && < 0.7,
directory >= 1.3 && < 1.4,
either >= 5.0 && < 5.1,
extra >= 1.7 && < 1.8,
filepath >= 1.4 && < 1.5,
fused-effects >= 1.1.1.1 && < 1.2,
hashable >= 1.3.4 && < 1.5,
hsnoise >= 0.0.3 && < 0.1,
lens >= 4.19 && < 5.3,
linear >= 1.21.6 && < 1.23,
megaparsec >= 9.6.1 && < 9.7,
transformers >= 0.5 && < 0.7,
murmur3 >= 1.0.4 && < 1.1,
nonempty-containers >= 0.3.4 && < 0.3.5,
palette >= 0.3 && < 0.4,
parser-combinators >= 1.2 && < 1.4,
prettyprinter >= 1.7.0 && < 1.8,
servant-docs >= 0.12 && < 0.14,
simple-enumeration >= 0.2 && < 0.3,
tagged >= 0.8 && < 0.9,
text >= 1.2.4 && < 2.1,
time >= 1.9 && < 1.14,
transformers >= 0.5.6.2 && < 0.6.2.0,
vector >= 0.12 && < 0.14,
vty >= 6.1 && < 6.2,
witch >= 1.1.1.0 && < 1.3,
witherable >= 0.4 && < 0.5,
yaml >= 0.11 && < 0.11.12.0,
build-depends: swarm:swarm-util,
swarm:swarm-lang,
hs-source-dirs: src/swarm-scenario
default-language: Haskell2010
default-extensions:
-- Avoid unexpected unevaluated thunk buildup
-- See discussion in #415
StrictData
library swarm-engine
import: stan-config, common, ghc2021-extensions
visibility: public
exposed-modules: Swarm.Effect
Swarm.Effect.Time
Swarm.Game.Achievement.Attainment
Swarm.Game.Achievement.Description
Swarm.Game.Achievement.Persistence
Swarm.Game.CESK
Swarm.Game.Exception
Swarm.Game.Robot.Concrete
Swarm.Game.Robot.Context
Swarm.Game.Scenario.Objective.WinCheck
Swarm.Game.Scenario.Topography.Center
Swarm.Game.Scenario.Topography.Navigation.Util
Swarm.Game.Scenario.Topography.Structure.Recognition.Tracking
Swarm.Game.State
Swarm.Game.State.Config
Swarm.Game.State.Robot
@ -226,73 +295,45 @@ library swarm-engine
Swarm.Game.Step.Util
Swarm.Game.Step.Util.Command
Swarm.Game.Step.Util.Inspect
Swarm.Game.Terrain
Swarm.Game.Value
Swarm.Game.World
Swarm.Game.World.Abstract
Swarm.Game.World.Compile
Swarm.Game.World.Coords
Swarm.Game.World.Eval
Swarm.Game.World.Gen
Swarm.Game.World.Interpret
Swarm.Game.World.Load
Swarm.Game.World.Modify
Swarm.Game.World.Parse
Swarm.Game.World.Render
Swarm.Game.World.Syntax
Swarm.Game.World.Typecheck
Swarm.Log
Swarm.Util.Effect
Swarm.Util.Content
other-modules: Paths_swarm
autogen-modules: Paths_swarm
build-depends: base >= 4.14 && < 4.19,
AhoCorasick >= 0.0.4 && < 0.0.5,
aeson >= 2 && < 2.2,
array >= 0.5.4 && < 0.6,
astar >= 0.3 && < 0.3.1,
boolexpr >= 0.2 && < 0.3,
bytestring >= 0.10 && < 0.12,
clock >= 0.8.2 && < 0.9,
colour >= 2.3.6 && < 2.4,
containers >= 0.6.2 && < 0.7,
directory >= 1.3 && < 1.4,
either >= 5.0 && < 5.1,
extra >= 1.7 && < 1.8,
filepath >= 1.4 && < 1.5,
fused-effects >= 1.1.1.1 && < 1.2,
fused-effects-lens >= 1.2.0.1 && < 1.3,
hashable >= 1.3.4 && < 1.5,
hsnoise >= 0.0.3 && < 0.1,
JuicyPixels >= 3.3 && < 3.4,
lens >= 4.19 && < 5.3,
linear >= 1.21.6 && < 1.23,
megaparsec >= 9.6.1 && < 9.7,
transformers >= 0.5 && < 0.7,
mtl >= 2.2.2 && < 2.4,
murmur3 >= 1.0.4 && < 1.1,
nonempty-containers >= 0.3.4 && < 0.3.5,
palette >= 0.3 && < 0.4,
parser-combinators >= 1.2 && < 1.4,
prettyprinter >= 1.7.0 && < 1.8,
random >= 1.2.0 && < 1.3,
servant-docs >= 0.12 && < 0.14,
SHA >= 1.6.4 && < 1.6.5,
simple-enumeration >= 0.2 && < 0.3,
tagged >= 0.8 && < 0.9,
text >= 1.2.4 && < 2.1,
time >= 1.9 && < 1.14,
transformers >= 0.5.6.2 && < 0.6.2.0,
unordered-containers >= 0.2.14 && < 0.3,
vector >= 0.12 && < 0.14,
vty >= 6.1 && < 6.2,
witch >= 1.1.1.0 && < 1.3,
witherable >= 0.4 && < 0.5,
yaml >= 0.11 && < 0.11.12.0,
build-depends: swarm:swarm-util,
swarm:swarm-lang,
swarm:swarm-scenario,
hs-source-dirs: src/swarm-engine
default-language: Haskell2010
default-extensions:
@ -441,6 +482,7 @@ library
, Swarm.Game.Recipe
, Swarm.Game.ResourceLoading
, Swarm.Game.Robot
, Swarm.Game.Robot.Concrete
, Swarm.Game.Robot.Context
, Swarm.Game.Scenario
, Swarm.Game.Universe
@ -493,6 +535,7 @@ library
, Swarm.Game.Step.Util.Command
, Swarm.Game.Step.Util.Inspect
, Swarm.Game.Terrain
, Swarm.Game.Tick
, Swarm.Game.Value
, Swarm.Game.World
, Swarm.Game.World.Abstract
@ -586,6 +629,7 @@ library
build-depends: swarm:swarm-util,
swarm:swarm-lang,
swarm:swarm-engine,
swarm:swarm-scenario,
hs-source-dirs: src
default-language: Haskell2010
default-extensions:

View File

@ -30,10 +30,12 @@ import Swarm.Doc.Keyword (EditorType (..))
import Swarm.Doc.Keyword qualified as Keyword
import Swarm.Effect (runTimeIO)
import Swarm.Game.Achievement.Definitions (GameplayAchievement (..))
import Swarm.Game.CESK (emptyStore, getTickNumber, initMachine)
import Swarm.Game.CESK (emptyStore, initMachine)
import Swarm.Game.Entity (EntityMap, lookupByName)
import Swarm.Game.Failure (SystemFailure)
import Swarm.Game.Robot (activityCounts, commandsHistogram, defReqs, equippedDevices, lifetimeStepCount, machine, robotContext, robotLog, systemRobot, tangibleCommandCount, waitingUntil)
import Swarm.Game.Robot (activityCounts, commandsHistogram, equippedDevices, lifetimeStepCount, robotLog, systemRobot, tangibleCommandCount)
import Swarm.Game.Robot.Concrete (machine, robotContext, waitingUntil)
import Swarm.Game.Robot.Context (defReqs)
import Swarm.Game.Scenario (Scenario)
import Swarm.Game.State (
GameState,
@ -61,6 +63,7 @@ import Swarm.Game.State.Substate (
)
import Swarm.Game.Step (gameTick)
import Swarm.Game.Step.Path.Type
import Swarm.Game.Tick (getTickNumber)
import Swarm.Game.World.Typecheck (WorldMap)
import Swarm.Language.Context qualified as Ctx
import Swarm.Language.Pipeline (ProcessedTerm (..), processTerm)

View File

@ -9,11 +9,11 @@ module TestNotification where
import Control.Lens (Getter, Ixed (ix), view, (&), (.~), (^.), (^?!))
import Data.Text (Text)
import Data.Text qualified as T
import Swarm.Game.CESK (TickNumber (..))
import Swarm.Game.Robot
import Swarm.Game.State
import Swarm.Game.State.Robot
import Swarm.Game.State.Substate
import Swarm.Game.Tick (TickNumber (..))
import Swarm.Log
import Test.Tasty
import Test.Tasty.HUnit

View File

@ -6,11 +6,11 @@ module TestScoring where
import Data.Text.IO qualified as TIO
import Data.Time.Calendar.OrdinalDate
import Data.Time.LocalTime
import Swarm.Game.CESK (TickNumber (..))
import Swarm.Game.Scenario.Scoring.Best
import Swarm.Game.Scenario.Scoring.CodeSize
import Swarm.Game.Scenario.Scoring.ConcreteMetrics
import Swarm.Game.Scenario.Scoring.GenericMetrics
import Swarm.Game.Tick (TickNumber (..))
import Swarm.Language.Module
import Swarm.Language.Pipeline
import Swarm.Language.Syntax

View File

@ -17,6 +17,7 @@ import Swarm.Effect
import Swarm.Game.CESK
import Swarm.Game.Exception
import Swarm.Game.Robot
import Swarm.Game.Robot.Concrete (isActive)
import Swarm.Game.State
import Swarm.Game.State.Robot
import Swarm.Game.State.Substate