diff --git a/data/scenarios/Challenges/2048.yaml b/data/scenarios/Challenges/2048.yaml index e4ea5b982..78b140b1f 100644 --- a/data/scenarios/Challenges/2048.yaml +++ b/data/scenarios/Challenges/2048.yaml @@ -5,10 +5,10 @@ description: Make 2048! objectives: - goal: - OK, OK, it's not really the same as the classic "2048" game. - However, your goal is still to make 2048! You start with a `1` + However, your goal is still to make 2048! You start with a `1`{=entity} which regrows immediately when - it is harvested, so if you plant it, you can get as many as you want. Your - job is to combine `1`s in order to make a `2048` + it is `harvest`ed, so if you plant it, you can get as many as you want. Your + job is to combine `1`{=entity}s in order to make a `2048`{=entity} entity. - "Hint: the `format` command can turn numbers into strings!" condition: | diff --git a/data/scenarios/Challenges/Mazes/easy_cave_maze.yaml b/data/scenarios/Challenges/Mazes/easy_cave_maze.yaml index 94794a54c..42667376b 100644 --- a/data/scenarios/Challenges/Mazes/easy_cave_maze.yaml +++ b/data/scenarios/Challenges/Mazes/easy_cave_maze.yaml @@ -7,7 +7,7 @@ objectives: - You are at the top of a cave that gradually descends until reaching a dead end. - At its bottom is a great treasure. - Luckily, the cave does not branch out, so it is easy to find the path to the treasure. - - Send a robot to the the item marked as '!'. You win once the robot grabs it. + - Send a robot to the the item marked as '**!**'. You win once the robot `grab`s it. condition: | j <- robotNamed "judge"; as j {has "goal"} diff --git a/data/scenarios/Challenges/Ranching/capture.yaml b/data/scenarios/Challenges/Ranching/capture.yaml index 5740be713..bc0d541f4 100644 --- a/data/scenarios/Challenges/Ranching/capture.yaml +++ b/data/scenarios/Challenges/Ranching/capture.yaml @@ -15,7 +15,7 @@ objectives: goal: - | This porcine pest has been running amok. - Block the pig by a "monolith" on all four sides to capture it. + Block the pig by a `monolith`{=entity} on all four sides to capture it. - Blocking on fewer than four sides will spook the pig, and he will escape! condition: | def isSurrounded = \n. diff --git a/data/scenarios/Challenges/Ranching/gated-paddock.yaml b/data/scenarios/Challenges/Ranching/gated-paddock.yaml index 6e375469a..78dafddbb 100644 --- a/data/scenarios/Challenges/Ranching/gated-paddock.yaml +++ b/data/scenarios/Challenges/Ranching/gated-paddock.yaml @@ -12,14 +12,14 @@ objectives: You've homesteaded on a small island in the ocean. It's time to gather resources to trade. - | - You encounter some feral sheep (@). They slowly wander the island and eat grass. + You encounter some feral sheep (**@**). They slowly wander the island and eat grass. Your mind wanders to textiles... - | First, paddock at least one sheep so they don't drown. Make sure there are no gaps in the fence! - | - Note that you can use the "drill" command (by way of the "post puller" tool) - to demolish a fence that has been "placed". + Note that you can use the `drill` command (by way of the `post puller`{=entity} tool) + to demolish a `fence`{=entity} that has been `place`d. condition: |- // Algorithm: // ---------- @@ -287,7 +287,7 @@ objectives: The sheep will move toward something edible on an adjacent tile and will eat it if they walk over it. - | - You may want to add a gate to the fence + You may want to add a `gate`{=entity} to the fence to give yourself easier access. condition: |- def getTruthForSheepIndex = \predicateCmd. \i. @@ -317,11 +317,12 @@ objectives: anySheep (has "clover") 3; prerequisite: enclose_sheep - - goal: + - teaser: Knit sweater + goal: - | - Yum! Contented, well-fed sheep may drop wool. + Yum! Contented, well-fed sheep may drop `wool`{=entity}. - | - Winter is coming! Collect three wool bundles to make a sweater. + Winter is coming! Collect three wool bundles to make a `sweater`{=entity}. - | Each sheep drops a finite amount over their lifetime. diff --git a/data/scenarios/Challenges/Ranching/powerset.yaml b/data/scenarios/Challenges/Ranching/powerset.yaml index 538e84214..14160ca3d 100644 --- a/data/scenarios/Challenges/Ranching/powerset.yaml +++ b/data/scenarios/Challenges/Ranching/powerset.yaml @@ -32,7 +32,7 @@ objectives: However, his experiment is incomplete! He has forgotten one combination. - | Place the missing hybrid combination in the empty eastern-most column. - After you have done this, `place` the "bell" anywhere, and then Bill will inspect + After you have done this, `place` the `bell`{=entity} anywhere, and then Bill will inspect your work. prerequisite: not: wrong_anwser diff --git a/data/scenarios/Challenges/Sliding Puzzles/3x3.yaml b/data/scenarios/Challenges/Sliding Puzzles/3x3.yaml index 8758f2f9a..8261342c7 100644 --- a/data/scenarios/Challenges/Sliding Puzzles/3x3.yaml +++ b/data/scenarios/Challenges/Sliding Puzzles/3x3.yaml @@ -23,7 +23,7 @@ objectives: - | Or, if you prefer, `drill` a tile to cause it to slide into the adjacent empty space. However, you must not drill a tile that - has nowhere to slide. Also, drilling consumes "ink", which will be replenished + has nowhere to slide. Also, drilling consumes `ink`{=entity}, which will be replenished after the sliding operation is complete, so avoid drilling too fast in succession. condition: | diff --git a/data/scenarios/Challenges/Sokoban/Gadgets/no-reverse.yaml b/data/scenarios/Challenges/Sokoban/Gadgets/no-reverse.yaml index 101545b6f..ab98ed0a7 100644 --- a/data/scenarios/Challenges/Sokoban/Gadgets/no-reverse.yaml +++ b/data/scenarios/Challenges/Sokoban/Gadgets/no-reverse.yaml @@ -7,7 +7,7 @@ creative: false seed: 0 objectives: - goal: - - Grab the flower. + - Grab the `flower`{=entity}. condition: | as base { has "flower"; diff --git a/data/scenarios/Challenges/Sokoban/Gadgets/one-way.yaml b/data/scenarios/Challenges/Sokoban/Gadgets/one-way.yaml index 0ef32e6ff..712ebd54c 100644 --- a/data/scenarios/Challenges/Sokoban/Gadgets/one-way.yaml +++ b/data/scenarios/Challenges/Sokoban/Gadgets/one-way.yaml @@ -7,7 +7,7 @@ creative: false seed: 0 objectives: - goal: - - Grab the flower. + - Grab the `flower`{=entity}. condition: | as base { has "flower"; diff --git a/data/scenarios/Challenges/Sokoban/Simple/trapdoor.yaml b/data/scenarios/Challenges/Sokoban/Simple/trapdoor.yaml index c2ad7d1ad..999f09e01 100644 --- a/data/scenarios/Challenges/Sokoban/Simple/trapdoor.yaml +++ b/data/scenarios/Challenges/Sokoban/Simple/trapdoor.yaml @@ -12,7 +12,7 @@ creative: false seed: 0 objectives: - goal: - - Place the flower on the target. + - Place the `flower`{=entity} on the target. - You may have to start over if you get stuck. condition: | as base { diff --git a/data/scenarios/Challenges/Sokoban/foresight.yaml b/data/scenarios/Challenges/Sokoban/foresight.yaml index dc54b682e..88095d9d8 100644 --- a/data/scenarios/Challenges/Sokoban/foresight.yaml +++ b/data/scenarios/Challenges/Sokoban/foresight.yaml @@ -13,7 +13,7 @@ attrs: fg: "#bbbbff" objectives: - goal: - - Push a monolith onto the base's initial location. + - Push a `monolith`{=entity} onto the base's initial location. condition: | as base { teleport self (0,0); @@ -23,7 +23,7 @@ objectives: hidden: true optional: true goal: - - Grab the flower + - Grab the `flower`{=entity} condition: | as base { has "flower"; diff --git a/data/scenarios/Challenges/arbitrage.yaml b/data/scenarios/Challenges/arbitrage.yaml index d5a671d03..c5abd85d8 100644 --- a/data/scenarios/Challenges/arbitrage.yaml +++ b/data/scenarios/Challenges/arbitrage.yaml @@ -16,7 +16,7 @@ objectives: As an itinerant merchant, you may exploit market asymmetry for profit. - | - Amass a fortune of 100 paperclips. + Amass a fortune of 100 `paperclip`{=entity}s. condition: | as base { pcount <- count "paperclip"; diff --git a/data/scenarios/Challenges/blender.yaml b/data/scenarios/Challenges/blender.yaml index bf452a418..7284caed1 100644 --- a/data/scenarios/Challenges/blender.yaml +++ b/data/scenarios/Challenges/blender.yaml @@ -8,10 +8,10 @@ objectives: - teaser: Get amulet goal: - | - `grab` the Amulet of Yoneda from the northwest sanctum while - timing your passage carefully to avoid Side Effects (X) on patrol. + `grab` the `Amulet of Yoneda`{=entity} from the northwest sanctum while + timing your passage carefully to avoid Side Effects (**X**) on patrol. - | - To unlock a red door, `drill` it with the "door key" equipped. + To unlock a red door, `drill` it with the `door key`{=entity} equipped. condition: | as base {has "Amulet of Yoneda"} prerequisite: diff --git a/data/scenarios/Challenges/bridge-building.yaml b/data/scenarios/Challenges/bridge-building.yaml index 7a972b531..5e81c6cb6 100644 --- a/data/scenarios/Challenges/bridge-building.yaml +++ b/data/scenarios/Challenges/bridge-building.yaml @@ -55,7 +55,7 @@ objectives: - id: hammer_time teaser: Hammer time goal: - - Produce an obsidian shard. + - Produce an `obsidian shard`{=entity}. optional: true hidden: true condition: | @@ -87,14 +87,14 @@ objectives: - id: get_map teaser: Get the map goal: - - As a humble peat farmer, you subsist in a simple cabin by the bog. + - As a humble `peat`{=entity} farmer, you subsist in a simple cabin by the `bog`{=entity}. Though long content with this ascetic lifestyle, recently the barren walls have left you restless. Something is missing... - "The majestic landscape that is your back yard is insufficient to distract you---not - even the ferocious, lava-spewing volcano little more than a stone's throw from + even the ferocious, `lava`{=entity}-spewing volcano little more than a stone's throw from your porch. You are preoccupied by one task: to find the perfect household decoration." - - First, grab a map to orient yourself. + - First, grab a `map`{=entity} to orient yourself. condition: | as base { has "map"; @@ -103,18 +103,18 @@ objectives: teaser: Find the temple prerequisite: get_map goal: - - You study the map. - - Glacier-bound mountains tower in the east, - a volcano oozes a river of lava to the north, and beyond that lies a mountain lake, punctuated with islands + - You study the `map`{=entity}. + - Glacier-bound mountains tower in the `east`, + a volcano oozes a river of `lava`{=entity} to the `north`, and beyond that lies a mountain lake, punctuated with islands in the northwest. Iron mines penetrate the base of the volcano. They could be useful, but how will you get there? - - A jungle abuts the volcano, ensconcing an ancient ruin. + - A `jungle`{=entity} abuts the volcano, ensconcing an ancient ruin. The map notes that bygone travelers have stashed tools among the northeasterly mountains to blaze a path through the jungle. - - Your only neighbor, a hemp farmer to the northwest, has evacuated since the sudden volcanic eruption. - - A disused quarry and clay pit flanks your cabin to the west, as does the familiar, swampy bog to the east. - A highly-prized flower is said to grow in the caves beyond the bog. - - Your mind is made up. You will pillage the ruins for treasure! Head to the ruins and "scan" them. + - Your only neighbor, a `hemp`{=entity} farmer to the northwest, has evacuated since the sudden volcanic eruption. + - A disused quarry and clay pit flanks your cabin to the `west`, as does the familiar, swampy `bog`{=entity} to the `east`. + A highly-prized `flower`{=entity} is said to grow in the caves beyond the bog. + - Your mind is made up. You will pillage the ruins for treasure! Head to the ruins and `scan` them. Ingenuity and endurance are your allies as you forge paths through varied obstacles. Study your "recipes" for clues! condition: | @@ -126,8 +126,8 @@ objectives: prerequisite: find_temple goal: - "A note on the door says:" - - '"Greetings, intrepid traveler. Encircle this temple with the rare "flower" of the southeastern - caves, and the treasure of this temple shall be revealed."' + - '"Greetings, intrepid traveler. Encircle this temple with the rare `flower`{=entity} of the southeastern + caves, and the treasure of this `temple`{=entity} shall be revealed."' - Plant a ring of flowers around the jungle temple. condition: | as base { diff --git a/data/scenarios/Challenges/bucket-brigade.yaml b/data/scenarios/Challenges/bucket-brigade.yaml index 30e11f48b..9ccd2b54a 100644 --- a/data/scenarios/Challenges/bucket-brigade.yaml +++ b/data/scenarios/Challenges/bucket-brigade.yaml @@ -57,14 +57,14 @@ objectives: - id: deliver_coal_lump teaser: Get coal to base goal: - - Deliver a "coal lump" to the base. + - Deliver a `coal lump`{=entity} to the base. - | - To excavate coal from the "lignite mine" (M), a robot needs to - `drill` while in posession of a "bucketwheel excavator". + To excavate coal from the `lignite mine`{=entity} (**M**), a robot needs to + `drill` while in possession of a `bucketwheel excavator`{=entity}. - | To assemble the excavator, you'll need to repurpose - some "treads". - Unfortunately, you have only one set of "treads". + some `treads`{=entity}. + Unfortunately, you have only one set of `treads`{=entity}. You'll have to make do... condition: | as base {has "coal lump"} diff --git a/data/scenarios/Challenges/chess_horse.yaml b/data/scenarios/Challenges/chess_horse.yaml index 95b06cb61..f3bbaf677 100644 --- a/data/scenarios/Challenges/chess_horse.yaml +++ b/data/scenarios/Challenges/chess_horse.yaml @@ -4,8 +4,8 @@ author: Ondřej Šebek description: In this quirky challenge, you move as the chess knight piece. Can you capture the enemy king? objectives: - goal: - - Robots can use the 'move' command to move. - But they only 'turn' in cardinal directions. + - Robots can use the `move` command to move. + But they only `turn` in cardinal directions. - You are special. You are a knight. - Go forth and capture the King! condition: | diff --git a/data/scenarios/Challenges/gopher.yaml b/data/scenarios/Challenges/gopher.yaml index 5601d4eb5..fb4e81ac9 100644 --- a/data/scenarios/Challenges/gopher.yaml +++ b/data/scenarios/Challenges/gopher.yaml @@ -10,12 +10,12 @@ objectives: teaser: Defeat gopher goal: - | - A gopher (G) is defiling your immaculate garden! + A gopher (**G**) is defiling your immaculate garden! - | - He will burrow (o) underground awhile, then pop up + He will burrow (**o**) underground awhile, then pop up anywhere within the rectangular grassy region - to gloat atop his "mound" of dirt for a short time. - `drill` the "mound" while he sits to drive him + to gloat atop his `mound`{=entity} of dirt for a short time. + `drill` the `mound`{=entity} while he sits to drive him away. Eventually you should wear down his resolve! condition: | try { diff --git a/data/scenarios/Challenges/hackman.yaml b/data/scenarios/Challenges/hackman.yaml index eb45d73f3..1e3ca95cb 100644 --- a/data/scenarios/Challenges/hackman.yaml +++ b/data/scenarios/Challenges/hackman.yaml @@ -19,10 +19,10 @@ objectives: - teaser: Get pellets goal: - | - Pick up all of the caffeine "pellets" so that Hackman can write more code. + Pick up all of the caffeine `pellet`{=entity}s so that Hackman can write more code. - | Pay no mind to the colorful ghosts meandering about. That is, unless - you are feeling generous with your "donuts"... + you are feeling generous with your `donut`{=entity}s... - | Can you find all of the secret objectives? condition: | diff --git a/data/scenarios/Challenges/ice-cream.yaml b/data/scenarios/Challenges/ice-cream.yaml index c9bb1e454..c1ca97d8f 100644 --- a/data/scenarios/Challenges/ice-cream.yaml +++ b/data/scenarios/Challenges/ice-cream.yaml @@ -11,15 +11,15 @@ objectives: goal: - | Congratulations on the grand opening of your new ice cream shop. - You have advertised: "All you can eat, for 1 bitcoin!" + You have advertised: "All you can eat, for 1 `bitcoin`{=entity}!" - | - A customer is approaching the "Counter". They look hungry! + A customer is approaching the `Counter`{=entity}. They look hungry! - "..." - | Customer: "`give` me a cone, and then I'll tell you how many scoops I want." - "..." - | - Oh dear, you've forgotten to stock your shop with a "calculator". + Oh dear, you've forgotten to provision your shop with a `calculator`{=entity}. Let's hope this order is simple. condition: | customer <- robotnamed "customer"; diff --git a/data/scenarios/Challenges/maypole.yaml b/data/scenarios/Challenges/maypole.yaml index d6e976447..b9df97090 100644 --- a/data/scenarios/Challenges/maypole.yaml +++ b/data/scenarios/Challenges/maypole.yaml @@ -19,7 +19,7 @@ objectives: teaser: Around you go! goal: - | - Go around the maypole several times counter-clockwise. + Go around the `maypole`{=entity} several times counter-clockwise. condition: | monitor <- robotnamed "monitor"; as monitor {has "dizzy"}; diff --git a/data/scenarios/Challenges/teleport.yaml b/data/scenarios/Challenges/teleport.yaml index 1fa88724d..8d39909d8 100644 --- a/data/scenarios/Challenges/teleport.yaml +++ b/data/scenarios/Challenges/teleport.yaml @@ -4,7 +4,7 @@ author: Ondřej Šebek description: An impossible challenge - can you magically jump across the water? objectives: - goal: - - Get to the other room and grab the lambda. + - Get to the other room and grab the `lambda`{=entity}. - Oh wait. - The tunnel is flooded. - Just give up then. It is impossible to get there. diff --git a/data/scenarios/Challenges/wolf-goat-cabbage.yaml b/data/scenarios/Challenges/wolf-goat-cabbage.yaml index dd7d8a752..8e888d116 100644 --- a/data/scenarios/Challenges/wolf-goat-cabbage.yaml +++ b/data/scenarios/Challenges/wolf-goat-cabbage.yaml @@ -40,11 +40,11 @@ robots: objectives: - goal: - | - Ferry all three of the wolf, goat, and cabbage across the lake. + Ferry all three of the `wolf`{=entity}, `goat`{=entity}, and `cabbage`{=entity} across the lake. However, only one of these can be carried at a time. - | - Furthermore, if left unattended together, the wolf will eat the goat, - or the goat will eat the cabbage. + Furthermore, if left unattended together, the `wolf`{=entity} will eat the `goat`{=entity}, + or the `goat`{=entity} will eat the `cabbage`{=entity}. condition: | run "data/scenarios/Challenges/_wolf-goat-cabbage/together-on-east-bank.sw"; prerequisite: diff --git a/data/scenarios/Challenges/word-search.yaml b/data/scenarios/Challenges/word-search.yaml index 01e1afffd..8b272f802 100644 --- a/data/scenarios/Challenges/word-search.yaml +++ b/data/scenarios/Challenges/word-search.yaml @@ -7,9 +7,9 @@ seed: 2 creative: false objectives: - goal: - - Use the `drill` command (e.g. "drill down" when on top of the + - Use the `drill` command (e.g. `drill down` when on top of the intended letter) to mark the sequence of letters that - spells COW within the designated playfield. + spells `C`{=entity}`O`{=entity}`W`{=entity} within the designated playfield. - | The sequence may appear horizontally in either the leftward or rightward direction, diff --git a/data/scenarios/Speedruns/curry.yaml b/data/scenarios/Speedruns/curry.yaml index 9e7c89e8e..5e7cd0157 100644 --- a/data/scenarios/Speedruns/curry.yaml +++ b/data/scenarios/Speedruns/curry.yaml @@ -1,7 +1,7 @@ version: 1 name: Curry author: Brent Yorgey -description: Race to make a bowl of curry as quickly as possible. +description: Race to make a bowl of `curry`{=entity} as quickly as possible. See the Swarm wiki for more information on Swarm speedrunning. objectives: - goal: diff --git a/data/scenarios/Speedruns/forester.yaml b/data/scenarios/Speedruns/forester.yaml index 1698aa467..d15459f0d 100644 --- a/data/scenarios/Speedruns/forester.yaml +++ b/data/scenarios/Speedruns/forester.yaml @@ -1,11 +1,11 @@ version: 1 name: Forester author: Brent Yorgey -description: Race to harvest 1024 trees as quickly as possible. +description: Race to harvest 1024 `tree`{=entity}s as quickly as possible. See the Swarm wiki for more information on Swarm speedrunning. objectives: - goal: - - Harvest 1024 trees as quickly as possible! + - Harvest 1024 `tree`{=entity}s as quickly as possible! condition: as base {n <- count "tree"; return (n >= 1024)} robots: - name: base diff --git a/data/scenarios/Speedruns/mithril.yaml b/data/scenarios/Speedruns/mithril.yaml index c9d24fa0e..d2d03fd7e 100644 --- a/data/scenarios/Speedruns/mithril.yaml +++ b/data/scenarios/Speedruns/mithril.yaml @@ -1,11 +1,11 @@ version: 1 name: Mithril author: Brent Yorgey -description: Race to mine some mithril. +description: Race to mine some `mithril`{=entity}. See the Swarm wiki for more information on Swarm speedrunning. objectives: - goal: - - Mine some mithril as quickly as possible! + - Mine some `mithril`{=entity} as quickly as possible! condition: as base {has "mithril"} robots: - name: base diff --git a/data/scenarios/Tutorials/world101.sw b/data/scenarios/Tutorials/world101.sw new file mode 100644 index 000000000..9d0bde51d --- /dev/null +++ b/data/scenarios/Tutorials/world101.sw @@ -0,0 +1,36 @@ +def tB = turn back end +def tR = turn right end +def tL = turn left end + +def m = move end +def m2 = m;m end +def m4 = m2;m2 end +def m8 = m4;m4 end +def m9 = m8;m end +def m10 = m8;m2 end + +def mg = m; grab end + +def get_3_trees : cmd unit = + tB; m; mg; mg; mg; tB; m4 +end + +def make_harvester : cmd unit = + make "log"; make "log"; make "log"; + make "board"; make "board"; make "board"; + make "box"; + make "wooden gear"; make "wooden gear"; + make "harvester" +end + +def get_lambda : cmd unit = + m10; tR; m9; harvest; tB; m9; tL; m10 +end + +def solution : cmd unit = + build {get_3_trees}; wait 16; salvage; + make_harvester; + build {get_lambda}; wait 50; salvage +end; + +solution diff --git a/data/scenarios/Tutorials/world101.yaml b/data/scenarios/Tutorials/world101.yaml index 8326a9883..00435a7ee 100644 --- a/data/scenarios/Tutorials/world101.yaml +++ b/data/scenarios/Tutorials/world101.yaml @@ -31,16 +31,16 @@ objectives: - id: get_harvester teaser: Make a harvester goal: - - Nice work! Now, use the trees to make a harvester device. + - Nice work! Now, use the `tree`{=entity}s to make a `harvester`{=entity} device. This will require several intermediate products; try making - various things, and take a look at your available recipes (F3) + various things, and take a look at your available recipes (**F3**) and at the recipes listed for items in your inventory. Of course, you may end up needing some additional trees. condition: | try { as base {has "harvester"} } {return false} prerequisite: get_trees - goal: - - Now that you have a harvester, you can use `harvest` instead of `grab` + - Now that you have a `harvester`{=entity}, you can use `harvest` instead of `grab` whenever you pick up a growing item (check for the word "growing" at the top of the item description), to leave behind a seed that will regrow. - "**TIP:** since you only have a single harvester device for now, whenever you @@ -56,6 +56,8 @@ objectives: condition: | try { as base {has "lambda"} } {return false} prerequisite: get_harvester +solution: | + run "scenarios/Tutorials/world101.sw" robots: - name: base display: diff --git a/src/Swarm/TUI/View.hs b/src/Swarm/TUI/View.hs index 1fcea7245..3b0be5fcd 100644 --- a/src/Swarm/TUI/View.hs +++ b/src/Swarm/TUI/View.hs @@ -887,8 +887,7 @@ colorLogs e = case e ^. leSource of Critical -> redAttr where -- color each robot message with different color of the world - robotColor rid = worldAttributeNames !! (rid `mod` fgColLen) - fgColLen = length worldAttributeNames + robotColor = indexWrapNonEmpty worldAttributeNames -- | Draw the F-key modal menu. This is displayed in the top left world corner. drawModalMenu :: AppState -> Widget Name diff --git a/src/Swarm/TUI/View/Attribute/Attr.hs b/src/Swarm/TUI/View/Attribute/Attr.hs index 42427e06a..03ff96071 100644 --- a/src/Swarm/TUI/View/Attribute/Attr.hs +++ b/src/Swarm/TUI/View/Attribute/Attr.hs @@ -54,7 +54,7 @@ import Brick.Forms import Brick.Widgets.Dialog import Brick.Widgets.Edit qualified as E import Brick.Widgets.List hiding (reverse) -import Data.Bifunctor (bimap) +import Data.Bifunctor (bimap, first) import Data.Colour.Palette.BrewerSet import Data.List.NonEmpty (NonEmpty (..)) import Data.List.NonEmpty qualified as NE @@ -78,7 +78,7 @@ swarmAttrMap = attrMap V.defAttr $ NE.toList activityMeterAttributes - <> worldAttributes + <> NE.toList (NE.map (first getWorldAttrName) worldAttributes) <> [(waterAttr, V.white `on` V.blue)] <> terrainAttr <> [ -- Robot attribute @@ -107,42 +107,59 @@ swarmAttrMap = (defAttr, V.defAttr) ] -entityAttr :: AttrName -entityAttr = fst $ head worldAttributes - worldPrefix :: AttrName worldPrefix = attrName "world" +-- | We introduce this (module-private) newtype +-- so that we can define the 'entity' attribute +-- separate from the list of other 'worldAttributes', +-- while enforcing the convention that both its attribute +-- name and the rest of 'worldAttributes' be consistently +-- prefixed by 'worldPrefix'. +newtype WorldAttr = WorldAttr + { getWorldAttrName :: AttrName + } + +mkWorldAttr :: String -> WorldAttr +mkWorldAttr = WorldAttr . (worldPrefix <>) . attrName + +entity :: (WorldAttr, V.Attr) +entity = (mkWorldAttr "entity", fg V.white) + +entityAttr :: AttrName +entityAttr = getWorldAttrName $ fst entity + -- | Colors of entities in the world. -- -- Also used to color messages, so water is special and excluded. -worldAttributes :: [(AttrName, V.Attr)] +worldAttributes :: NonEmpty (WorldAttr, V.Attr) worldAttributes = - bimap ((worldPrefix <>) . attrName) fg - <$> [ ("entity", V.white) - , ("device", V.brightYellow) - , ("plant", V.green) - , ("rock", V.rgbColor @Int 80 80 80) - , ("wood", V.rgbColor @Int 139 69 19) - , ("flower", V.rgbColor @Int 200 0 200) - , ("rubber", V.rgbColor @Int 245 224 179) - , ("copper", V.yellow) - , ("copper'", V.rgbColor @Int 78 117 102) - , ("iron", V.rgbColor @Int 97 102 106) - , ("iron'", V.rgbColor @Int 183 65 14) - , ("quartz", V.white) - , ("silver", V.rgbColor @Int 192 192 192) - , ("gold", V.rgbColor @Int 255 215 0) - , ("snow", V.white) - , ("sand", V.rgbColor @Int 194 178 128) - , ("fire", V.brightRed) - , ("red", V.red) - , ("green", V.green) - , ("blue", V.blue) - ] - -worldAttributeNames :: [AttrName] -worldAttributeNames = map fst worldAttributes + entity + :| map + (bimap mkWorldAttr fg) + [ ("device", V.brightYellow) + , ("plant", V.green) + , ("rock", V.rgbColor @Int 80 80 80) + , ("wood", V.rgbColor @Int 139 69 19) + , ("flower", V.rgbColor @Int 200 0 200) + , ("rubber", V.rgbColor @Int 245 224 179) + , ("copper", V.yellow) + , ("copper'", V.rgbColor @Int 78 117 102) + , ("iron", V.rgbColor @Int 97 102 106) + , ("iron'", V.rgbColor @Int 183 65 14) + , ("quartz", V.white) + , ("silver", V.rgbColor @Int 192 192 192) + , ("gold", V.rgbColor @Int 255 215 0) + , ("snow", V.white) + , ("sand", V.rgbColor @Int 194 178 128) + , ("fire", V.brightRed) + , ("red", V.red) + , ("green", V.green) + , ("blue", V.blue) + ] + +worldAttributeNames :: NonEmpty AttrName +worldAttributeNames = NE.map (getWorldAttrName . fst) worldAttributes activityMeterPrefix :: AttrName activityMeterPrefix = attrName "activityMeter" diff --git a/test/integration/Main.hs b/test/integration/Main.hs index a4303ffcb..fe939dc0c 100644 --- a/test/integration/Main.hs +++ b/test/integration/Main.hs @@ -199,6 +199,7 @@ testScenarioSolutions rs ui = , testTutorialSolution Default "Tutorials/require" , testTutorialSolution (Sec 3) "Tutorials/requireinv" , testTutorialSolution Default "Tutorials/conditionals" + , testTutorialSolution Default "Tutorials/world101" , testTutorialSolution (Sec 5) "Tutorials/farming" ] , testGroup