From 55ca86071aad759ee11f061dce98bead707f2175 Mon Sep 17 00:00:00 2001 From: Brent Yorgey Date: Thu, 19 Dec 2024 13:53:47 -0600 Subject: [PATCH 1/7] add `print` command, and update `read` to parse text starting with "paper:" --- data/entities.yaml | 3 ++- src/swarm-engine/Swarm/Game/Step/Const.hs | 9 +++++++++ src/swarm-lang/Swarm/Language/Parser/Value.hs | 7 ++++++- src/swarm-lang/Swarm/Language/Syntax/Constants.hs | 10 ++++++++++ src/swarm-lang/Swarm/Language/Typecheck.hs | 1 + test/unit/TestEval.hs | 7 +++++++ 6 files changed, 35 insertions(+), 2 deletions(-) diff --git a/data/entities.yaml b/data/entities.yaml index f8a164280..89401922f 100644 --- a/data/entities.yaml +++ b/data/entities.yaml @@ -884,8 +884,9 @@ attr: device char: 'Д' description: - - A typewriter is used to inscribe symbols on paper, thus reifying pure, platonic information into a physical form. + - A typewriter is used to inscribe symbols on `paper`{=entity}, thus reifying pure, platonic information into a physical form. properties: [pickable] + capabilities: [print] - name: 3D printer display: attr: device diff --git a/src/swarm-engine/Swarm/Game/Step/Const.hs b/src/swarm-engine/Swarm/Game/Step/Const.hs index e7bf82c7e..e7c3e596b 100644 --- a/src/swarm-engine/Swarm/Game/Step/Const.hs +++ b/src/swarm-engine/Swarm/Game/Step/Const.hs @@ -1214,6 +1214,15 @@ execConst runChildProg c vs s k = do Nothing -> raise Read ["Could not read", showT txt, "at type", prettyText ty] Just v -> return (mkReturn v) _ -> badConst + Print -> case vs of + -- TODO: limit amount of text on one paper? + [VText txt] -> do + paper <- ensureItem "paper" "print" + let newEntityName = "paper: " <> txt + robotInventory %= delete paper + robotInventory %= insert (paper & entityName .~ newEntityName) + return $ mkReturn newEntityName + _ -> badConst Chars -> case vs of [VText t] -> return $ mkReturn $ T.length t _ -> badConst diff --git a/src/swarm-lang/Swarm/Language/Parser/Value.hs b/src/swarm-lang/Swarm/Language/Parser/Value.hs index f9be8aa7c..107454208 100644 --- a/src/swarm-lang/Swarm/Language/Parser/Value.hs +++ b/src/swarm-lang/Swarm/Language/Parser/Value.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE OverloadedStrings #-} + -- | -- SPDX-License-Identifier: BSD-3-Clause -- @@ -6,9 +8,11 @@ -- of the proper type. module Swarm.Language.Parser.Value (readValue) where +import Control.Applicative ((<|>)) import Control.Lens ((^.)) import Data.Either.Extra (eitherToMaybe) import Data.Text (Text) +import Data.Text qualified as T import Swarm.Language.Context qualified as Ctx import Swarm.Language.Key (parseKeyComboFull) import Swarm.Language.Parser (readNonemptyTerm) @@ -20,7 +24,8 @@ import Text.Megaparsec qualified as MP readValue :: Type -> Text -> Maybe Value readValue ty txt = do - s <- eitherToMaybe $ readNonemptyTerm txt + txt' <- T.stripPrefix "paper:" txt <|> pure txt + s <- eitherToMaybe $ readNonemptyTerm txt' _ <- eitherToMaybe $ checkTop Ctx.empty Ctx.empty Ctx.empty s ty toValue $ s ^. sTerm diff --git a/src/swarm-lang/Swarm/Language/Syntax/Constants.hs b/src/swarm-lang/Swarm/Language/Syntax/Constants.hs index 6d15f7cc9..099736c49 100644 --- a/src/swarm-lang/Swarm/Language/Syntax/Constants.hs +++ b/src/swarm-lang/Swarm/Language/Syntax/Constants.hs @@ -273,6 +273,8 @@ data Const Format | -- | Try to turn a string into a value Read + | -- | Print a string onto a piece of paper + Print | -- | Concatenate string values Concat | -- | Count number of characters. @@ -816,6 +818,14 @@ constInfo c = case c of 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." Read -> function 2 $ shortDoc Set.empty "Try to read a string into a value of the expected type." + Print -> + command 1 short . doc (Set.singleton $ Mutation $ RobotChange InventoryChange) + "Print text onto a piece of paper." $ + [ "Consumes one `paper` entity from your inventory, and produces an entity" + , "whose name is \"paper: \" concatenated with the given text." + , "In conjunction with `format`, this can be used to print values onto paper" + , "and give them to other robots, which can reconstitute the values with `read`." + ] 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 -> diff --git a/src/swarm-lang/Swarm/Language/Typecheck.hs b/src/swarm-lang/Swarm/Language/Typecheck.hs index 49c7bda68..4307e37e4 100644 --- a/src/swarm-lang/Swarm/Language/Typecheck.hs +++ b/src/swarm-lang/Swarm/Language/Typecheck.hs @@ -1100,6 +1100,7 @@ inferConst c = run . runReader @TVCtx Ctx.empty . quantify $ case c of Exp -> arithBinT Format -> [tyQ| a -> Text |] Read -> [tyQ| Text -> a |] + Print -> [tyQ| Text -> Cmd Text |] Concat -> [tyQ| Text -> Text -> Text |] Chars -> [tyQ| Text -> Int |] Split -> [tyQ| Int -> Text -> (Text * Text) |] diff --git a/test/unit/TestEval.hs b/test/unit/TestEval.hs index 34164a7fd..75898ad60 100644 --- a/test/unit/TestEval.hs +++ b/test/unit/TestEval.hs @@ -383,6 +383,13 @@ testEval g = ( "read \"inr (3, inr (5, inl ()))\" : rec l. Unit + (Int * l)" `evaluatesToV` [3 :: Integer, 5] ) + , testCase + "read paper with int" + ("read \"paper: 52\" : Int" `evaluatesToV` (52 :: Integer)) + , testCase + "read paper with tuple" + ("read \"paper: (3, false, ())\" : Int * Bool * Unit" + `evaluatesToV` (3 :: Integer, (False, ()))) ] , testGroup "records - #1093" From 7166703daf9989f9733fcfbad817d1d17e7bf1b3 Mon Sep 17 00:00:00 2001 From: Brent Yorgey Date: Fri, 27 Dec 2024 08:25:10 -0600 Subject: [PATCH 2/7] add `erase` command --- data/entities.yaml | 2 +- src/swarm-engine/Swarm/Game/Step/Const.hs | 16 +++++++++++++++- .../Swarm/Language/Syntax/Constants.hs | 9 +++++++++ src/swarm-lang/Swarm/Language/Typecheck.hs | 1 + 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/data/entities.yaml b/data/entities.yaml index 89401922f..6c29dc212 100644 --- a/data/entities.yaml +++ b/data/entities.yaml @@ -886,7 +886,7 @@ description: - A typewriter is used to inscribe symbols on `paper`{=entity}, thus reifying pure, platonic information into a physical form. properties: [pickable] - capabilities: [print] + capabilities: [print, erase] - name: 3D printer display: attr: device diff --git a/src/swarm-engine/Swarm/Game/Step/Const.hs b/src/swarm-engine/Swarm/Game/Step/Const.hs index e7c3e596b..e9b42d4b5 100644 --- a/src/swarm-engine/Swarm/Game/Step/Const.hs +++ b/src/swarm-engine/Swarm/Game/Step/Const.hs @@ -1215,7 +1215,6 @@ execConst runChildProg c vs s k = do Just v -> return (mkReturn v) _ -> badConst Print -> case vs of - -- TODO: limit amount of text on one paper? [VText txt] -> do paper <- ensureItem "paper" "print" let newEntityName = "paper: " <> txt @@ -1223,6 +1222,21 @@ execConst runChildProg c vs s k = do robotInventory %= insert (paper & entityName .~ newEntityName) return $ mkReturn newEntityName _ -> badConst + Erase -> case vs of + [VText paperName] -> do + toErase <- ensureItem paperName "erase" + (paperName /= "paper") + `holdsOrFail` ["That is already blank!"] + ("paper: " `T.isPrefixOf` paperName) + `holdsOrFail` ["Can only erase paper, not", indefinite paperName <> "."] + em <- use $ landscape . terrainAndEntities . entityMap + paper <- lookupEntityName "paper" em + `isJustOrFail` ["Can't erase, I don't know what paper is."] + + robotInventory %= delete toErase + robotInventory %= insert paper + return $ mkReturn () + _ -> badConst Chars -> case vs of [VText t] -> return $ mkReturn $ T.length t _ -> badConst diff --git a/src/swarm-lang/Swarm/Language/Syntax/Constants.hs b/src/swarm-lang/Swarm/Language/Syntax/Constants.hs index 099736c49..99c09520b 100644 --- a/src/swarm-lang/Swarm/Language/Syntax/Constants.hs +++ b/src/swarm-lang/Swarm/Language/Syntax/Constants.hs @@ -275,6 +275,8 @@ data Const Read | -- | Print a string onto a piece of paper Print + | -- | Erase a piece of paper + Erase | -- | Concatenate string values Concat | -- | Count number of characters. @@ -826,6 +828,13 @@ constInfo c = case c of , "In conjunction with `format`, this can be used to print values onto paper" , "and give them to other robots, which can reconstitute the values with `read`." ] + Erase -> + command 1 short . doc (Set.singleton $ Mutation $ RobotChange InventoryChange) + "Erase a piece of paper." $ + [ "Consumes the named entity from your inventory, whose name must begin with" + , "\"paper: \", and produces a \"paper\" entity. This can be used to undo" + , "the effects of a `print` command." + ] 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 -> diff --git a/src/swarm-lang/Swarm/Language/Typecheck.hs b/src/swarm-lang/Swarm/Language/Typecheck.hs index 4307e37e4..3ba7ca88e 100644 --- a/src/swarm-lang/Swarm/Language/Typecheck.hs +++ b/src/swarm-lang/Swarm/Language/Typecheck.hs @@ -1101,6 +1101,7 @@ inferConst c = run . runReader @TVCtx Ctx.empty . quantify $ case c of Format -> [tyQ| a -> Text |] Read -> [tyQ| Text -> a |] Print -> [tyQ| Text -> Cmd Text |] + Erase -> [tyQ| Text -> Cmd Unit |] Concat -> [tyQ| Text -> Text -> Text |] Chars -> [tyQ| Text -> Int |] Split -> [tyQ| Int -> Text -> (Text * Text) |] From 923da8aa16d52ca45e36dfc505e5eadb2ca6f932 Mon Sep 17 00:00:00 2001 From: Brent Yorgey Date: Fri, 27 Dec 2024 08:58:15 -0600 Subject: [PATCH 3/7] telephone scenario --- data/scenarios/Challenges/00-ORDER.txt | 3 +- data/scenarios/Challenges/_telephone/judge.sw | 80 +++++++ .../Challenges/_telephone/photocopier.sw | 34 +++ .../Challenges/_telephone/shuttle.sw | 47 ++++ .../Challenges/_telephone/solution.sw | 77 +++++++ data/scenarios/Challenges/telephone.yaml | 210 ++++++++++++++++++ 6 files changed, 450 insertions(+), 1 deletion(-) create mode 100644 data/scenarios/Challenges/_telephone/judge.sw create mode 100644 data/scenarios/Challenges/_telephone/photocopier.sw create mode 100644 data/scenarios/Challenges/_telephone/shuttle.sw create mode 100644 data/scenarios/Challenges/_telephone/solution.sw create mode 100644 data/scenarios/Challenges/telephone.yaml diff --git a/data/scenarios/Challenges/00-ORDER.txt b/data/scenarios/Challenges/00-ORDER.txt index c1c1f6c3b..453eee71d 100644 --- a/data/scenarios/Challenges/00-ORDER.txt +++ b/data/scenarios/Challenges/00-ORDER.txt @@ -20,7 +20,8 @@ dna.yaml friend.yaml pack-tetrominoes.yaml dimsum.yaml +telephone.yaml Mazes Ranching Sokoban -Sliding Puzzles \ No newline at end of file +Sliding Puzzles diff --git a/data/scenarios/Challenges/_telephone/judge.sw b/data/scenarios/Challenges/_telephone/judge.sw new file mode 100644 index 000000000..5f5c734fd --- /dev/null +++ b/data/scenarios/Challenges/_telephone/judge.sw @@ -0,0 +1,80 @@ +def forever: ∀ a b. {Cmd a} -> Cmd b = \c. force c; forever c end + +def x : Int -> Cmd a -> Cmd Unit = \n. \c. + if (n == 0) {} {c; x (n-1) c} +end + +def andC : Cmd Bool -> Cmd Bool -> Cmd Bool = \c1. \c2. + b1 <- c1; + if b1 {c2} {return false} +end + +tydef List a = rec l. Unit + a * l end + +def for : Int -> (Int -> Cmd a) -> Cmd (List a) = \n. \k. + if (n == 0) + { return $ inl () } + { x <- k (n-1); + xs <- for (n-1) k; + return (inr (x,xs)) + } +end + +def readRow : Cmd (List (Unit + Text)) = + r <- for 8 (\_. s <- scan down; move; return s); + turn back; x 8 move; turn right; move; turn right; + return r +end + +tydef Rect = List (List (Unit + Text)) end + +def readRect : Cmd Rect = + lst <- for 4 (\_. readRow); + turn right; x 4 move; turn left; + return lst +end + +def checkCell : Unit + Text -> Cmd Bool = \pat. + actual <- scan down; + move; + return (actual == pat) +end + +def checkRow : List (Unit + Text) -> Cmd Bool = \row. + case row + (\_. turn back; x 8 move; turn right; move; turn right; return true) + (\cons. andC (checkCell (fst cons)) (checkRow (snd cons))) +end + +def checkRect : Rect -> Cmd Bool = \rect. + case rect + (\_. return true) + (\cons. andC (checkRow (fst cons)) (checkRect (snd cons))) +end + +def check : Rect -> Cmd Unit = \rect. + log "check!"; + origLoc <- whereami; + teleport self (93, -8); + b <- checkRect rect; + if b {create "X"} {}; + teleport self origLoc; turn east; +end + +def judge = + instant ( + loc <- whereami; + for 4 (\y. + for 8 (\x. + surveil (fst loc + x, snd loc + y) + ) + ); + ); + wait 1024; + instant ( + rect <- readRect; + check rect; + ) +end + +forever {judge}; diff --git a/data/scenarios/Challenges/_telephone/photocopier.sw b/data/scenarios/Challenges/_telephone/photocopier.sw new file mode 100644 index 000000000..7683a95ce --- /dev/null +++ b/data/scenarios/Challenges/_telephone/photocopier.sw @@ -0,0 +1,34 @@ +def forever: ∀ a b. {Cmd a} -> Cmd b = \c. force c; forever c end + +def X : Int -> Cmd Unit -> Cmd Unit = \n. \c. + if (n == 0) {} {c; X (n-1) c} +end + +def pixel : (Int * Int) * Text -> Cmd Unit = \instr. + let loc = fst instr in + let x = fst loc in + let y = snd loc in + let ty = snd instr in + turn back; X 5 move; turn right; X 2 move; + turn west; X x move; turn north; X y move; + place ty; + turn south; X y move; turn east; X x move; + X 5 move; turn right; X 2 move; turn east +end + +def followInstructions : Text -> Cmd Unit = \paper. + try { + let res = (read paper : ((Int * Int) * Text)) + in pixel res + } {} +end + +def copy : Cmd Unit = + watch down; wait 1024; + p <- atomic (b <- isempty; if b {return ""} {grab}); + if (p == "") {} {followInstructions p} +end + +def go = forever {copy} end + +go; diff --git a/data/scenarios/Challenges/_telephone/shuttle.sw b/data/scenarios/Challenges/_telephone/shuttle.sw new file mode 100644 index 000000000..4ee877f90 --- /dev/null +++ b/data/scenarios/Challenges/_telephone/shuttle.sw @@ -0,0 +1,47 @@ +def ifC: ∀ a. Cmd Bool -> {Cmd a} -> {Cmd a} -> Cmd a + = \test. \then. \else. + b <- test; + if b then else +end + +def while: ∀ a. Cmd Bool -> {Cmd a} -> Cmd Unit + = \test. \body. + ifC test {force body; while test body} {} +end + +def forever: ∀ a b. {Cmd a} -> Cmd b = \c. force c; forever c end + +def notC : Cmd Bool -> Cmd Bool = \c. + b <- c; return (not b) +end + +def or : Cmd Bool -> Cmd Bool -> Cmd Bool = \c1. \c2. + ifC c1 {return true} {c2} +end + +def followTrack : Cmd Unit = + move; + while (or (isHere "track") (isHere "mountain")) { move }; + turn back; +end + +def pickup : Cmd Text = + atomic (b <- isempty; if b {return ""} {grab}); +end + +def dropoff : Text -> Cmd Bool = \thing. + atomic (b <- isempty; if b {place thing} {}; return b) +end + +def deliver : Text -> Cmd Unit = \thing. + move; + followTrack; + if (thing == "") {} + { + while (notC (dropoff thing)) { followTrack; followTrack } + }; +end + +def go = forever {followTrack; thing <- pickup; deliver thing} end + +go; diff --git a/data/scenarios/Challenges/_telephone/solution.sw b/data/scenarios/Challenges/_telephone/solution.sw new file mode 100644 index 000000000..53eb37d0f --- /dev/null +++ b/data/scenarios/Challenges/_telephone/solution.sw @@ -0,0 +1,77 @@ +def x : Int -> Cmd a -> Cmd Unit = \n. \c. + if (n == 0) {} {c; x (n-1) c} +end + +def ifC: ∀ a. Cmd Bool -> {Cmd a} -> {Cmd a} -> Cmd a + = \test. \then. \else. + b <- test; + if b then else +end + +def while: ∀ a. Cmd Bool -> {Cmd a} -> Cmd Unit + = \test. \body. + ifC test {force body; while test body} {} +end + +def for : Int -> (Int -> Cmd a) -> Cmd Unit = \n. \k. + if (n == 0) {} {k n; for (n-1) k} +end + +def harvestMay = + e <- isempty; + if e {} {harvest; return ()} +end + +def harvestTrees = + turn back; move; turn left; x 5 move; + turn left; + x 5 (x 10 (harvestMay; move); turn back; x 10 move; turn left; move; turn left); + turn left; x 10 move; turn right; move +end + +def getWater = + turn back; x 3 move; turn left; move; + x 32 grab; + turn back; move; turn right; x 3 move +end + +def getPaper = + harvestTrees; + while (has "tree") {make "log"}; + x 2 (make "board"); make "boat"; equip "boat"; + getWater; x 4 (make "paper") +end + +def scanAt : Int -> Int -> Cmd (Unit + Text) = \h. \v. + x h move; turn right; x v move; + s <- scan down; + turn back; x v move; turn left; x h move; turn back; + return s +end + +def atTerminal : Cmd a -> Cmd a = \c. + x 12 move; turn left; x 2 move; + a <- c; + turn back; x 2 move; turn right; x 12 move; turn back; + return a +end + +def waitToPlace : Text -> Cmd Unit = \t. + success <- atomic (b <- isempty; if b {place t} {}; return b); + if success {} { watch down; wait 1024; waitToPlace t } +end + +def go = + getPaper; + x 2 move; turn left; x 4 move; + for 8 (\h. + for 4 (\v. + res <- scanAt (h-1) (v-1); + case res + (\_. return ()) + (\t. atTerminal (p <- print (format ((h-1,v-1),t)); waitToPlace p)) + ) + ) +end + +go; diff --git a/data/scenarios/Challenges/telephone.yaml b/data/scenarios/Challenges/telephone.yaml new file mode 100644 index 000000000..7e94218ac --- /dev/null +++ b/data/scenarios/Challenges/telephone.yaml @@ -0,0 +1,210 @@ +version: 1 +name: Telephone +author: Brent Yorgey +description: | + Give another robot instructions to duplicate a pattern. +creative: false +objectives: + - id: 'paper' + teaser: Make some paper + goal: + - | + As part of a scheme to prove your intelligence to anyone + watching from space, the mystical design of X's and O's in the + blue square needs to be copied into the square across the + mountains. + - | + One small problem is that the mountains are too high for you + to cross! However, there is a shuttle service, running on a + regular schedule through a tunnel, that can deliver small + packages to the terminal on the other side of the mountains. + At the other terminal is a general-purpose utility robot; you + will have to send it instructions so it can recreate the + design for you. + - | + As a first step, you will need `paper`{=entity} on which to + write the instructions; make at least 8 sheets of + `paper`{=entity}. + condition: | + as base { + pcount <- count "paper"; + return $ pcount >= 8; + }; + - teaser: Duplicate the design + prerequisite: 'paper' + goal: + - | + Now that you have some `paper`{=entity}, you can use your + `typewriter`{=entity} to `print` on it. If you `format` a + value, `print` it on some paper, and send it via the shuttle, + the utility robot will be able to read the value. + - | + In particular, the utility robot is expecting to read values + of type `(Int * Int) * Text`{=type}, where the + `(Int * Int)`{=type} tuple is an x- and y-offset from the lower right + corner of the blue box, and the `Text`{=type} is the name of + the entity (either `X`{=entity} or `O`{=entity}) to place + there. For example, if you printed `((3,1), "O")` then the + utility robot would place an `O`{=entity} 3 units to the left + and 1 unit above the bottom-right corner of the blue square. + condition: | + judge <- robotNamed "judge"; + as judge { has "X" } +solution: | + run "scenarios/Challenges/_telephone/solution.sw" +robots: + - name: base + dir: north + devices: + - branch predictor + - treads + - antenna + - comparator + - ADT calculator + - workbench + - harvester + - dictionary + - lambda + - logger + - welder + - scanner + - strange loop + - solar panel + - string + - typewriter + - rolex + - rubber band + - tweezers + inventory: [] + - name: shuttle + system: true + dir: east + display: + invisible: false + char: 'Ξ' + priority: 8 + program: | + run "scenarios/Challenges/_telephone/shuttle.sw" + - name: photocopier + system: true + dir: east + display: + invisible: false + char: '*' + inventory: + - [100, 'O'] + - [100, 'X'] + program: | + run "scenarios/Challenges/_telephone/photocopier.sw" + - name: judge + system: true + dir: east + devices: + - logger + program: | + run "scenarios/Challenges/_telephone/judge.sw" +attrs: + - name: greyborder + fg: '#cccccc' + bg: '#002f00' + - name: blueborder + fg: '#4287f5' + bg: '#002f00' + - name: purpleborder + fg: '#d885ff' + bg: '#002f00' + - name: yg + fg: '#ffff8f' + bg: '#002f00' + - name: rg + fg: '#ff8f8f' + bg: '#002f00' +entities: + - name: mountain + display: + attr: snow + char: 'A' + priority: 9 + description: + - An impassably tall mountain. + properties: [unwalkable, opaque] + - name: blueborder + display: + char: '%' + attr: 'blueborder' + description: + - Decorative border + properties: [known, boundary] + - name: purpleborder + display: + char: '&' + attr: 'purpleborder' + description: + - Decorative border + properties: [known, boundary] + - name: track + display: + char: '=' + attr: entity + description: + - Narrow-gauge track. + properties: [known] + - name: O + display: + char: 'O' + attr: rg + description: + - O + properties: [known] + - name: X + display: + char: 'X' + attr: yg + description: + - X + properties: [known] +known: [mountain, tree, water, wavy water] +world: + dsl: | + overlay + [ {grass} + , if (x >= 93 && x <= 100 && y >= -8 && y <= -5) then + (if (hash % 7 <= 1) then {X} else if (hash % 7 <= 3) then {O} else {grass}) + else {grass} + ] + upperleft: [0, 0] + offset: false + palette: + '.': [grass] + '#': [grass, wall] + '%': [grass, blueborder] + 'A': [stone, mountain] + 'T': [grass, tree] + 'W': [stone, water] + '~': [stone, wavy water] + '^': [grass, null, base] + '&': [grass, purpleborder] + '=': [grass, track] + 'S': [grass, track, shuttle] + 'P': [grass, null, photocopier] + 'J': [grass, null, judge] + map: | + ######################################################################################################################## + #..........................................................AA..........................................................# + #.........................................................AAA..................................................T.......# + #........................................................AAA..................................................TT.......# + #.................%%%%%%%%%%..............................AAAA..............................%%%%%%%%%%.........TTT.....# + #.................%........%...............................AAAAA............................%........%.......TTT.......# + #.................%........%..................................AA............................%........%.....TTTT........# + #.................%........%..............................AAAAA.............................%........%.......TTT.......# + #.................%J.......%.............................AAA................................%........%.........T.......# + #.................%%%%%%%%%%..&&&.......................AAAAA..........................&&&..%%%%%%%%%%.......TTTT......# + #.............................&P==========================AA====================S=======.&..............^.....TTTT.....# + #.............................&&&........................AAAA..........................&&&.....................TT......# + #..........................................................AAA.........................................................# + #...........................................................AAAA.........................................WWW...........# + #..........................................................AAAAAA........................................WW~W..........# + #...........................................................AAAA.......................................WW~WW...........# + #..........................................................AAAA..........................................WW............# + #.........................................................AAA..........................................................# + ######################################################################################################################## From 5f1296949a7731180e6baab9a3caf16e6fb6b555 Mon Sep 17 00:00:00 2001 From: Brent Yorgey Date: Sat, 28 Dec 2024 22:51:41 -0600 Subject: [PATCH 4/7] add telephone challenge scenario to test suite --- test/integration/Main.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration/Main.hs b/test/integration/Main.hs index fd53247dd..5f9f57cc7 100644 --- a/test/integration/Main.hs +++ b/test/integration/Main.hs @@ -249,6 +249,7 @@ testScenarioSolutions rs ui key = , testSolution Default "Challenges/pack-tetrominoes" , testSolution (Sec 10) "Challenges/dimsum" , testSolution (Sec 15) "Challenges/gallery" + , testSolution (Sec 10) "Challenges/telephone" , testGroup "Mazes" [ testSolution Default "Challenges/Mazes/easy_cave_maze" From 03da3edd39fe1bf8dc990ffdc53d36f4b245fd65 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 28 Dec 2024 23:07:04 -0600 Subject: [PATCH 5/7] Restyled by fourmolu (#2246) Co-authored-by: Restyled.io --- src/swarm-engine/Swarm/Game/Step/Const.hs | 5 +++-- .../Swarm/Language/Syntax/Constants.hs | 16 ++++++++++------ test/unit/TestEval.hs | 5 +++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/swarm-engine/Swarm/Game/Step/Const.hs b/src/swarm-engine/Swarm/Game/Step/Const.hs index e9b42d4b5..748973d2c 100644 --- a/src/swarm-engine/Swarm/Game/Step/Const.hs +++ b/src/swarm-engine/Swarm/Game/Step/Const.hs @@ -1230,8 +1230,9 @@ execConst runChildProg c vs s k = do ("paper: " `T.isPrefixOf` paperName) `holdsOrFail` ["Can only erase paper, not", indefinite paperName <> "."] em <- use $ landscape . terrainAndEntities . entityMap - paper <- lookupEntityName "paper" em - `isJustOrFail` ["Can't erase, I don't know what paper is."] + paper <- + lookupEntityName "paper" em + `isJustOrFail` ["Can't erase, I don't know what paper is."] robotInventory %= delete toErase robotInventory %= insert paper diff --git a/src/swarm-lang/Swarm/Language/Syntax/Constants.hs b/src/swarm-lang/Swarm/Language/Syntax/Constants.hs index 99c09520b..4b11c26f8 100644 --- a/src/swarm-lang/Swarm/Language/Syntax/Constants.hs +++ b/src/swarm-lang/Swarm/Language/Syntax/Constants.hs @@ -821,17 +821,21 @@ constInfo c = case c of Format -> function 1 $ shortDoc Set.empty "Turn an arbitrary value into a string." Read -> function 2 $ shortDoc Set.empty "Try to read a string into a value of the expected type." Print -> - command 1 short . doc (Set.singleton $ Mutation $ RobotChange InventoryChange) - "Print text onto a piece of paper." $ - [ "Consumes one `paper` entity from your inventory, and produces an entity" + command 1 short + . doc + (Set.singleton $ Mutation $ RobotChange InventoryChange) + "Print text onto a piece of paper." + $ [ "Consumes one `paper` entity from your inventory, and produces an entity" , "whose name is \"paper: \" concatenated with the given text." , "In conjunction with `format`, this can be used to print values onto paper" , "and give them to other robots, which can reconstitute the values with `read`." ] Erase -> - command 1 short . doc (Set.singleton $ Mutation $ RobotChange InventoryChange) - "Erase a piece of paper." $ - [ "Consumes the named entity from your inventory, whose name must begin with" + command 1 short + . doc + (Set.singleton $ Mutation $ RobotChange InventoryChange) + "Erase a piece of paper." + $ [ "Consumes the named entity from your inventory, whose name must begin with" , "\"paper: \", and produces a \"paper\" entity. This can be used to undo" , "the effects of a `print` command." ] diff --git a/test/unit/TestEval.hs b/test/unit/TestEval.hs index 75898ad60..0a4a94252 100644 --- a/test/unit/TestEval.hs +++ b/test/unit/TestEval.hs @@ -388,8 +388,9 @@ testEval g = ("read \"paper: 52\" : Int" `evaluatesToV` (52 :: Integer)) , testCase "read paper with tuple" - ("read \"paper: (3, false, ())\" : Int * Bool * Unit" - `evaluatesToV` (3 :: Integer, (False, ()))) + ( "read \"paper: (3, false, ())\" : Int * Bool * Unit" + `evaluatesToV` (3 :: Integer, (False, ())) + ) ] , testGroup "records - #1093" From a1e09dae7cc58feed9f3e0a6b7d3225cf6be4c91 Mon Sep 17 00:00:00 2001 From: Brent Yorgey Date: Sat, 28 Dec 2024 23:07:22 -0600 Subject: [PATCH 6/7] update editor configs --- editors/emacs/swarm-mode.el | 2 ++ editors/vim/swarm.vim | 2 +- editors/vscode/syntaxes/swarm.tmLanguage.yaml | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/editors/emacs/swarm-mode.el b/editors/emacs/swarm-mode.el index 60120084d..36b0ced8f 100644 --- a/editors/emacs/swarm-mode.el +++ b/editors/emacs/swarm-mode.el @@ -118,6 +118,8 @@ "run" "return" "try" + "print" + "erase" "swap" "atomic" "instant" diff --git a/editors/vim/swarm.vim b/editors/vim/swarm.vim index ab06c8bbe..aa3a9a7a2 100644 --- a/editors/vim/swarm.vim +++ b/editors/vim/swarm.vim @@ -1,6 +1,6 @@ syn keyword Keyword def tydef rec end let in require syn keyword Builtins self parent base if inl inr case fst snd force undefined fail not format read chars split charat tochar key -syn keyword Command noop wait selfdestruct move backup volume path push stride turn grab harvest sow ignite place ping give equip unequip make has equipped count drill use build salvage reprogram say listen log view appear create halt time scout whereami locateme waypoint structure floorplan hastag tagmembers detect resonate density sniff chirp watch surveil heading blocked scan upload ishere isempty meet meetall whoami setname random run return try swap atomic instant installkeyhandler teleport warp as robotnamed robotnumbered knows +syn keyword Command noop wait selfdestruct move backup volume path push stride turn grab harvest sow ignite place ping give equip unequip make has equipped count drill use build salvage reprogram say listen log view appear create halt time scout whereami locateme waypoint structure floorplan hastag tagmembers detect resonate density sniff chirp watch surveil heading blocked scan upload ishere isempty meet meetall whoami setname random run return try print erase swap atomic instant installkeyhandler teleport warp as robotnamed robotnumbered knows syn keyword Direction east north west south down forward left back right syn match Type "\<[A-Z][a-zA-Z_]*\>" syn match Operators "[-=!<>|&+*/^$:]" diff --git a/editors/vscode/syntaxes/swarm.tmLanguage.yaml b/editors/vscode/syntaxes/swarm.tmLanguage.yaml index ecee58ad9..6cd7b56e7 100644 --- a/editors/vscode/syntaxes/swarm.tmLanguage.yaml +++ b/editors/vscode/syntaxes/swarm.tmLanguage.yaml @@ -46,7 +46,7 @@ repository: keyword: name: keyword.other match: >- - \b(self|parent|base|if|inl|inr|case|fst|snd|force|undefined|fail|not|format|read|chars|split|charat|tochar|key|noop|wait|selfdestruct|move|backup|volume|path|push|stride|turn|grab|harvest|sow|ignite|place|ping|give|equip|unequip|make|has|equipped|count|drill|use|build|salvage|reprogram|say|listen|log|view|appear|create|halt|time|scout|whereami|locateme|waypoint|structure|floorplan|hastag|tagmembers|detect|resonate|density|sniff|chirp|watch|surveil|heading|blocked|scan|upload|ishere|isempty|meet|meetall|whoami|setname|random|run|return|try|swap|atomic|instant|installkeyhandler|teleport|warp|as|robotnamed|robotnumbered|knows)\b + \b(self|parent|base|if|inl|inr|case|fst|snd|force|undefined|fail|not|format|read|chars|split|charat|tochar|key|noop|wait|selfdestruct|move|backup|volume|path|push|stride|turn|grab|harvest|sow|ignite|place|ping|give|equip|unequip|make|has|equipped|count|drill|use|build|salvage|reprogram|say|listen|log|view|appear|create|halt|time|scout|whereami|locateme|waypoint|structure|floorplan|hastag|tagmembers|detect|resonate|density|sniff|chirp|watch|surveil|heading|blocked|scan|upload|ishere|isempty|meet|meetall|whoami|setname|random|run|return|try|print|erase|swap|atomic|instant|installkeyhandler|teleport|warp|as|robotnamed|robotnumbered|knows)\b require: name: keyword.control.require match: \b(require)\b From 3ef37381d0407bbc554d780dbe5635b91754d0c3 Mon Sep 17 00:00:00 2001 From: Brent Yorgey Date: Tue, 31 Dec 2024 21:03:13 -0600 Subject: [PATCH 7/7] explain how to send items via the shuttle --- data/scenarios/Challenges/telephone.yaml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/data/scenarios/Challenges/telephone.yaml b/data/scenarios/Challenges/telephone.yaml index 7e94218ac..0eafba0ab 100644 --- a/data/scenarios/Challenges/telephone.yaml +++ b/data/scenarios/Challenges/telephone.yaml @@ -47,6 +47,12 @@ objectives: there. For example, if you printed `((3,1), "O")` then the utility robot would place an `O`{=entity} 3 units to the left and 1 unit above the bottom-right corner of the blue square. + - | + To send something via the shuttle, just place the item you + wish to send on the purple cell at the center of the eastern + terminal, that is, the cell with coordinates `(88, -10)`. The + next time the shuttle arrives, it will notice the item and + pick it up for delivery. condition: | judge <- robotNamed "judge"; as judge { has "X" } @@ -113,6 +119,8 @@ attrs: - name: purpleborder fg: '#d885ff' bg: '#002f00' + - name: purplebg + bg: '#d885ff' - name: yg fg: '#ffff8f' bg: '#002f00' @@ -163,6 +171,10 @@ entities: description: - X properties: [known] +terrains: + - name: terminal + attr: purplebg + description: Shuttle terminal known: [mountain, tree, water, wavy water] world: dsl: | @@ -188,6 +200,7 @@ world: 'S': [grass, track, shuttle] 'P': [grass, null, photocopier] 'J': [grass, null, judge] + 't': [terminal] map: | ######################################################################################################################## #..........................................................AA..........................................................# @@ -199,7 +212,7 @@ world: #.................%........%..............................AAAAA.............................%........%.......TTT.......# #.................%J.......%.............................AAA................................%........%.........T.......# #.................%%%%%%%%%%..&&&.......................AAAAA..........................&&&..%%%%%%%%%%.......TTTT......# - #.............................&P==========================AA====================S=======.&..............^.....TTTT.....# + #.............................&P==========================AA====================S=======t&..............^.....TTTT.....# #.............................&&&........................AAAA..........................&&&.....................TT......# #..........................................................AAA.........................................................# #...........................................................AAAA.........................................WWW...........#