mirror of
https://github.com/swarm-game/swarm.git
synced 2024-07-07 11:16:35 +03:00
Render command matrix (#1658)
## Demo 1. Run `scripts/play.sh` 2. Load http://localhost:5357/command-matrix.html The rows are sortable by column. ### Also stack build swarm:swarm-docs --fast && stack exec swarm-docs -- cheatsheet --matrix ## Screenshot ![Screenshot from 2024-01-21 21-32-56](https://github.com/swarm-game/swarm/assets/261693/f92f5ac9-8440-4aac-9a4b-9e5edac616f2)
This commit is contained in:
parent
101b17c882
commit
5f53082971
|
@ -50,6 +50,7 @@ cliParser =
|
|||
, Just Recipes <$ switch (long "recipes" <> help "Generate recipes page (uses data from recipes.yaml)")
|
||||
, Just Capabilities <$ switch (long "capabilities" <> help "Generate capabilities page (uses entity map)")
|
||||
, Just Commands <$ switch (long "commands" <> help "Generate commands page (uses constInfo, constCaps and inferConst)")
|
||||
, Just CommandMatrix <$ switch (long "matrix" <> help "Generate commands matrix page")
|
||||
, Just Scenario <$ switch (long "scenario" <> help "Generate scenario schema page")
|
||||
]
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import Swarm.Doc.Schema.Parse
|
|||
import Swarm.Doc.Schema.Refined
|
||||
import Swarm.Doc.Schema.SchemaType
|
||||
import Swarm.Doc.Util
|
||||
import Swarm.Doc.Wiki.Util
|
||||
import Swarm.Util (applyWhen, brackets, quote, showT)
|
||||
import System.Directory (listDirectory)
|
||||
import System.FilePath (splitExtension, (<.>), (</>))
|
||||
|
@ -112,9 +113,7 @@ recombineExtension (filenameStem, fileExtension) =
|
|||
|
||||
genMarkdown :: [SchemaData] -> Either T.Text T.Text
|
||||
genMarkdown schemaThings =
|
||||
left renderError $
|
||||
runPure $
|
||||
writeMarkdown (def {writerExtensions = extensionsFromList [Ext_pipe_tables]}) pd
|
||||
pandocToText pd
|
||||
where
|
||||
titleMap = makeTitleMap schemaThings
|
||||
pd =
|
||||
|
|
|
@ -25,6 +25,8 @@ import Data.Text qualified as T
|
|||
import Data.Text.IO qualified as T
|
||||
import Swarm.Doc.Schema.Render
|
||||
import Swarm.Doc.Util
|
||||
import Swarm.Doc.Wiki.Matrix
|
||||
import Swarm.Doc.Wiki.Util
|
||||
import Swarm.Game.Display (displayChar)
|
||||
import Swarm.Game.Entity (Entity, EntityMap (entitiesByName), entityDisplay, entityName, loadEntities)
|
||||
import Swarm.Game.Entity qualified as E
|
||||
|
@ -52,7 +54,7 @@ data PageAddress = PageAddress
|
|||
deriving (Eq, Show)
|
||||
|
||||
-- | An enumeration of the kinds of cheat sheets we can produce.
|
||||
data SheetType = Entities | Commands | Capabilities | Recipes | Scenario
|
||||
data SheetType = Entities | Commands | CommandMatrix | Capabilities | Recipes | Scenario
|
||||
deriving (Eq, Show, Enum, Bounded)
|
||||
|
||||
-- * Functions
|
||||
|
@ -62,6 +64,9 @@ makeWikiPage address s = case s of
|
|||
Nothing -> error "Not implemented for all Wikis"
|
||||
Just st -> case st of
|
||||
Commands -> T.putStrLn commandsPage
|
||||
CommandMatrix -> case pandocToText commandsMatrix of
|
||||
Right x -> T.putStrLn x
|
||||
Left x -> error $ T.unpack x
|
||||
Capabilities -> simpleErrorHandle $ do
|
||||
entities <- loadEntities
|
||||
sendIO $ T.putStrLn $ capabilityPage address entities
|
||||
|
|
40
app/doc/Swarm/Doc/Wiki/Matrix.hs
Normal file
40
app/doc/Swarm/Doc/Wiki/Matrix.hs
Normal file
|
@ -0,0 +1,40 @@
|
|||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
-- |
|
||||
-- SPDX-License-Identifier: BSD-3-Clause
|
||||
--
|
||||
-- Auto-generation of command attributes matrix.
|
||||
module Swarm.Doc.Wiki.Matrix where
|
||||
|
||||
import Data.List.NonEmpty qualified as NE
|
||||
import Data.Text qualified as T
|
||||
import Swarm.Doc.Command
|
||||
import Text.Pandoc
|
||||
import Text.Pandoc.Builder
|
||||
|
||||
commandsMatrix :: Pandoc
|
||||
commandsMatrix =
|
||||
setTitle (text "Commands matrix") $
|
||||
doc (header 3 (text "Commands matrix"))
|
||||
<> doc (makePropsTable ["Command", "Effects", "Actor Target", "Type"])
|
||||
|
||||
makePropsTable ::
|
||||
[T.Text] ->
|
||||
Blocks
|
||||
makePropsTable headingsList =
|
||||
simpleTable headerRow $ map genPropsRow catalogEntries
|
||||
where
|
||||
CommandCatalog catalogEntries = getCatalog
|
||||
headerRow = map (plain . text) headingsList
|
||||
|
||||
genPropsRow :: CommandEntry -> [Blocks]
|
||||
genPropsRow e =
|
||||
[ showCode (cmd e)
|
||||
, showCode (effects e)
|
||||
, showCode (hasActorTarget $ derivedAttrs e)
|
||||
]
|
||||
<> NE.toList completeTypeMembers
|
||||
where
|
||||
showCode :: Show a => a -> Blocks
|
||||
showCode = plain . code . T.pack . show
|
||||
completeTypeMembers = NE.map showCode $ argTypes e
|
15
app/doc/Swarm/Doc/Wiki/Util.hs
Normal file
15
app/doc/Swarm/Doc/Wiki/Util.hs
Normal file
|
@ -0,0 +1,15 @@
|
|||
-- |
|
||||
-- SPDX-License-Identifier: BSD-3-Clause
|
||||
--
|
||||
-- Utilities for generating doc markup
|
||||
module Swarm.Doc.Wiki.Util where
|
||||
|
||||
import Control.Arrow (left)
|
||||
import Data.Text (Text)
|
||||
import Text.Pandoc
|
||||
|
||||
pandocToText :: Pandoc -> Either Text Text
|
||||
pandocToText =
|
||||
left renderError
|
||||
. runPure
|
||||
. writeMarkdown (def {writerExtensions = extensionsFromList [Ext_pipe_tables]})
|
76
src/Swarm/Doc/Command.hs
Normal file
76
src/Swarm/Doc/Command.hs
Normal file
|
@ -0,0 +1,76 @@
|
|||
-- |
|
||||
-- SPDX-License-Identifier: BSD-3-Clause
|
||||
--
|
||||
-- Auto-generation of command attributes matrix.
|
||||
module Swarm.Doc.Command where
|
||||
|
||||
import Data.Aeson (ToJSON)
|
||||
import Data.List.NonEmpty qualified as NE
|
||||
import Data.Set (Set)
|
||||
import Data.Set qualified as Set
|
||||
import GHC.Generics (Generic)
|
||||
import Servant.Docs qualified as SD
|
||||
import Swarm.Doc.Util
|
||||
import Swarm.Language.Pretty (unchainFun)
|
||||
import Swarm.Language.Syntax
|
||||
import Swarm.Language.Syntax.CommandMetadata
|
||||
import Swarm.Language.Typecheck (inferConst)
|
||||
import Swarm.Language.Types
|
||||
import Swarm.Util (listEnums)
|
||||
|
||||
data DerivedAttrs = DerivedAttrs
|
||||
{ hasActorTarget :: Bool
|
||||
, pureComputation :: Bool
|
||||
, modifiesEnvironment :: Bool
|
||||
, modifiesRobot :: Bool
|
||||
, movesRobot :: Bool
|
||||
, returnsValue :: Bool
|
||||
, outputType :: String
|
||||
}
|
||||
deriving (Generic, ToJSON)
|
||||
|
||||
data CommandEntry = CommandEntry
|
||||
{ cmd :: Const
|
||||
, effects :: Set CommandEffect
|
||||
, argTypes :: NE.NonEmpty Type
|
||||
, derivedAttrs :: DerivedAttrs
|
||||
}
|
||||
deriving (Generic, ToJSON)
|
||||
|
||||
newtype CommandCatalog = CommandCatalog
|
||||
{ entries :: [CommandEntry]
|
||||
}
|
||||
deriving (Generic, ToJSON)
|
||||
|
||||
instance SD.ToSample CommandCatalog where
|
||||
toSamples _ = SD.noSamples
|
||||
|
||||
-- | Uses explicit effects documentation as well as
|
||||
-- type signature information to compute various flags
|
||||
mkEntry :: Const -> CommandEntry
|
||||
mkEntry c =
|
||||
CommandEntry c cmdEffects rawArgs $
|
||||
DerivedAttrs
|
||||
{ hasActorTarget = operatesOnActor inputArgs
|
||||
, pureComputation = Set.null cmdEffects
|
||||
, modifiesEnvironment = Mutation EntityChange `Set.member` cmdEffects
|
||||
, modifiesRobot = not . Set.disjoint cmdEffects . Set.fromList $ map (Mutation . RobotChange) listEnums
|
||||
, movesRobot = Mutation (RobotChange PositionChange) `Set.member` cmdEffects
|
||||
, returnsValue = theOutputType /= TyCmd TyUnit
|
||||
, outputType = show theOutputType
|
||||
}
|
||||
where
|
||||
cmdInfo = constInfo c
|
||||
cmdEffects = effectInfo $ constDoc cmdInfo
|
||||
|
||||
getArgs ((Forall _ t)) = unchainFun t
|
||||
|
||||
rawArgs = getArgs $ inferConst c
|
||||
|
||||
inputArgs = NE.init rawArgs
|
||||
theOutputType = NE.last rawArgs
|
||||
|
||||
operatesOnActor = elem TyActor
|
||||
|
||||
getCatalog :: CommandCatalog
|
||||
getCatalog = CommandCatalog $ map mkEntry commands
|
|
@ -97,13 +97,15 @@ import Data.Int (Int32)
|
|||
import Data.List.NonEmpty (NonEmpty)
|
||||
import Data.List.NonEmpty qualified as NonEmpty
|
||||
import Data.Map.Strict (Map)
|
||||
import Data.Set (Set)
|
||||
import Data.Set qualified as S
|
||||
import Data.String (IsString (fromString))
|
||||
import Data.Set qualified as Set
|
||||
import Data.Text hiding (filter, length, map)
|
||||
import Data.Text qualified as T
|
||||
import Data.Tree
|
||||
import GHC.Generics (Generic)
|
||||
import Swarm.Language.Direction
|
||||
import Swarm.Language.Syntax.CommandMetadata
|
||||
import Swarm.Language.Types
|
||||
import Swarm.Util qualified as Util
|
||||
import Witch.From (from)
|
||||
|
@ -402,15 +404,21 @@ data ConstInfo = ConstInfo
|
|||
}
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
data ConstDoc = ConstDoc {briefDoc :: Text, longDoc :: Text}
|
||||
data ConstDoc = ConstDoc
|
||||
{ effectInfo :: Set CommandEffect
|
||||
-- ^ NOTE: The absence of effects implies a "pure computation".
|
||||
, briefDoc :: Text
|
||||
, longDoc :: Text
|
||||
}
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
instance IsString ConstDoc where
|
||||
fromString = flip ConstDoc "" . T.pack
|
||||
|
||||
data ConstMeta
|
||||
= -- | Function with arity of which some are commands
|
||||
ConstMFunc Int Bool
|
||||
ConstMFunc
|
||||
-- | Arity
|
||||
Int
|
||||
-- | Is a command?
|
||||
Bool
|
||||
| -- | Unary operator with fixity and associativity.
|
||||
ConstMUnOp MUnAssoc
|
||||
| -- | Binary operator with fixity and associativity.
|
||||
|
@ -527,88 +535,163 @@ isLong c = case tangibility (constInfo c) of
|
|||
-- matching gives us warning if we add more constants.
|
||||
constInfo :: Const -> ConstInfo
|
||||
constInfo c = case c of
|
||||
Wait -> command 0 long "Wait for a number of time steps."
|
||||
Wait ->
|
||||
command 0 long $
|
||||
shortDoc
|
||||
(Set.singleton $ Mutation $ RobotChange BehaviorChange)
|
||||
"Wait for a number of time steps."
|
||||
Noop ->
|
||||
command 0 Intangible . doc "Do nothing." $
|
||||
command 0 Intangible . doc Set.empty "Do nothing." $
|
||||
[ "This is different than `Wait` in that it does not take up a time step."
|
||||
, "It is useful for commands like if, which requires you to provide both branches."
|
||||
, "Usually it is automatically inserted where needed, so you do not have to worry about it."
|
||||
]
|
||||
Selfdestruct ->
|
||||
command 0 short . doc "Self-destruct a robot." $
|
||||
[ "Useful to not clutter the world."
|
||||
, "This destroys the robot's inventory, so consider `salvage` as an alternative."
|
||||
]
|
||||
Move -> command 0 short "Move forward one step."
|
||||
Backup -> command 0 short "Move backward one step."
|
||||
command 0 short
|
||||
. doc
|
||||
(Set.singleton $ Mutation $ RobotChange ExistenceChange)
|
||||
"Self-destruct a robot."
|
||||
$ [ "Useful to not clutter the world."
|
||||
, "This destroys the robot's inventory, so consider `salvage` as an alternative."
|
||||
]
|
||||
Move ->
|
||||
command 0 short $
|
||||
shortDoc
|
||||
(Set.singleton $ Mutation $ RobotChange PositionChange)
|
||||
"Move forward one step."
|
||||
Backup ->
|
||||
command 0 short $
|
||||
shortDoc
|
||||
(Set.singleton $ Mutation $ RobotChange PositionChange)
|
||||
"Move backward one step."
|
||||
Path ->
|
||||
command 2 short . doc "Obtain shortest path to the destination." $
|
||||
[ "Optionally supply a distance limit as the first argument."
|
||||
, "Supply either a location (`inL`) or an entity (`inR`) as the second argument."
|
||||
, "If a path exists, returns the direction to proceed along and the remaining distance."
|
||||
]
|
||||
command 2 short
|
||||
. doc
|
||||
(Set.singleton $ Query $ Sensing EntitySensing)
|
||||
"Obtain shortest path to the destination."
|
||||
$ [ "Optionally supply a distance limit as the first argument."
|
||||
, "Supply either a location (`inL`) or an entity (`inR`) as the second argument."
|
||||
, "If a path exists, returns the direction to proceed along and the remaining distance."
|
||||
]
|
||||
Push ->
|
||||
command 1 short . doc "Push an entity forward one step." $
|
||||
[ "Both entity and robot moves forward one step."
|
||||
, "Destination must not contain an entity."
|
||||
]
|
||||
command 1 short
|
||||
. doc
|
||||
(Set.fromList [Mutation EntityChange, Mutation $ RobotChange PositionChange])
|
||||
"Push an entity forward one step."
|
||||
$ [ "Both entity and robot moves forward one step."
|
||||
, "Destination must not contain an entity."
|
||||
]
|
||||
Stride ->
|
||||
command 1 short . doc "Move forward multiple steps." $
|
||||
[ T.unwords ["Has a max range of", T.pack $ show maxStrideRange, "units."]
|
||||
]
|
||||
Turn -> command 1 short "Turn in some direction."
|
||||
Grab -> command 0 short "Grab an item from the current location."
|
||||
command 1 short
|
||||
. doc
|
||||
(Set.singleton $ Mutation $ RobotChange PositionChange)
|
||||
"Move forward multiple steps."
|
||||
$ [ T.unwords ["Has a max range of", T.pack $ show maxStrideRange, "units."]
|
||||
]
|
||||
Turn ->
|
||||
command 1 short $
|
||||
shortDoc
|
||||
(Set.singleton $ Mutation $ RobotChange PositionChange)
|
||||
"Turn in some direction."
|
||||
Grab ->
|
||||
command 0 short $
|
||||
shortDoc
|
||||
(Set.fromList [Mutation EntityChange, Mutation $ RobotChange InventoryChange])
|
||||
"Grab an item from the current location."
|
||||
Harvest ->
|
||||
command 0 short . doc "Harvest an item from the current location." $
|
||||
command 0 short . doc (Set.fromList [Mutation EntityChange, Mutation $ RobotChange InventoryChange]) "Harvest an item from the current location." $
|
||||
[ "Leaves behind a growing seed if the harvested item is growable."
|
||||
, "Otherwise it works exactly like `grab`."
|
||||
]
|
||||
Ignite ->
|
||||
command 1 short . doc "Ignite a combustible item in the specified direction." $
|
||||
[ "Combustion persists for a random duration and may spread."
|
||||
]
|
||||
command 1 short
|
||||
. doc
|
||||
(Set.singleton $ Mutation EntityChange)
|
||||
"Ignite a combustible item in the specified direction."
|
||||
$ [ "Combustion persists for a random duration and may spread."
|
||||
]
|
||||
Place ->
|
||||
command 1 short . doc "Place an item at the current location." $
|
||||
["The current location has to be empty for this to work."]
|
||||
command 1 short
|
||||
. doc
|
||||
(Set.fromList [Mutation EntityChange, Mutation $ RobotChange InventoryChange])
|
||||
"Place an item at the current location."
|
||||
$ ["The current location has to be empty for this to work."]
|
||||
Ping ->
|
||||
command 1 short . doc "Obtain the relative location of another robot." $
|
||||
[ "The other robot must be within transmission range, accounting for antennas installed on either end, and the invoking robot must be oriented in a cardinal direction."
|
||||
, "The location (x, y) is given relative to one's current orientation:"
|
||||
, "Positive x value is to the right, negative left. Likewise, positive y value is forward, negative back."
|
||||
]
|
||||
Give -> command 2 short "Give an item to another actor nearby."
|
||||
Equip -> command 1 short "Equip a device on oneself."
|
||||
Unequip -> command 1 short "Unequip an equipped device, returning to inventory."
|
||||
Make -> command 1 long "Make an item using a recipe."
|
||||
Has -> command 1 Intangible "Sense whether the robot has a given item in its inventory."
|
||||
Equipped -> command 1 Intangible "Sense whether the robot has a specific device equipped."
|
||||
Count -> command 1 Intangible "Get the count of a given item in a robot's inventory."
|
||||
command 1 short
|
||||
. doc
|
||||
(Set.singleton $ Query $ Sensing RobotSensing)
|
||||
"Obtain the relative location of another robot."
|
||||
$ [ "The other robot must be within transmission range, accounting for antennas installed on either end, and the invoking robot must be oriented in a cardinal direction."
|
||||
, "The location (x, y) is given relative to one's current orientation:"
|
||||
, "Positive x value is to the right, negative left. Likewise, positive y value is forward, negative back."
|
||||
]
|
||||
Give ->
|
||||
command 2 short $
|
||||
shortDoc
|
||||
(Set.singleton $ Mutation $ RobotChange InventoryChange)
|
||||
"Give an item to another actor nearby."
|
||||
Equip ->
|
||||
command 1 short $
|
||||
shortDoc
|
||||
(Set.fromList [Mutation $ RobotChange InventoryChange, Mutation $ RobotChange BehaviorChange])
|
||||
"Equip a device on oneself."
|
||||
Unequip ->
|
||||
command 1 short $
|
||||
shortDoc
|
||||
(Set.fromList [Mutation $ RobotChange InventoryChange, Mutation $ RobotChange BehaviorChange])
|
||||
"Unequip an equipped device, returning to inventory."
|
||||
Make ->
|
||||
command 1 long $
|
||||
shortDoc
|
||||
(Set.singleton $ Mutation $ RobotChange InventoryChange)
|
||||
"Make an item using a recipe."
|
||||
Has ->
|
||||
command 1 Intangible $
|
||||
shortDoc
|
||||
(Set.singleton $ Query $ Sensing RobotSensing)
|
||||
"Sense whether the robot has a given item in its inventory."
|
||||
Equipped ->
|
||||
command 1 Intangible $
|
||||
shortDoc
|
||||
(Set.singleton $ Query $ Sensing RobotSensing)
|
||||
"Sense whether the robot has a specific device equipped."
|
||||
Count ->
|
||||
command 1 Intangible $
|
||||
shortDoc
|
||||
(Set.singleton $ Query $ Sensing RobotSensing)
|
||||
"Get the count of a given item in a robot's inventory."
|
||||
Reprogram ->
|
||||
command 2 long . doc "Reprogram another robot with a new command." $
|
||||
["The other robot has to be nearby and idle."]
|
||||
command 2 long
|
||||
. doc
|
||||
(Set.singleton $ Mutation $ RobotChange BehaviorChange)
|
||||
"Reprogram another robot with a new command."
|
||||
$ ["The other robot has to be nearby and idle."]
|
||||
Drill ->
|
||||
command 1 long . doc "Drill through an entity." $
|
||||
[ "Usually you want to `drill forward` when exploring to clear out obstacles."
|
||||
, "When you have found a source to drill, you can stand on it and `drill down`."
|
||||
, "See what recipes with drill you have available."
|
||||
, "The `drill` command may return the name of an entity added to your inventory."
|
||||
]
|
||||
command 1 long
|
||||
. doc
|
||||
(Set.fromList [Mutation EntityChange, Mutation $ RobotChange InventoryChange])
|
||||
"Drill through an entity."
|
||||
$ [ "Usually you want to `drill forward` when exploring to clear out obstacles."
|
||||
, "When you have found a source to drill, you can stand on it and `drill down`."
|
||||
, "See what recipes with drill you have available."
|
||||
, "The `drill` command may return the name of an entity added to your inventory."
|
||||
]
|
||||
Use ->
|
||||
command 2 long . doc "Use one entity upon another." $
|
||||
command 2 long . doc (Set.singleton $ Mutation EntityChange) "Use one entity upon another." $
|
||||
[ "Which entities you can `use` with others depends on the available recipes."
|
||||
, "The object being used must be a 'required' entity in a recipe."
|
||||
]
|
||||
Build ->
|
||||
command 1 long . doc "Construct a new robot." $
|
||||
command 1 long . doc (Set.singleton $ Mutation $ RobotChange ExistenceChange) "Construct a new robot." $
|
||||
[ "You can specify a command for the robot to execute."
|
||||
, "If the command requires devices they will be taken from your inventory and "
|
||||
<> "equipped on the new robot."
|
||||
]
|
||||
Salvage ->
|
||||
command 0 long . doc "Deconstruct an old robot." $
|
||||
command 0 long . doc (Set.singleton $ Mutation $ RobotChange ExistenceChange) "Deconstruct an old robot." $
|
||||
["Salvaging a robot will give you its inventory, equipped devices and log."]
|
||||
Say ->
|
||||
command 1 short . doc "Emit a message." $
|
||||
command 1 short . doc (Set.singleton $ Mutation $ RobotChange BehaviorChange) "Emit a message." $
|
||||
[ "The message will be in the robot's log (if it has one) and the global log."
|
||||
, "You can view the message that would be picked by `listen` from the global log "
|
||||
<> "in the messages panel, along with your own messages and logs."
|
||||
|
@ -617,208 +700,217 @@ constInfo c = case c of
|
|||
, "In creative mode, there is of course no such limitation."
|
||||
]
|
||||
Listen ->
|
||||
command 1 long . doc "Listen for a message from other actors." $
|
||||
command 1 long . doc (Set.singleton $ Query $ Sensing RobotSensing) "Listen for a message from other actors." $
|
||||
[ "It will take the first message said by the closest actor."
|
||||
, "You do not need to actively listen for the message to be logged though, "
|
||||
<> "that is done automatically once you have a listening device equipped."
|
||||
, "Note that you can see the messages either in your logger device or the message panel."
|
||||
]
|
||||
Log -> command 1 short "Log the string in the robot's logger."
|
||||
Log -> command 1 short $ shortDoc (Set.singleton $ Mutation LogEmission) "Log the string in the robot's logger."
|
||||
View ->
|
||||
command 1 short . doc "View the given actor." $
|
||||
command 1 short . doc (Set.singleton $ Query $ Sensing RobotSensing) "View the given actor." $
|
||||
[ "This will recenter the map on the target robot and allow its inventory and logs to be inspected."
|
||||
]
|
||||
Appear ->
|
||||
command 1 short . doc "Set how the robot is displayed." $
|
||||
command 1 short . doc (Set.singleton $ Mutation Cosmetic) "Set how the robot is displayed." $
|
||||
[ "You can either specify one character or five (for each direction)."
|
||||
, "The default is \"X^>v<\"."
|
||||
]
|
||||
Create ->
|
||||
command 1 short . doc "Create an item out of thin air." $
|
||||
command 1 short . doc (Set.fromList [Mutation EntityChange, Mutation $ RobotChange InventoryChange]) "Create an item out of thin air." $
|
||||
["Only available in creative mode."]
|
||||
Halt -> command 1 short "Tell a robot to halt."
|
||||
Time -> command 0 Intangible "Get the current time."
|
||||
Halt -> command 1 short $ shortDoc (Set.singleton $ Mutation $ RobotChange BehaviorChange) "Tell a robot to halt."
|
||||
Time ->
|
||||
command 0 Intangible $
|
||||
shortDoc
|
||||
(Set.singleton $ Query $ Sensing WorldCondition)
|
||||
"Get the current time."
|
||||
Scout ->
|
||||
command 1 short . doc "Detect whether a robot is within line-of-sight in a direction." $
|
||||
command 1 short . doc (Set.singleton $ Query $ Sensing RobotSensing) "Detect whether a robot is within line-of-sight in a direction." $
|
||||
[ "Perception is blocked by 'Opaque' entities."
|
||||
, T.unwords ["Has a max range of", T.pack $ show maxScoutRange, "units."]
|
||||
]
|
||||
Whereami -> command 0 Intangible "Get the current x and y coordinates."
|
||||
Whereami ->
|
||||
command 0 Intangible $
|
||||
shortDoc
|
||||
(Set.singleton $ Query $ Sensing RobotSensing)
|
||||
"Get the current x and y coordinates."
|
||||
Waypoint ->
|
||||
command 2 Intangible . doc "Get the x, y coordinates of a named waypoint, by index" $
|
||||
command 2 Intangible . doc (Set.singleton $ Query APriori) "Get the x, y coordinates of a named waypoint, by index" $
|
||||
[ "Return only the waypoints in the same subworld as the calling robot."
|
||||
, "Since waypoint names can have plural multiplicity, returns a tuple of (count, (x, y))."
|
||||
, "The supplied index will be wrapped automatically, modulo the waypoint count."
|
||||
, "A robot can use the count to know whether they have iterated over the full waypoint circuit."
|
||||
]
|
||||
Structure ->
|
||||
command 2 Intangible . doc "Get the x, y coordinates of the southwest corner of a constructed structure, by name and index" $
|
||||
command 2 Intangible . doc (Set.singleton $ Query $ Sensing EntitySensing) "Get the x, y coordinates of the southwest corner of a constructed structure, by name and index" $
|
||||
[ "The outermost type of the return value indicates whether any structure of such name exists."
|
||||
, "Since structures can have multiple occurrences, returns a tuple of (count, (x, y))."
|
||||
, "The supplied index will be wrapped automatically, modulo the structure count."
|
||||
, "A robot can use the count to know whether they have iterated over the full structure list."
|
||||
]
|
||||
Floorplan ->
|
||||
command 1 Intangible . doc "Get the dimensions of a structure template" $
|
||||
command 1 Intangible . doc (Set.singleton $ Query APriori) "Get the dimensions of a structure template" $
|
||||
[ "Returns a tuple of (width, height) for the structure of the requested name."
|
||||
, "Yields an error if the supplied string is not the name of a structure."
|
||||
]
|
||||
HasTag ->
|
||||
command 2 Intangible . doc "Check whether the given entity has the given tag" $
|
||||
command 2 Intangible . doc (Set.singleton $ Query APriori) "Check whether the given entity has the given tag" $
|
||||
[ "Returns true if the first argument is an entity that is labeled by the tag in the second argument."
|
||||
, "Yields an error if the first argument is not a valid entity."
|
||||
]
|
||||
TagMembers ->
|
||||
command 2 Intangible . doc "Get the entities labeled by a tag, by alphabetical index" $
|
||||
command 2 Intangible . doc (Set.singleton $ Query APriori) "Get the entities labeled by a tag, by alphabetical index" $
|
||||
[ "Returns a tuple of (member count, entity)."
|
||||
, "The supplied index will be wrapped automatically, modulo the member count."
|
||||
, "A robot can use the count to know whether they have iterated over the full list."
|
||||
]
|
||||
Detect ->
|
||||
command 2 Intangible . doc "Detect an entity within a rectangle." $
|
||||
command 2 Intangible . doc (Set.singleton $ Query $ Sensing EntitySensing) "Detect an entity within a rectangle." $
|
||||
["Locate the closest instance of a given entity within the rectangle specified by opposite corners, relative to the current location."]
|
||||
Resonate ->
|
||||
command 2 Intangible . doc "Count specific entities within a rectangle." $
|
||||
command 2 Intangible . doc (Set.singleton $ Query $ Sensing EntitySensing) "Count specific entities within a rectangle." $
|
||||
[ "Applies a strong magnetic field over a given area and stimulates the matter within, generating a non-directional radio signal. A receiver tuned to the resonant frequency of the target entity is able to measure its quantity."
|
||||
, "Counts the entities within the rectangle specified by opposite corners, relative to the current location."
|
||||
]
|
||||
Density ->
|
||||
command 1 Intangible . doc "Count all entities within a rectangle." $
|
||||
command 1 Intangible . doc (Set.singleton $ Query $ Sensing EntitySensing) "Count all entities within a rectangle." $
|
||||
[ "Applies a strong magnetic field over a given area and stimulates the matter within, generating a non-directional radio signal. A receiver measured the signal intensity to measure the quantity."
|
||||
, "Counts the entities within the rectangle specified by opposite corners, relative to the current location."
|
||||
]
|
||||
Sniff ->
|
||||
command 1 short . doc "Determine distance to entity." $
|
||||
command 1 short . doc (Set.singleton $ Query $ Sensing EntitySensing) "Determine distance to entity." $
|
||||
[ "Measures concentration of airborne particles to infer distance to a certain kind of entity."
|
||||
, "If none is detected, returns (-1)."
|
||||
, T.unwords ["Has a max range of", T.pack $ show maxSniffRange, "units."]
|
||||
]
|
||||
Chirp ->
|
||||
command 1 short . doc "Determine direction to entity." $
|
||||
command 1 short . doc (Set.singleton $ Query $ Sensing EntitySensing) "Determine direction to entity." $
|
||||
[ "Uses a directional sonic emitter and microphone tuned to the acoustic signature of a specific entity to determine its direction."
|
||||
, "Returns 'down' if out of range or the direction is indeterminate."
|
||||
, "Provides absolute directions if \"compass\" equipped, relative directions otherwise."
|
||||
, T.unwords ["Has a max range of", T.pack $ show maxSniffRange, "units."]
|
||||
]
|
||||
Watch ->
|
||||
command 1 short . doc "Interrupt `wait` upon location changes." $
|
||||
command 1 short . doc (Set.singleton $ Query $ Sensing EntitySensing) "Interrupt `wait` upon location changes." $
|
||||
[ "Place seismic detectors to alert upon entity changes to the specified location."
|
||||
, "Supply a direction, as with the `scan` command, to specify a nearby location."
|
||||
, "Can be invoked more than once until the next `wait` command, at which time the only the registered locations that are currently nearby are preserved."
|
||||
, "Any change to entities at the monitored locations will cause the robot to wake up before the `wait` timeout."
|
||||
]
|
||||
Surveil ->
|
||||
command 1 short . doc "Interrupt `wait` upon (remote) location changes." $
|
||||
command 1 short . doc (Set.singleton $ Query $ Sensing EntitySensing) "Interrupt `wait` upon (remote) location changes." $
|
||||
[ "Like `watch`, but with no restriction on distance."
|
||||
]
|
||||
Heading -> command 0 Intangible "Get the current heading."
|
||||
Blocked -> command 0 Intangible "See if the robot can move forward."
|
||||
Heading -> command 0 Intangible $ shortDoc (Set.singleton $ Query $ Sensing RobotSensing) "Get the current heading."
|
||||
Blocked -> command 0 Intangible $ shortDoc (Set.singleton $ Query $ Sensing EntitySensing) "See if the robot can move forward."
|
||||
Scan ->
|
||||
command 1 Intangible . doc "Scan a nearby location for entities." $
|
||||
command 1 Intangible . doc (Set.singleton $ Query $ Sensing EntitySensing) "Scan a nearby location for entities." $
|
||||
[ "Adds the entity (not actor) to your inventory with count 0 if there is any."
|
||||
, "If you can use sum types, you can also inspect the result directly."
|
||||
]
|
||||
Upload -> command 1 short "Upload a robot's known entities and log to another robot."
|
||||
Ishere -> command 1 Intangible "See if a specific entity is in the current location."
|
||||
Upload -> command 1 short $ shortDoc (Set.singleton $ Mutation $ RobotChange BehaviorChange) "Upload a robot's known entities and log to another robot."
|
||||
Ishere -> command 1 Intangible $ shortDoc (Set.singleton $ Query $ Sensing EntitySensing) "See if a specific entity is in the current location."
|
||||
Isempty ->
|
||||
command 0 Intangible . doc "Check if the current location is empty." $
|
||||
command 0 Intangible . doc (Set.singleton $ Query $ Sensing EntitySensing) "Check if the current location is empty." $
|
||||
[ "Detects whether or not the current location contains an entity."
|
||||
, "Does not detect robots or other actors."
|
||||
]
|
||||
Self -> function 0 "Get a reference to the current robot."
|
||||
Parent -> function 0 "Get a reference to the robot's parent."
|
||||
Base -> function 0 "Get a reference to the base."
|
||||
Meet -> command 0 Intangible "Get a reference to a nearby actor, if there is one."
|
||||
MeetAll -> command 0 long "Run a command for each nearby actor."
|
||||
Whoami -> command 0 Intangible "Get the robot's display name."
|
||||
Setname -> command 1 short "Set the robot's display name."
|
||||
Self -> function 0 $ shortDoc (Set.singleton $ Query APriori) "Get a reference to the current robot."
|
||||
Parent -> function 0 $ shortDoc (Set.singleton $ Query APriori) "Get a reference to the robot's parent."
|
||||
Base -> function 0 $ shortDoc (Set.singleton $ Query APriori) "Get a reference to the base."
|
||||
Meet -> command 0 Intangible $ shortDoc (Set.singleton $ Query $ Sensing RobotSensing) "Get a reference to a nearby actor, if there is one."
|
||||
MeetAll -> command 0 long $ shortDoc (Set.fromList [Mutation $ RobotChange BehaviorChange, Query $ Sensing RobotSensing]) "Run a command for each nearby actor."
|
||||
Whoami -> command 0 Intangible $ shortDoc (Set.singleton $ Query $ Sensing RobotSensing) "Get the robot's display name."
|
||||
Setname -> command 1 short $ shortDoc (Set.singleton $ Mutation $ RobotChange BehaviorChange) "Set the robot's display name."
|
||||
Random ->
|
||||
command 1 Intangible . doc "Get a uniformly random integer." $
|
||||
command 1 Intangible . doc (Set.singleton $ Query PRNG) "Get a uniformly random integer." $
|
||||
["The random integer will be chosen from the range 0 to n-1, exclusive of the argument."]
|
||||
Run -> command 1 long "Run a program loaded from a file."
|
||||
Return -> command 1 Intangible "Make the value a result in `cmd`."
|
||||
Try -> command 2 Intangible "Execute a command, catching errors."
|
||||
Undefined -> function 0 "A value of any type, that is evaluated as error."
|
||||
Fail -> function 1 "A value of any type, that is evaluated as error with message."
|
||||
Run -> command 1 long $ shortDoc (Set.singleton $ Mutation $ RobotChange BehaviorChange) "Run a program loaded from a file."
|
||||
Return -> command 1 Intangible $ shortDoc Set.empty "Make the value a result in `cmd`."
|
||||
Try -> command 2 Intangible $ shortDoc Set.empty "Execute a command, catching errors."
|
||||
Undefined -> function 0 $ shortDoc Set.empty "A value of any type, that is evaluated as error."
|
||||
Fail -> function 1 $ shortDoc Set.empty "A value of any type, that is evaluated as error with message."
|
||||
If ->
|
||||
function 3 . doc "If-Then-Else function." $
|
||||
function 3 . doc Set.empty "If-Then-Else function." $
|
||||
["If the bool predicate is true then evaluate the first expression, otherwise the second."]
|
||||
Inl -> function 1 "Put the value into the left component of a sum type."
|
||||
Inr -> function 1 "Put the value into the right component of a sum type."
|
||||
Case -> function 3 "Evaluate one of the given functions on a value of sum type."
|
||||
Fst -> function 1 "Get the first value of a pair."
|
||||
Snd -> function 1 "Get the second value of a pair."
|
||||
Force -> function 1 "Force the evaluation of a delayed value."
|
||||
Not -> function 1 "Negate the boolean value."
|
||||
Neg -> unaryOp "-" 7 P "Negate the given integer value."
|
||||
Add -> binaryOp "+" 6 L "Add the given integer values."
|
||||
And -> binaryOp "&&" 3 R "Logical and (true if both values are true)."
|
||||
Or -> binaryOp "||" 2 R "Logical or (true if either value is true)."
|
||||
Sub -> binaryOp "-" 6 L "Subtract the given integer values."
|
||||
Mul -> binaryOp "*" 7 L "Multiply the given integer values."
|
||||
Div -> binaryOp "/" 7 L "Divide the left integer value by the right one, rounding down."
|
||||
Exp -> binaryOp "^" 8 R "Raise the left integer value to the power of the right one."
|
||||
Eq -> binaryOp "==" 4 N "Check that the left value is equal to the right one."
|
||||
Neq -> binaryOp "!=" 4 N "Check that the left value is not equal to the right one."
|
||||
Lt -> binaryOp "<" 4 N "Check that the left value is lesser than the right one."
|
||||
Gt -> binaryOp ">" 4 N "Check that the left value is greater than the right one."
|
||||
Leq -> binaryOp "<=" 4 N "Check that the left value is lesser or equal to the right one."
|
||||
Geq -> binaryOp ">=" 4 N "Check that the left value is greater or equal to the right one."
|
||||
Format -> function 1 "Turn an arbitrary value into a string."
|
||||
Concat -> binaryOp "++" 6 R "Concatenate the given strings."
|
||||
Chars -> function 1 "Counts the number of characters in the text."
|
||||
Inl -> function 1 $ shortDoc Set.empty "Put the value into the left component of a sum type."
|
||||
Inr -> function 1 $ shortDoc Set.empty "Put the value into the right component of a sum type."
|
||||
Case -> function 3 $ shortDoc Set.empty "Evaluate one of the given functions on a value of sum type."
|
||||
Fst -> function 1 $ shortDoc Set.empty "Get the first value of a pair."
|
||||
Snd -> function 1 $ shortDoc Set.empty "Get the second value of a pair."
|
||||
Force -> function 1 $ shortDoc Set.empty "Force the evaluation of a delayed value."
|
||||
Not -> function 1 $ shortDoc Set.empty "Negate the boolean value."
|
||||
Neg -> unaryOp "-" 7 P $ shortDoc Set.empty "Negate the given integer value."
|
||||
Add -> binaryOp "+" 6 L $ shortDoc Set.empty "Add the given integer values."
|
||||
And -> binaryOp "&&" 3 R $ shortDoc Set.empty "Logical and (true if both values are true)."
|
||||
Or -> binaryOp "||" 2 R $ shortDoc Set.empty "Logical or (true if either value is true)."
|
||||
Sub -> binaryOp "-" 6 L $ shortDoc Set.empty "Subtract the given integer values."
|
||||
Mul -> binaryOp "*" 7 L $ shortDoc Set.empty "Multiply the given integer values."
|
||||
Div -> binaryOp "/" 7 L $ shortDoc Set.empty "Divide the left integer value by the right one, rounding down."
|
||||
Exp -> binaryOp "^" 8 R $ shortDoc Set.empty "Raise the left integer value to the power of the right one."
|
||||
Eq -> binaryOp "==" 4 N $ shortDoc Set.empty "Check that the left value is equal to the right one."
|
||||
Neq -> binaryOp "!=" 4 N $ shortDoc Set.empty "Check that the left value is not equal to the right one."
|
||||
Lt -> binaryOp "<" 4 N $ shortDoc Set.empty "Check that the left value is lesser than the right one."
|
||||
Gt -> binaryOp ">" 4 N $ shortDoc Set.empty "Check that the left value is greater than the right one."
|
||||
Leq -> binaryOp "<=" 4 N $ shortDoc Set.empty "Check that the left value is lesser or equal to the right one."
|
||||
Geq -> binaryOp ">=" 4 N $ shortDoc Set.empty "Check that the left value is greater or equal to the right one."
|
||||
Format -> function 1 $ shortDoc Set.empty "Turn an arbitrary value into a string."
|
||||
Concat -> binaryOp "++" 6 R $ shortDoc Set.empty "Concatenate the given strings."
|
||||
Chars -> function 1 $ shortDoc Set.empty "Counts the number of characters in the text."
|
||||
Split ->
|
||||
function 2 . doc "Split the text into two at given position." $
|
||||
function 2 . doc Set.empty "Split the text into two at given position." $
|
||||
[ "To be more specific, the following holds for all `text` values `s1` and `s2`:"
|
||||
, "`(s1,s2) == split (chars s1) (s1 ++ s2)`"
|
||||
, "So split can be used to undo concatenation if you know the length of the original string."
|
||||
]
|
||||
CharAt ->
|
||||
function 2 . doc "Get the character at a given index." $
|
||||
function 2 . doc Set.empty "Get the character at a given index." $
|
||||
[ "Gets the character (as an `int` representing a Unicode codepoint) at a specific index in a `text` value. Valid indices are 0 through `chars t - 1`."
|
||||
, "Throws an exception if given an out-of-bounds index."
|
||||
]
|
||||
ToChar ->
|
||||
function 1 . doc "Create a singleton `text` value from the given character code." $
|
||||
function 1 . doc Set.empty "Create a singleton `text` value from the given character code." $
|
||||
[ "That is, `chars (toChar c) == 1` and `charAt 0 (toChar c) == c`."
|
||||
]
|
||||
AppF ->
|
||||
binaryOp "$" 0 R . doc "Apply the function on the left to the value on the right." $
|
||||
binaryOp "$" 0 R . doc Set.empty "Apply the function on the left to the value on the right." $
|
||||
[ "This operator is useful to avoid nesting parentheses."
|
||||
, "For exaple:"
|
||||
, "`f $ g $ h x = f (g (h x))`"
|
||||
]
|
||||
Swap ->
|
||||
command 1 short . doc "Swap placed entity with one in inventory." $
|
||||
command 1 short . doc (Set.fromList [Mutation EntityChange, Mutation $ RobotChange InventoryChange]) "Swap placed entity with one in inventory." $
|
||||
[ "This essentially works like atomic grab and place."
|
||||
, "Use this to avoid race conditions where more robots grab, scan or place in one location."
|
||||
]
|
||||
Atomic ->
|
||||
command 1 Intangible . doc "Execute a block of commands atomically." $
|
||||
command 1 Intangible . doc (Set.singleton MetaEffect) "Execute a block of commands atomically." $
|
||||
[ "When executing `atomic c`, a robot will not be interrupted, that is, no other robots will execute any commands while the robot is executing @c@."
|
||||
]
|
||||
Instant ->
|
||||
command 1 Intangible . doc "Execute a block of commands instantly." $
|
||||
command 1 Intangible . doc (Set.singleton MetaEffect) "Execute a block of commands instantly." $
|
||||
[ "Like `atomic`, but with no restriction on program size."
|
||||
]
|
||||
Key ->
|
||||
function 1 . doc "Create a key value from a text description." $
|
||||
function 1 . doc Set.empty "Create a key value from a text description." $
|
||||
[ "The key description can optionally start with modifiers like 'C-', 'M-', 'A-', or 'S-', followed by either a regular key, or a special key name like 'Down' or 'End'"
|
||||
, "For example, 'M-C-x', 'Down', or 'S-4'."
|
||||
, "Which key combinations are actually possible to type may vary by keyboard and terminal program."
|
||||
]
|
||||
InstallKeyHandler ->
|
||||
command 2 Intangible . doc "Install a keyboard input handler." $
|
||||
command 2 Intangible . doc (Set.singleton $ Mutation $ RobotChange BehaviorChange) "Install a keyboard input handler." $
|
||||
[ "The first argument is a hint line that will be displayed when the input handler is active."
|
||||
, "The second argument is a function to handle keyboard inputs."
|
||||
]
|
||||
Teleport -> command 2 short "Teleport a robot to the given location."
|
||||
As -> command 2 Intangible "Hypothetically run a command as if you were another robot."
|
||||
RobotNamed -> command 1 Intangible "Find an actor by name."
|
||||
RobotNumbered -> command 1 Intangible "Find an actor by number."
|
||||
Knows -> command 1 Intangible "Check if the robot knows about an entity."
|
||||
Teleport -> command 2 short $ shortDoc (Set.singleton $ Mutation $ RobotChange PositionChange) "Teleport a robot to the given location."
|
||||
As -> command 2 Intangible $ shortDoc (Set.singleton $ Mutation $ RobotChange BehaviorChange) "Hypothetically run a command as if you were another robot."
|
||||
RobotNamed -> command 1 Intangible $ shortDoc (Set.singleton $ Query $ Sensing RobotSensing) "Find an actor by name."
|
||||
RobotNumbered -> command 1 Intangible $ shortDoc (Set.singleton $ Query $ Sensing RobotSensing) "Find an actor by number."
|
||||
Knows -> command 1 Intangible $ shortDoc (Set.singleton $ Query $ Sensing RobotSensing) "Check if the robot knows about an entity."
|
||||
where
|
||||
doc b ls = ConstDoc b (T.unlines ls)
|
||||
doc e b ls = ConstDoc e b (T.unlines ls)
|
||||
shortDoc e b = ConstDoc e b ""
|
||||
unaryOp s p side d =
|
||||
ConstInfo
|
||||
{ syntax = s
|
||||
|
|
43
src/swarm-lang/Swarm/Language/Syntax/CommandMetadata.hs
Normal file
43
src/swarm-lang/Swarm/Language/Syntax/CommandMetadata.hs
Normal file
|
@ -0,0 +1,43 @@
|
|||
-- |
|
||||
-- SPDX-License-Identifier: BSD-3-Clause
|
||||
--
|
||||
-- Command metadata for documentation
|
||||
module Swarm.Language.Syntax.CommandMetadata where
|
||||
|
||||
import Data.Aeson (ToJSON)
|
||||
import GHC.Generics (Generic)
|
||||
|
||||
data SensingType
|
||||
= RobotSensing
|
||||
| EntitySensing
|
||||
| WorldCondition
|
||||
deriving (Eq, Ord, Enum, Bounded, Show, Generic, ToJSON)
|
||||
|
||||
data QueryType
|
||||
= -- | empirical knowledge
|
||||
Sensing SensingType
|
||||
| -- | The random number generator
|
||||
PRNG
|
||||
| -- | a priori knowledge
|
||||
APriori
|
||||
deriving (Eq, Ord, Show, Generic, ToJSON)
|
||||
|
||||
data RobotChangeType
|
||||
= PositionChange
|
||||
| InventoryChange
|
||||
| ExistenceChange
|
||||
| BehaviorChange
|
||||
deriving (Eq, Ord, Enum, Bounded, Show, Generic, ToJSON)
|
||||
|
||||
data MutationType
|
||||
= Cosmetic
|
||||
| LogEmission
|
||||
| EntityChange
|
||||
| RobotChange RobotChangeType
|
||||
deriving (Eq, Ord, Show, Generic, ToJSON)
|
||||
|
||||
data CommandEffect
|
||||
= Query QueryType
|
||||
| MetaEffect
|
||||
| Mutation MutationType
|
||||
deriving (Eq, Ord, Show, Generic, ToJSON)
|
|
@ -63,6 +63,7 @@ import Servant
|
|||
import Servant.Docs (ToCapture)
|
||||
import Servant.Docs qualified as SD
|
||||
import Servant.Docs.Internal qualified as SD (renderCurlBasePath)
|
||||
import Swarm.Doc.Command
|
||||
import Swarm.Game.Robot
|
||||
import Swarm.Game.Scenario.Objective
|
||||
import Swarm.Game.Scenario.Objective.Graph
|
||||
|
@ -109,6 +110,7 @@ type SwarmAPI =
|
|||
:<|> "code" :> "render" :> ReqBody '[PlainText] T.Text :> Post '[PlainText] T.Text
|
||||
:<|> "code" :> "run" :> ReqBody '[PlainText] T.Text :> Post '[PlainText] T.Text
|
||||
:<|> "paths" :> "log" :> Get '[JSON] (RingBuffer CacheLogEntry)
|
||||
:<|> "commands" :> Get '[JSON] CommandCatalog
|
||||
:<|> "repl" :> "history" :> "full" :> Get '[JSON] [REPLHistItem]
|
||||
:<|> "map" :> Capture "size" AreaDimensions :> Get '[JSON] GridResponse
|
||||
|
||||
|
@ -164,6 +166,7 @@ mkApp state events =
|
|||
:<|> codeRenderHandler
|
||||
:<|> codeRunHandler events
|
||||
:<|> pathsLogHandler state
|
||||
:<|> cmdMatrixHandler state
|
||||
:<|> replHandler state
|
||||
:<|> mapViewHandler state
|
||||
|
||||
|
@ -241,6 +244,9 @@ pathsLogHandler appStateRef = do
|
|||
appState <- liftIO (readIORef appStateRef)
|
||||
pure $ appState ^. gameState . pathCaching . pathCachingLog
|
||||
|
||||
cmdMatrixHandler :: ReadableIORef AppState -> Handler CommandCatalog
|
||||
cmdMatrixHandler _ = pure getCatalog
|
||||
|
||||
replHandler :: ReadableIORef AppState -> Handler [REPLHistItem]
|
||||
replHandler appStateRef = do
|
||||
appState <- liftIO (readIORef appStateRef)
|
||||
|
|
|
@ -115,6 +115,7 @@ library swarm-lang
|
|||
Swarm.Language.Pretty
|
||||
Swarm.Language.Requirement
|
||||
Swarm.Language.Syntax
|
||||
Swarm.Language.Syntax.CommandMetadata
|
||||
Swarm.Language.Text.Markdown
|
||||
Swarm.Language.Typecheck
|
||||
Swarm.Language.Typecheck.Unify
|
||||
|
@ -422,7 +423,8 @@ library swarm-util
|
|||
|
||||
library
|
||||
import: stan-config, common, ghc2021-extensions
|
||||
exposed-modules: Swarm.Doc.Keyword
|
||||
exposed-modules: Swarm.Doc.Command
|
||||
Swarm.Doc.Keyword
|
||||
Swarm.Doc.Pedagogy
|
||||
Swarm.Doc.Util
|
||||
Swarm.TUI.Border
|
||||
|
@ -566,6 +568,7 @@ library
|
|||
, Swarm.Language.Pretty
|
||||
, Swarm.Language.Requirement
|
||||
, Swarm.Language.Syntax
|
||||
, Swarm.Language.Syntax.CommandMetadata
|
||||
, Swarm.Language.Text.Markdown
|
||||
, Swarm.Language.Typecheck
|
||||
, Swarm.Language.Typecheck.Unify
|
||||
|
@ -675,6 +678,8 @@ executable swarm-docs
|
|||
Swarm.Doc.Schema.Render
|
||||
Swarm.Doc.Schema.SchemaType
|
||||
Swarm.Doc.Wiki.Cheatsheet
|
||||
Swarm.Doc.Wiki.Matrix
|
||||
Swarm.Doc.Wiki.Util
|
||||
build-depends: optparse-applicative >= 0.16 && < 0.19,
|
||||
dotgen >= 0.4 && < 0.5,
|
||||
pandoc >= 3.0 && < 3.2,
|
||||
|
|
37
web/command-matrix.html
Normal file
37
web/command-matrix.html
Normal file
|
@ -0,0 +1,37 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Command matrix</title>
|
||||
|
||||
<link rel="stylesheet" href="style/tablesort.css"/>
|
||||
<link rel="stylesheet" href="style/command-matrix.css"/>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/tablesort/5.1.0/tablesort.min.js"></script>
|
||||
<script src="script/command-matrix.js"></script>
|
||||
|
||||
<script>
|
||||
window.onload=()=>{
|
||||
const tableElement = document.querySelector("table");
|
||||
doFetch(tableElement);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<table id="my-table">
|
||||
<thead>
|
||||
<tr data-sort-method="none">
|
||||
<th style="color: gray">Command</th>
|
||||
<th>Explicit robot target</th>
|
||||
<th>Pure computation</th>
|
||||
<th>Modifies environment</th>
|
||||
<th>Modifies robot</th>
|
||||
<th>Moves robot</th>
|
||||
<th>Returns value</th>
|
||||
<!-- <th>Output type</th> -->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="my-table-body">
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
63
web/script/command-matrix.js
Normal file
63
web/script/command-matrix.js
Normal file
|
@ -0,0 +1,63 @@
|
|||
function mkLink(text, url) {
|
||||
const anchor = document.createElement("a");
|
||||
anchor.href = url
|
||||
anchor.textContent = text;
|
||||
return anchor;
|
||||
}
|
||||
|
||||
function insertTableRows(myTableBody, entries) {
|
||||
for (const entry of entries) {
|
||||
const rowItem = document.createElement("tr");
|
||||
|
||||
const fieldVals = [
|
||||
entry.derivedAttrs.hasActorTarget,
|
||||
entry.derivedAttrs.pureComputation,
|
||||
entry.derivedAttrs.modifiesEnvironment,
|
||||
entry.derivedAttrs.modifiesRobot,
|
||||
entry.derivedAttrs.movesRobot,
|
||||
entry.derivedAttrs.returnsValue,
|
||||
]
|
||||
|
||||
const cellVals = [
|
||||
mkLink(entry.cmd, "https://github.com/swarm-game/swarm/wiki/Commands-Cheat-Sheet#" + entry.cmd),
|
||||
];
|
||||
|
||||
for (const val of fieldVals) {
|
||||
const span = document.createElement("span");
|
||||
span.className = val ? "trueValue" : "falseValue";
|
||||
span.appendChild(document.createTextNode(val));
|
||||
cellVals.push(span);
|
||||
}
|
||||
|
||||
// cellVals.push(document.createTextNode(entry.derivedAttrs.outputType));
|
||||
|
||||
for (const val of cellVals) {
|
||||
const cellElement = document.createElement("td");
|
||||
cellElement.appendChild(val);
|
||||
rowItem.append(cellElement);
|
||||
}
|
||||
|
||||
myTableBody.appendChild(rowItem);
|
||||
}
|
||||
}
|
||||
|
||||
function doFetch(myTable) {
|
||||
fetch("commands")
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error, status = ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then((data) => {
|
||||
const myTableBody = myTable.querySelector("tbody");
|
||||
insertTableRows(myTableBody, data.entries);
|
||||
// Documentation: http://tristen.ca/tablesort/demo/
|
||||
new Tablesort(document.getElementById('my-table'));
|
||||
})
|
||||
.catch((error) => {
|
||||
const p = document.createElement("p");
|
||||
p.appendChild(document.createTextNode(`Error: ${error.message}`));
|
||||
document.body.insertBefore(p, myTable);
|
||||
});
|
||||
}
|
34
web/style/command-matrix.css
Normal file
34
web/style/command-matrix.css
Normal file
|
@ -0,0 +1,34 @@
|
|||
body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.trueValue {
|
||||
color: #00a000;
|
||||
}
|
||||
|
||||
.falseValue {
|
||||
color: #a00000;
|
||||
}
|
||||
|
||||
a:link {
|
||||
color: lavender;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* visited link */
|
||||
a:visited {
|
||||
color: lightblue;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* mouse over link */
|
||||
a:hover {
|
||||
color: hotpink;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* selected link */
|
||||
a:active {
|
||||
color: blue;
|
||||
text-decoration: underline;
|
||||
}
|
48
web/style/tablesort.css
Normal file
48
web/style/tablesort.css
Normal file
|
@ -0,0 +1,48 @@
|
|||
body {
|
||||
color: white;
|
||||
background-color: #212121;
|
||||
}
|
||||
|
||||
table {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 0.25em;
|
||||
}
|
||||
|
||||
th[role=columnheader]:not(.no-sort) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
th[role=columnheader]:not(.no-sort):after {
|
||||
content: '';
|
||||
float: right;
|
||||
margin-top: 7px;
|
||||
margin-left: 5px;
|
||||
border-width: 0 4px 4px;
|
||||
border-style: solid;
|
||||
border-color: white transparent;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
-ms-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
th[aria-sort=ascending]:not(.no-sort):after {
|
||||
border-bottom: none;
|
||||
border-width: 4px 4px 0;
|
||||
}
|
||||
|
||||
th[aria-sort]:not(.no-sort):after {
|
||||
visibility: visible;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
th[role=columnheader]:not(.no-sort):hover:after {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
Loading…
Reference in New Issue
Block a user