diff --git a/data/entities.yaml b/data/entities.yaml index cb7d43355..728856d75 100644 --- a/data/entities.yaml +++ b/data/entities.yaml @@ -596,15 +596,15 @@ attr: blue char: 'B' description: - - This enables the `structure` and `floorplan` commands to locate and analyze structures placed in the world. + - This enables the `structures` and `floorplan` commands to locate and analyze structures placed in the world. - | - `structure : Text -> Int -> Cmd (Unit + (Int * (Int * Int)))` - - Gets the x, y coordinates of the southwest corner of a constructed structure, by name and index. + `structures : Text -> Cmd (rec l. Unit + (Int * Int) * l)` + - Gets the x, y coordinates of the southwest corner of every constructed structure with a given name. - | `floorplan : Text -> Cmd (Int * Int)` - Gets the dimensions of a structure template. properties: [pickable] - capabilities: [structure, floorplan] + capabilities: [structures, floorplan] - name: drill bit display: attr: entity diff --git a/data/scenarios/Challenges/Ranching/_beekeeping/queenbee.sw b/data/scenarios/Challenges/Ranching/_beekeeping/queenbee.sw index 59caab37f..af8548956 100644 --- a/data/scenarios/Challenges/Ranching/_beekeeping/queenbee.sw +++ b/data/scenarios/Challenges/Ranching/_beekeeping/queenbee.sw @@ -152,65 +152,29 @@ def takeStepTowardItem = \item. } end; -/** -Searches through the existing instances of -a given structure template, starting at a supplied -index. -Either returns the (potentially new) index of the structure -(in the case that more had been built since the last check), -or unit. Re-using the newly found index amortizes the "search" -within the structure list over many ticks to constant time -rather than linear time. -*/ -def findStructureNewIndex = \remainingCount. \structureLoc. \lastIdx. - if (remainingCount > 0) { - foundStructure <- structure "beehive" lastIdx; - case foundStructure (\_. return $ inL ()) (\fs. - if (structureLoc == snd fs) { - return $ inR lastIdx; - } { - findStructureNewIndex (remainingCount - 1) structureLoc $ lastIdx + 1; - } - ); +def workerProgram = \structureLoc. + try {make "honeycomb";} {}; + hasHoneycomb <- has "honeycomb"; + if hasHoneycomb { + goToHive structureLoc; } { - return $ inL (); - } - end; - -def workerProgram = \hiveIdx. \structureLoc. - eitherFoundStructure <- structure "beehive" hiveIdx; - case eitherFoundStructure return (\fs. - let hasSameStructure = structureLoc == snd fs in - if hasSameStructure { - try {make "honeycomb";} {}; - hasHoneycomb <- has "honeycomb"; - if hasHoneycomb { - goToHive structureLoc; - } { - takeStepTowardItem "wildflower"; - return (); - }; - workerProgram hiveIdx structureLoc; - } { - eitherNewIdx <- findStructureNewIndex (fst fs) structureLoc hiveIdx; - case eitherNewIdx - (\_. selfdestruct) - (\newIdx. workerProgram newIdx structureLoc); - } - ); + takeStepTowardItem "wildflower"; + return (); + }; + workerProgram structureLoc; end; def mkBeeName = \structureLoc. "bee" ++ format structureLoc; end; -def workerProgramInit = \beename. \hiveIdx. \structureLoc. +def workerProgramInit = \beename. \structureLoc. setname beename; appear "B" (inl ()); - workerProgram hiveIdx structureLoc; + workerProgram structureLoc; end; -def createWorkerForStructure = \structureIdx. \fs. +def createWorkerForStructure = \loc. // Build worker bee, assign ID, location create "wax gland"; create "proboscis"; @@ -236,38 +200,36 @@ def createWorkerForStructure = \structureIdx. \fs. create "treads"; create "workbench"; - teleport self $ snd fs; - let beename = mkBeeName (snd fs) in + teleport self $ loc; + let beename = mkBeeName loc in build { require 1 "wax gland"; - workerProgramInit beename structureIdx $ snd fs; + workerProgramInit beename loc; }; return (); end; -def associateAllHives = \remainingCount. \idx. - if (remainingCount > 0) { - - foundStructure <- structure "beehive" idx; - case foundStructure return (\fs. - let beename = mkBeeName (snd fs) in - try { - // Fails if the robot does not exist - robotnamed beename; - return (); - } { - createWorkerForStructure idx fs; - - // Give the child robot time to register its new - // name so that we don't end up spawning multiple - // bees for the same location - wait 1; - }; - - associateAllHives (remainingCount - 1) (idx + 1); - ); - } {} - end; +def associateHive = \loc. + let beename = mkBeeName loc in + try { + // Fails if the robot does not exist + robotnamed beename; + return (); + } { + createWorkerForStructure loc; + + // Give the child robot time to register its new + // name so that we don't end up spawning multiple + // bees for the same location + wait 1; + }; + end; + +def mapM_ : (a -> Cmd b) -> (rec l. Unit + a * l) -> Cmd Unit = \f. \l. + case l + (\_. return ()) + (\cons. f (fst cons); mapM_ f (snd cons)) + end; /** Each tick, iterates through all hives, @@ -278,12 +240,8 @@ creates a bee named after the location. */ def observeHives = - // This invocation is just to get the total structure count. - // We will invoke it again once per iteration of 'associateAllHives'. - foundStructure <- structure "beehive" 0; - case foundStructure return (\fs. - associateAllHives (fst fs) 0; - ); + beehives <- structures "beehive"; + mapM_ associateHive beehives; // Wait at least 1 tick so that we do not spin infinitely until // we saturate our computation quota for the tick. diff --git a/data/scenarios/Challenges/Ranching/_fishing/hauler.sw b/data/scenarios/Challenges/Ranching/_fishing/hauler.sw index 2c942509a..9d1e978d4 100644 --- a/data/scenarios/Challenges/Ranching/_fishing/hauler.sw +++ b/data/scenarios/Challenges/Ranching/_fishing/hauler.sw @@ -9,29 +9,29 @@ def intersperse = \n. \f2. \f1. if (n > 0) { } {}; end; -def isEnclosureFull = \idx. - foundBox <- structure "rubbish enclosure" idx; - case foundBox (\_. return false) (\enclosure. - let boxPos = snd enclosure in - - prevLoc <- whereami; - - dims <- floorplan "rubbish enclosure"; - teleport self boxPos; - - c <- density ((0, 0), dims); - let area = fst dims * snd dims in - let notFull = c < area in - - teleport self prevLoc; - return $ not notFull; - ); +def isEnclosureFull : Int * Int -> Cmd Bool = \encl. + prevLoc <- whereami; + + dims <- floorplan "rubbish enclosure"; + teleport self encl; + + c <- density ((0, 0), dims); + let area = fst dims * snd dims in + let notFull = c < area in + + teleport self prevLoc; + return $ not notFull; end; +def any : (a -> Cmd Bool) -> (rec l. Unit + a * l) -> Cmd Bool = \p. \l. + case l + (\_. return false) + (\c. b <- p (fst c); if b {return true} {any p (snd c)}) +end; + def isEitherEnclosureFull = - full1 <- isEnclosureFull 0; - full2 <- isEnclosureFull 1; - return $ full1 || full2; + enclosures <- structures "rubbish enclosure"; + any isEnclosureFull enclosures end; def tryGrab = diff --git a/data/scenarios/Challenges/Ranching/_fishing/solution.sw b/data/scenarios/Challenges/Ranching/_fishing/solution.sw index 79c8f3a1d..2d5d87581 100644 --- a/data/scenarios/Challenges/Ranching/_fishing/solution.sw +++ b/data/scenarios/Challenges/Ranching/_fishing/solution.sw @@ -62,23 +62,11 @@ def harvestIngredients = move; end; -def getJunkItem = \idx. - result <- tagmembers "junk" idx; - let totalCount = fst result in - let member = snd result in - let nextIdx = idx + 1 in - - hasProhibited <- has member; - if hasProhibited { - return $ inr member; - } { - if (nextIdx < totalCount) { - getJunkItem nextIdx; - } { - return $ inl (); - } - } - end; +def find : (a -> Cmd Bool) -> (rec l. Unit + a * l) -> Cmd (Unit + a) = \p. \l. + case l + (\_. return (inl ())) + (\cons. h <- p (fst cons); if h {return $ inr (fst cons)} {find p (snd cons)}) +end def tryPlace = \item. try { @@ -131,7 +119,7 @@ def returnToCorner = def unloadTrash = try { placeSerpentine ( - item <- getJunkItem 0; + item <- find has (tagmembers "junk"); case item (\_. fail "done") (\item. place item); ); watch down; diff --git a/data/scenarios/Challenges/Ranching/beekeeping.yaml b/data/scenarios/Challenges/Ranching/beekeeping.yaml index fb2e924fd..c0e06add5 100644 --- a/data/scenarios/Challenges/Ranching/beekeeping.yaml +++ b/data/scenarios/Challenges/Ranching/beekeeping.yaml @@ -32,7 +32,7 @@ objectives: judicious placement is essential for efficient `honeycomb`{=entity} production. condition: | - foundStructure <- structure "beehive" 0; + foundStructure <- structures "beehive"; return $ case foundStructure (\_. false) (\_. true); - teaser: Collect honeycomb goal: @@ -75,7 +75,7 @@ objectives: - | Construct a `mead hall`{=structure}. condition: | - foundStructure <- structure "mead hall" 0; + foundStructure <- structures "mead hall"; return $ case foundStructure (\_. false) (\_. true); robots: - name: base @@ -193,7 +193,7 @@ entities: description: - Senses direction to nectar-producing flowers properties: [known, pickable] - capabilities: [chirp, structure] + capabilities: [chirp, structures] - name: honey display: char: 'h' diff --git a/data/scenarios/Challenges/Ranching/fishing.yaml b/data/scenarios/Challenges/Ranching/fishing.yaml index 312f88435..f120d2f4e 100644 --- a/data/scenarios/Challenges/Ranching/fishing.yaml +++ b/data/scenarios/Challenges/Ranching/fishing.yaml @@ -59,26 +59,14 @@ objectives: Otherwise, when the enclosure becomes full, the trash hauler will come to empty it. condition: | - // Returns true if prohibited item is in inventory. - def checkProhibited = \idx. - result <- tagmembers "junk" idx; - let totalCount = fst result in - let member = snd result in - let nextIdx = idx + 1 in + def hasAny : (rec l. Unit + Text * l) -> Cmd Bool = \items. + case items + (\_. return false) + (\c. b <- has (fst c); if b {return true} {hasAny (snd c)}) + end; - hasProhibited <- as base {has member}; - if hasProhibited { - return false; - } { - if (nextIdx < totalCount) { - checkProhibited nextIdx; - } { - return true; - } - } - end; - - checkProhibited 0; + let prohibited = tagmembers "junk" in + h <- hasAny prohibited; return (not h) - teaser: No littering id: littered hidden: true @@ -89,33 +77,33 @@ objectives: - | Note that certain items can be burned with a `torch`{=entity} to make room. condition: | - def locIsInsideEnclosure = \loc. \dims. \idx. - foundBox <- structure "rubbish enclosure" idx; - case foundBox (\_. return false) (\enclosure. - let boxPos = snd enclosure in - return $ snd loc >= snd boxPos - && snd loc < snd dims + snd boxPos - && fst loc >= fst boxPos - && fst loc < fst dims + fst boxPos; - ); + def locIsInsideEnclosure = \loc. \dims. \boxPos. + return $ snd loc >= snd boxPos + && snd loc < snd dims + snd boxPos + && fst loc >= fst boxPos + && fst loc < fst dims + fst boxPos; end; def junkOutsideEnclosure = result <- scan down; case result (\_. return false) (\item. - isJunk <- hastag item "junk"; + let isJunk = hastag item "junk" in if isJunk { - foundBox <- structure "rubbish enclosure" 0; + enclosures <- structures "rubbish enclosure"; - case foundBox (\_. return false) (\enclosure. + case enclosures (\_. return false) (\cons0. + let enclosure0 = fst cons0 in + case (snd cons0) (\_. return false) (\cons1. + let enclosure1 = fst cons1 in - dims <- floorplan "rubbish enclosure"; - loc <- whereami; + dims <- floorplan "rubbish enclosure"; + loc <- whereami; - insideFirst <- locIsInsideEnclosure loc dims 0; - insideSecond <- locIsInsideEnclosure loc dims 1; - return $ not $ insideFirst || insideSecond; - ); + insideFirst <- locIsInsideEnclosure loc dims enclosure0; + insideSecond <- locIsInsideEnclosure loc dims enclosure1; + return $ not $ insideFirst || insideSecond; + ) + ) } { return false; }; @@ -208,13 +196,11 @@ entities: description: - | Can tell you if something is `junk`{=tag} - via the `hastag` command. E.g.: + via the `hastag` function. E.g.: - | `hastag "car tire" "junk"` - | - Also allows you to iterate over the list of junk, e.g.: - - | - `tagmembers "junk" 0` + Also allows you to get the list of all junk items with `tagmembers "junk"`. capabilities: [hastag, tagmembers] - name: torch display: diff --git a/data/scenarios/Challenges/_dna/lab.sw b/data/scenarios/Challenges/_dna/lab.sw index 6e7aa2d6b..dcfbdc3ac 100644 --- a/data/scenarios/Challenges/_dna/lab.sw +++ b/data/scenarios/Challenges/_dna/lab.sw @@ -126,8 +126,7 @@ def makeDnaStrand = \receptacleLoc. teleport self (0, -11); waitUntilHere "switch (on)"; - bottomWaypointQuery <- waypoint "receiver" 1; - let receptacleLoc2 = snd bottomWaypointQuery in + let receptacleLoc2 = waypoint "receiver" 1 in teleport self receptacleLoc2; sow clonedItem; @@ -145,24 +144,22 @@ def makeDnaStrand = \receptacleLoc. def waitForCloneableOrganism = - waypointQuery <- waypoint "receiver" 0; - let receptacleLoc = snd waypointQuery in + let receptacleLoc = waypoint "receiver" 0 in organism <- instant ( teleport self receptacleLoc; waitUntilOccupied; thingHere <- scan down; - case thingHere (\x. return $ inL x) (\item. - isOrganism <- hastag item "organism"; - return $ inR item; - ); + return $ case thingHere (\x. inL x) (\item. + if (hastag item "organism") {inR item} {inL ()} + ) ); case organism (\_. say "Not a cloneable organism!"; waitForCloneableOrganism; - ) (\item. + ) (\_. create "pixel (G)"; makeDnaStrand receptacleLoc; ); diff --git a/data/scenarios/Challenges/_dna/solution.sw b/data/scenarios/Challenges/_dna/solution.sw index d63f598b3..e31ea08ea 100644 --- a/data/scenarios/Challenges/_dna/solution.sw +++ b/data/scenarios/Challenges/_dna/solution.sw @@ -243,20 +243,18 @@ def completeDnaTask = \sentinel. returnToInputReceptacle; end; -def iterateOrganisms = \idx. - result <- tagmembers "organism" idx; - let totalCount = fst result in - if (idx < totalCount) { - completeDnaTask $ snd result; - iterateOrganisms $ idx + 1; - } {}; - end; +def mapM_ : (a -> Cmd b) -> (rec l. Unit + a * l) -> Cmd Unit = \f. \l. + case l + (\_. return ()) + (\c. f (fst c); mapM_ f (snd c)) +end; def go = _sentinel <- pickFlowerAndWater; moveUntilBlocked; - iterateOrganisms 0; + let organisms = tagmembers "organism" in + mapM_ completeDnaTask organisms; end; go; diff --git a/data/scenarios/Challenges/_gallery/setup.sw b/data/scenarios/Challenges/_gallery/setup.sw index 474b097aa..72ccc5a87 100644 --- a/data/scenarios/Challenges/_gallery/setup.sw +++ b/data/scenarios/Challenges/_gallery/setup.sw @@ -88,13 +88,21 @@ def naiveRandomStack = \valueFunc. \maxval. \bitmask. \n. valueFunc val; end; -def getEntName = \idx. - result <- tagmembers "bust" idx; - return $ snd result; - end; +def index : Int -> (rec l. Unit + a * l) -> a = \i. \l. + case l + (\_. fail "bad index") + (\cons. if (i == 0) {fst cons} {index (i-1) (snd cons)}) +end + +def length : (rec l. Unit + a * l) -> Int = \l. + case l (\_. 0) (\cons. 1 + length (snd cons)) +end + +def busts : (rec l. Unit + Text * l) = tagmembers "bust" end +def bustCount : {Int} = {length busts} end def placeThing = \entIdx. - entName <- getEntName entIdx; + let entName = index entIdx busts in create entName; place entName; end; @@ -116,13 +124,10 @@ bust in the base's inventory increases monotonically. "idx" counts upwards. */ def populateInventory = \baseCount. \idx. - - result <- tagmembers "bust" idx; - let total = fst result in - if (idx < total) { + if (idx < force bustCount) { - let item = snd result in + let item = index idx busts in // Give copies to the base baseExtras <- random 5; @@ -143,12 +148,9 @@ def populateInventory = \baseCount. \idx. end; def setup = - result <- tagmembers "bust" 0; - let totalCount = fst result in - populateInventory 0 0; - naiveRandomStack placeEntByIndex totalCount 0 totalCount; + naiveRandomStack placeEntByIndex (force bustCount) 0 (force bustCount); turn back; move; create "bitcoin"; diff --git a/data/scenarios/Challenges/dna.yaml b/data/scenarios/Challenges/dna.yaml index 369510ff4..d09956a06 100644 --- a/data/scenarios/Challenges/dna.yaml +++ b/data/scenarios/Challenges/dna.yaml @@ -85,24 +85,16 @@ objectives: When all segments are placed, `drill` the switch on the western wall to commence rematerialization. `grab` the clone (once matured) as a souvenir! condition: | - def checkMember = \idx. - result <- tagmembers "organism" idx; - let totalCount = fst result in - hasThis <- has $ snd result; - if hasThis { - return true; - } { - if (idx < totalCount - 1) { - checkMember $ idx + 1; - } { - return false; - } - }; - end; + def hasAll : (rec l. Unit + Text * l) -> Cmd Bool = \l. + case l + (\_. return true) + (\c. b1 <- has (fst c); if b1 {hasAll (snd c)} {return false}) + end; k <- robotnamed "keeper"; as k { - checkMember 0; + let organisms = tagmembers "organism" in + hasAll organisms; } - prerequisite: create_clone teaser: More clones @@ -113,22 +105,16 @@ objectives: Find three more different `organism`{=tag}s to clone. Explore outside nearby the lab for specimens. condition: | - def countMembers = \memberCount. \idx. - result <- tagmembers "organism" idx; - let totalCount = fst result in - - if (idx < totalCount) { - hasThis <- has $ snd result; - let memberCountAddition = if hasThis {1} {0} in - countMembers (memberCount + memberCountAddition) $ idx + 1; - } { - return memberCount; - } - end; + def hasCount : (rec l. Unit + Text * l) -> Cmd Int = \l. + case l + (\_. return 0) + (\c. b <- has (fst c); n <- hasCount (snd c); return (if b {1} {0} + n)) + end; k <- robotnamed "keeper"; as k { - c <- countMembers 0 0; + let organisms = tagmembers "organism" in + c <- hasCount organisms; return $ c >= 4; } robots: diff --git a/data/scenarios/Challenges/gallery.yaml b/data/scenarios/Challenges/gallery.yaml index 69d8cf7c9..bba64700c 100644 --- a/data/scenarios/Challenges/gallery.yaml +++ b/data/scenarios/Challenges/gallery.yaml @@ -62,14 +62,18 @@ objectives: ); end; + def length : (rec l. Unit + a * l) -> Int = \l. + case l (\_. 0) (\cons. 1 + length (snd cons)) + end + j <- robotnamed "judge"; as j { // Flag that setup is done hasBitcoin <- has "bitcoin"; if hasBitcoin { - result <- tagmembers "bust" 0; - let itemCount = fst result in - ensureMonotonicDecreasing itemCount $ itemCount + 1; + let busts = tagmembers "bust" in + let bustCount = length busts in + ensureMonotonicDecreasing bustCount $ bustCount + 1; } { return false; } diff --git a/data/scenarios/Challenges/pack-tetrominoes.yaml b/data/scenarios/Challenges/pack-tetrominoes.yaml index 2643ce773..f159aaab7 100644 --- a/data/scenarios/Challenges/pack-tetrominoes.yaml +++ b/data/scenarios/Challenges/pack-tetrominoes.yaml @@ -21,7 +21,7 @@ objectives: Place all five tetrominoes. condition: | def found = \s. - fs <- structure s 0; + fs <- structures s; return $ case fs (\_. false) (\_. true); end; diff --git a/data/scenarios/Challenges/word-search.yaml b/data/scenarios/Challenges/word-search.yaml index d873f5c90..aee3ea6ed 100644 --- a/data/scenarios/Challenges/word-search.yaml +++ b/data/scenarios/Challenges/word-search.yaml @@ -14,8 +14,8 @@ objectives: or vertically in either the upward or downward direction. Diagonal appearances are not valid. condition: | - foundStructure <- structure "cow" 0; - return $ case foundStructure (\_. false) (\_. true); + foundStructures <- structures "cow"; + return $ case foundStructures (\_. false) (\_. true); robots: - name: base display: diff --git a/data/scenarios/Testing/1356-portals/_automatic-waypoint-patrol/program.sw b/data/scenarios/Testing/1356-portals/_automatic-waypoint-patrol/program.sw index 8cf22bf37..a28daa23e 100644 --- a/data/scenarios/Testing/1356-portals/_automatic-waypoint-patrol/program.sw +++ b/data/scenarios/Testing/1356-portals/_automatic-waypoint-patrol/program.sw @@ -34,15 +34,15 @@ def goToLocation = \currentLoc. \absoluteDestination. def visitNextWaypoint = \nextWpIdx. loc <- whereami; - nextWaypointQuery <- waypoint "wp" nextWpIdx; - goToLocation loc $ snd nextWaypointQuery; + let nextWaypointQuery = waypoint "wp" nextWpIdx in + goToLocation loc $ nextWaypointQuery; visitNextWaypoint $ nextWpIdx + 1; end; def go = - waypointQuery <- waypoint "wp" 0; - teleport self $ snd waypointQuery; + let waypointQuery = waypoint "wp" 0 in + teleport self $ waypointQuery; visitNextWaypoint 1; end; diff --git a/data/scenarios/Testing/1575-structure-recognizer/1575-bounding-box-overlap.yaml b/data/scenarios/Testing/1575-structure-recognizer/1575-bounding-box-overlap.yaml index a61008d79..179ab3194 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1575-bounding-box-overlap.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1575-bounding-box-overlap.yaml @@ -14,8 +14,11 @@ objectives: - | Build 3 `chevron`{=structure} structures condition: | - foundStructure <- structure "chevron" 0; - return $ case foundStructure (\_. false) (\x. fst x >= 3); + def length : (rec l. Unit + a * l) -> Int = \l. + case l (\_. 0) (\cons. 1 + length (snd cons)) + end; + foundStructures <- structures "chevron"; + return $ length foundStructures >= 3 robots: - name: base dir: east diff --git a/data/scenarios/Testing/1575-structure-recognizer/1575-browse-structures.yaml b/data/scenarios/Testing/1575-structure-recognizer/1575-browse-structures.yaml index f8553ee32..471dff73d 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1575-browse-structures.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1575-browse-structures.yaml @@ -14,7 +14,7 @@ objectives: - | Build a `precious`{=structure} structure condition: | - foundStructure <- structure "precious" 0; + foundStructure <- structures "precious"; return $ case foundStructure (\_. false) (\_. true); robots: - name: base diff --git a/data/scenarios/Testing/1575-structure-recognizer/1575-construction-count.yaml b/data/scenarios/Testing/1575-structure-recognizer/1575-construction-count.yaml index a5b43be7b..d7445f08c 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1575-construction-count.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1575-construction-count.yaml @@ -9,8 +9,11 @@ objectives: - | Build 12 copies of the `green_jewel`{=structure} structure condition: | - foundGreen <- structure "green_jewel" 0; - return $ case foundGreen (\_. false) (\x. fst x >= 12); + def length : (rec l. Unit + a * l) -> Int = \l. + case l (\_. 0) (\cons. 1 + length (snd cons)) + end; + foundGreen <- structures "green_jewel"; + return $ length foundGreen >= 12; robots: - name: base dir: east diff --git a/data/scenarios/Testing/1575-structure-recognizer/1575-ensure-disjoint.yaml b/data/scenarios/Testing/1575-structure-recognizer/1575-ensure-disjoint.yaml index 39995f64e..1b923dea2 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1575-ensure-disjoint.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1575-ensure-disjoint.yaml @@ -16,11 +16,11 @@ objectives: - | Build 2 of the same structure condition: | - foundStructure <- structure "chessboard" 0; - return $ case foundStructure (\_. false) (\fs. - let boardCount = fst fs in - boardCount >= 2; - ); + def length : (rec l. Unit + a * l) -> Int = \l. + case l (\_. 0) (\cons. 1 + length (snd cons)) + end; + foundStructures <- structures "chessboard"; + return $ length foundStructures >= 2 - id: premature_win teaser: Don't count win early optional: true @@ -29,13 +29,13 @@ objectives: Two structures shouldn't be recognized while the bases still possesses `silver`{=entity} condition: | + def length : (rec l. Unit + a * l) -> Int = \l. + case l (\_. 0) (\cons. 1 + length (snd cons)) + end; robotHasSilver <- as base {has "silver"}; - foundStructure <- structure "chessboard" 0; - return $ case foundStructure (\_. false) (\fs. - let boardCount = fst fs in - boardCount >= 2 && robotHasSilver; - ); + foundStructures <- structures "chessboard"; + return $ length foundStructures >= 2 && robotHasSilver; robots: - name: base dir: south diff --git a/data/scenarios/Testing/1575-structure-recognizer/1575-ensure-single-recognition.yaml b/data/scenarios/Testing/1575-structure-recognizer/1575-ensure-single-recognition.yaml index 55787caea..e4d9cdcae 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1575-ensure-single-recognition.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1575-ensure-single-recognition.yaml @@ -13,11 +13,11 @@ objectives: - | Build 2 of the same structure condition: | - foundStructure <- structure "chessboard" 0; - return $ case foundStructure (\_. false) (\fs. - let boardCount = fst fs in - boardCount >= 2; - ); + def length : (rec l. Unit + a * l) -> Int = \l. + case l (\_. 0) (\cons. 1 + length (snd cons)) + end; + foundStructures <- structures "chessboard"; + return $ length foundStructures >= 2; - id: premature_win teaser: Don't count win early optional: true @@ -26,13 +26,13 @@ objectives: Two structures shouldn't be recognized while the bases still possesses `gold`{=entity} condition: | + def length : (rec l. Unit + a * l) -> Int = \l. + case l (\_. 0) (\cons. 1 + length (snd cons)) + end; robotHasGold <- as base {has "gold"}; - foundStructure <- structure "chessboard" 0; - return $ case foundStructure (\_. false) (\fs. - let boardCount = fst fs in - boardCount >= 2 && robotHasGold; - ); + foundStructures <- structures "chessboard"; + return $ length foundStructures >= 2 && robotHasGold; robots: - name: base dir: south diff --git a/data/scenarios/Testing/1575-structure-recognizer/1575-floorplan-command.yaml b/data/scenarios/Testing/1575-structure-recognizer/1575-floorplan-command.yaml index 5cdf107e8..0a31fcef5 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1575-floorplan-command.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1575-floorplan-command.yaml @@ -11,7 +11,7 @@ objectives: Build a `wooden box`{=structure} structure. condition: | def isRight = \x. case x (\_. false) (\_. true); end; - foundBox <- structure "wooden box" 0; + foundBox <- structures "wooden box"; return $ isRight foundBox; robots: - name: base diff --git a/data/scenarios/Testing/1575-structure-recognizer/1575-handle-overlapping.yaml b/data/scenarios/Testing/1575-structure-recognizer/1575-handle-overlapping.yaml index 84768333c..74510709a 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1575-handle-overlapping.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1575-handle-overlapping.yaml @@ -10,7 +10,7 @@ objectives: - | Build a `precious`{=structure} structure condition: | - foundStructure <- structure "precious" 0; + foundStructure <- structures "precious"; return $ case foundStructure (\_. false) (\_. true); robots: - name: base diff --git a/data/scenarios/Testing/1575-structure-recognizer/1575-interior-entity-placement.yaml b/data/scenarios/Testing/1575-structure-recognizer/1575-interior-entity-placement.yaml index 7a6515706..964b5f62c 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1575-interior-entity-placement.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1575-interior-entity-placement.yaml @@ -17,9 +17,9 @@ objectives: - | Place the `rock`{=entity} entity back inside the `pigpen`{=structure}. condition: | - foundBox <- structure "pigpen" 0; - case foundBox (\_. return false) (\struc. - let structPos = snd struc in + foundBoxes <- structures "pigpen"; + case foundBoxes (\_. return false) (\cons. + let structPos = fst cons in j <- robotnamed "judge"; as j { structBounds <- floorplan "pigpen"; @@ -49,8 +49,8 @@ objectives: even with an extraneous entity within its bounds. condition: | def isRight = \x. case x (\_. false) (\_. true); end; - foundBox <- structure "pigpen" 0; - return $ isRight foundBox; + foundBoxes <- structures "pigpen"; + return $ isRight foundBoxes; robots: - name: base dir: east diff --git a/data/scenarios/Testing/1575-structure-recognizer/1575-nested-structure-definition.yaml b/data/scenarios/Testing/1575-structure-recognizer/1575-nested-structure-definition.yaml index af2ee74bf..323e33e3b 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1575-nested-structure-definition.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1575-nested-structure-definition.yaml @@ -13,7 +13,7 @@ objectives: Replace `tree`{=entity} after grabbing. The `double ring`{=structure} structure should be recognized again. condition: | - foundStructure <- structure "double ring" 0; + foundStructure <- structures "double ring"; return $ case foundStructure (\_. false) (\_. true); - teaser: Pre-recognized id: pre_recognized @@ -23,7 +23,7 @@ objectives: - | Pre-placed structure must be recognized condition: | - foundStructure <- structure "double ring" 0; + foundStructure <- structures "double ring"; return $ case foundStructure (\_. false) (\_. true); - teaser: Grab tree id: grab_tree diff --git a/data/scenarios/Testing/1575-structure-recognizer/1575-overlapping-tiebreaker-by-largest.yaml b/data/scenarios/Testing/1575-structure-recognizer/1575-overlapping-tiebreaker-by-largest.yaml index 51be897e1..792cbabff 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1575-overlapping-tiebreaker-by-largest.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1575-overlapping-tiebreaker-by-largest.yaml @@ -11,7 +11,7 @@ objectives: - | Build a 3x3 structure condition: | - foundStructure <- structure "large" 0; + foundStructure <- structures "large"; return $ case foundStructure (\_. false) (\_. true); - id: wrong_structure teaser: Don't recognize small structure @@ -20,7 +20,7 @@ objectives: - | The small structure shouldn't be recognized. condition: | - foundStructure <- structure "small" 0; + foundStructure <- structures "small"; return $ case foundStructure (\_. false) (\_. true); robots: - name: base diff --git a/data/scenarios/Testing/1575-structure-recognizer/1575-overlapping-tiebreaker-by-location.yaml b/data/scenarios/Testing/1575-structure-recognizer/1575-overlapping-tiebreaker-by-location.yaml index 3dc2b0394..2b15adee1 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1575-overlapping-tiebreaker-by-location.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1575-overlapping-tiebreaker-by-location.yaml @@ -12,7 +12,7 @@ objectives: - | Build a `topleft`{=structure} structure condition: | - foundStructure <- structure "topleft" 0; + foundStructure <- structures "topleft"; return $ case foundStructure (\_. false) (\_. true); - id: wrong_structure teaser: Don't recognize small structure @@ -21,7 +21,7 @@ objectives: - | The `bottomright`{=structure} structure shouldn't be recognized. condition: | - foundStructure <- structure "bottomright" 0; + foundStructure <- structures "bottomright"; return $ case foundStructure (\_. false) (\_. true); robots: - name: base diff --git a/data/scenarios/Testing/1575-structure-recognizer/1575-placement-occlusion.yaml b/data/scenarios/Testing/1575-structure-recognizer/1575-placement-occlusion.yaml index 9c5aa6539..abd3df85a 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1575-placement-occlusion.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1575-placement-occlusion.yaml @@ -15,7 +15,7 @@ objectives: condition: | def isRight = \x. case x (\_. false) (\_. true); end; - foundGreen <- structure "green_jewel" 0; + foundGreen <- structures "green_jewel"; return $ isRight foundGreen; - id: complete_red_structure optional: true @@ -26,7 +26,7 @@ objectives: condition: | def isRight = \x. case x (\_. false) (\_. true); end; - foundRed <- structure "red_jewel" 0; + foundRed <- structures "red_jewel"; return $ isRight foundRed; robots: - name: base diff --git a/data/scenarios/Testing/1575-structure-recognizer/1575-remove-structure.yaml b/data/scenarios/Testing/1575-structure-recognizer/1575-remove-structure.yaml index d6e7e6a9f..e43a9ae67 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1575-remove-structure.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1575-remove-structure.yaml @@ -11,16 +11,16 @@ objectives: - | Remove a piece of the structure to destroy it condition: | - foundStructure <- structure "chessboard" 0; - return $ case foundStructure (\_. true) (\_. false); + foundStructures <- structures "chessboard"; + return $ case foundStructures (\_. true) (\_. false); - id: complete_structure teaser: Complete structure goal: - | Build a `chessboard`{=structure} structure condition: | - foundStructure <- structure "chessboard" 0; - return $ case foundStructure (\_. false) (\_. true); + foundStructures <- structures "chessboard"; + return $ case foundStructures (\_. false) (\_. true); robots: - name: base dir: south diff --git a/data/scenarios/Testing/1575-structure-recognizer/1575-swap-structure.yaml b/data/scenarios/Testing/1575-structure-recognizer/1575-swap-structure.yaml index 497c1ce14..81e10bb4d 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1575-swap-structure.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1575-swap-structure.yaml @@ -13,9 +13,9 @@ objectives: condition: | def isRight = \x. case x (\_. false) (\_. true); end; - foundBlue <- structure "blue_jewel" 0; - foundGreen <- structure "green_jewel" 0; - foundRed <- structure "red_jewel" 0; + foundBlue <- structures "blue_jewel"; + foundGreen <- structures "green_jewel"; + foundRed <- structures "red_jewel"; return $ isRight foundBlue && not (isRight foundRed) && not (isRight foundRed); - id: complete_green_structure teaser: Complete green structure @@ -26,8 +26,8 @@ objectives: condition: | def isRight = \x. case x (\_. false) (\_. true); end; - foundGreen <- structure "green_jewel" 0; - foundRed <- structure "red_jewel" 0; + foundGreen <- structures "green_jewel"; + foundRed <- structures "red_jewel"; return $ isRight foundGreen && not (isRight foundRed); - id: complete_red_structure teaser: Complete red structure @@ -37,7 +37,7 @@ objectives: condition: | def isRight = \x. case x (\_. false) (\_. true); end; - foundRed <- structure "red_jewel" 0; + foundRed <- structures "red_jewel"; return $ isRight foundRed; robots: - name: base diff --git a/data/scenarios/Testing/1575-structure-recognizer/1644-rotated-preplacement-recognition.yaml b/data/scenarios/Testing/1575-structure-recognizer/1644-rotated-preplacement-recognition.yaml index 8dcfbb518..13e63a1de 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1644-rotated-preplacement-recognition.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1644-rotated-preplacement-recognition.yaml @@ -9,7 +9,7 @@ objectives: - | Have a `tee`{=structure} structure condition: | - foundStructure <- structure "tee" 0; + foundStructure <- structures "tee"; return $ case foundStructure (\_. false) (\_. true); robots: - name: base diff --git a/data/scenarios/Testing/1575-structure-recognizer/1644-rotated-recognition.yaml b/data/scenarios/Testing/1575-structure-recognizer/1644-rotated-recognition.yaml index c1ae6ac2d..a37a1233e 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/1644-rotated-recognition.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/1644-rotated-recognition.yaml @@ -9,7 +9,7 @@ objectives: - | Build a `tee`{=structure} structure condition: | - foundStructure <- structure "tee" 0; + foundStructure <- structures "tee"; return $ case foundStructure (\_. false) (\_. true); robots: - name: base diff --git a/data/scenarios/Testing/1575-structure-recognizer/2115-encroaching-upon-exterior-transparent-cells.yaml b/data/scenarios/Testing/1575-structure-recognizer/2115-encroaching-upon-exterior-transparent-cells.yaml index 52f09fcb9..b0b9399a6 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/2115-encroaching-upon-exterior-transparent-cells.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/2115-encroaching-upon-exterior-transparent-cells.yaml @@ -19,8 +19,8 @@ objectives: condition: | def isRight = \x. case x (\_. false) (\_. true); end; - foundBox <- structure "chevron" 0; - return $ isRight foundBox; + foundBoxes <- structures "chevron"; + return $ isRight foundBoxes; robots: - name: base dir: east diff --git a/data/scenarios/Testing/1575-structure-recognizer/2115-encroaching-upon-interior-transparent-cells.yaml b/data/scenarios/Testing/1575-structure-recognizer/2115-encroaching-upon-interior-transparent-cells.yaml index 49b48a1fc..db8c365c0 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/2115-encroaching-upon-interior-transparent-cells.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/2115-encroaching-upon-interior-transparent-cells.yaml @@ -18,8 +18,8 @@ objectives: even with an extraneous entity within its bounds. condition: | def isRight = \x. case x (\_. false) (\_. true); end; - foundBox <- structure "pigpen" 0; - return $ isRight foundBox; + foundBoxes <- structures "pigpen"; + return $ isRight foundBoxes; robots: - name: base dir: east diff --git a/data/scenarios/Testing/1575-structure-recognizer/2201-initial-recognition-overlap.yaml b/data/scenarios/Testing/1575-structure-recognizer/2201-initial-recognition-overlap.yaml index c5dbc370f..5fd96bdd7 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/2201-initial-recognition-overlap.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/2201-initial-recognition-overlap.yaml @@ -18,8 +18,11 @@ objectives: Although two of these structures were initially placed, only the upper-left one should be recognized. condition: | - foundStructure <- structure "square" 0; - return $ case foundStructure (\_. false) (\pair. fst pair == 1); + def length : (rec l. Unit + a * l) -> Int = \l. + case l (\_. 0) (\c. 1 + length (snd c)) + end; + foundStructures <- structures "square"; + return $ length foundStructures == 1; robots: - name: base dir: north diff --git a/data/scenarios/Testing/1575-structure-recognizer/2201-piecewise-lines.yaml b/data/scenarios/Testing/1575-structure-recognizer/2201-piecewise-lines.yaml index 6b371cf99..29ed33a38 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/2201-piecewise-lines.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/2201-piecewise-lines.yaml @@ -15,8 +15,8 @@ objectives: `spaceship`{=structure} structure should be recognized upon completion. condition: | def isRight = \x. case x (\_. false) (\_. true); end; - foundStructure <- structure "spaceship" 0; - return $ isRight foundStructure; + foundStructures <- structures "spaceship"; + return $ isRight foundStructures; robots: - name: base dir: east diff --git a/data/scenarios/Testing/1575-structure-recognizer/2201-preclude-overlapping-recognition.yaml b/data/scenarios/Testing/1575-structure-recognizer/2201-preclude-overlapping-recognition.yaml index b71153a82..bc3e5a7e8 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/2201-preclude-overlapping-recognition.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/2201-preclude-overlapping-recognition.yaml @@ -10,18 +10,18 @@ objectives: `line`{=structure} structure should be recognized upon completion. condition: | def isRight = \x. case x (\_. false) (\_. true); end; - foundStructure <- structure "line" 0; - return $ isRight foundStructure; + foundStructures <- structures "line"; + return $ isRight foundStructures; - teaser: Recognize second structure id: found_elbow optional: true goal: - | - `line`{=structure} structure should be recognized upon completion. + `elbow`{=structure} structure should be recognized upon completion. condition: | def isRight = \x. case x (\_. false) (\_. true); end; - foundStructure <- structure "elbow" 0; - return $ isRight foundStructure; + foundStructures <- structures "elbow"; + return $ isRight foundStructures; - teaser: Grab tree prerequisite: not: found_elbow diff --git a/data/scenarios/Testing/1575-structure-recognizer/2229-position-uniqueness-multiple-orientations.yaml b/data/scenarios/Testing/1575-structure-recognizer/2229-position-uniqueness-multiple-orientations.yaml index 8adcc1104..ae472ceb9 100644 --- a/data/scenarios/Testing/1575-structure-recognizer/2229-position-uniqueness-multiple-orientations.yaml +++ b/data/scenarios/Testing/1575-structure-recognizer/2229-position-uniqueness-multiple-orientations.yaml @@ -16,10 +16,11 @@ objectives: Without distinguishing them by orientation in the registry, only one would be recognized. condition: | - foundStructure <- structure "thingy" 0; - return $ case foundStructure - (\_. false) - (\result. fst result == 2); + def length : (rec l. Unit + a * l) -> Int = \l. + case l (\_. 0) (\c. 1 + length (snd c)) + end; + foundStructures <- structures "thingy"; + return $ length foundStructures == 2; robots: - name: base dir: north diff --git a/data/scenarios/Testing/1631-tags.yaml b/data/scenarios/Testing/1631-tags.yaml index f12fb1f56..eb7d05038 100644 --- a/data/scenarios/Testing/1631-tags.yaml +++ b/data/scenarios/Testing/1631-tags.yaml @@ -14,26 +14,14 @@ objectives: id: got_fruit optional: true condition: | - // Returns true if prohibited item is in inventory. - def checkFruit = \idx. - result <- tagmembers "fruit" idx; - let totalCount = fst result in - let member = snd result in - let nextIdx = idx + 1 in + def hasAny : (rec l. Unit + Text * l) -> Cmd Bool = \items. + case items + (\_. return false) + (\c. b <- has (fst c); if b {return true} {hasAny (snd c)}) + end; - hasProhibited <- as base {has member}; - if hasProhibited { - return true; - } { - if (nextIdx < totalCount) { - checkFruit nextIdx; - } { - return false; - } - } - end; - - checkFruit 0; + let prohibited = tagmembers "fruit" in + hasAny prohibited goal: - | Do not pick up any fruit. @@ -41,8 +29,8 @@ solution: | def findTarget = result <- scan down; isTarget <- case result (\_. return false) (\item. - isEdible <- hastag item "edible"; - isFruit <- hastag item "fruit"; + let isEdible = hastag item "edible" in + let isFruit = hastag item "fruit" in return $ isEdible && not isFruit; ); diff --git a/data/scenarios/Testing/836-pathfinding/_836-automatic-waypoint-navigation/patrol.sw b/data/scenarios/Testing/836-pathfinding/_836-automatic-waypoint-navigation/patrol.sw index b2e61db2b..678147e48 100644 --- a/data/scenarios/Testing/836-pathfinding/_836-automatic-waypoint-navigation/patrol.sw +++ b/data/scenarios/Testing/836-pathfinding/_836-automatic-waypoint-navigation/patrol.sw @@ -23,8 +23,8 @@ def followRoute = \loc. end; def visitNextWaypoint = \nextWpIdx. - nextWaypointQuery <- waypoint "wp" nextWpIdx; - followRoute $ snd nextWaypointQuery; + let nextWaypointQuery = waypoint "wp" nextWpIdx in + followRoute $ nextWaypointQuery; visitNextWaypoint $ nextWpIdx + 1; end; diff --git a/data/scenarios/Testing/836-pathfinding/_836-automatic-waypoint-navigation/rabbit.sw b/data/scenarios/Testing/836-pathfinding/_836-automatic-waypoint-navigation/rabbit.sw index 245970de8..814ee58b5 100644 --- a/data/scenarios/Testing/836-pathfinding/_836-automatic-waypoint-navigation/rabbit.sw +++ b/data/scenarios/Testing/836-pathfinding/_836-automatic-waypoint-navigation/rabbit.sw @@ -7,10 +7,10 @@ def visitNextWaypoint = \nextWpIdx. } {}; } {}; watch down; - nextWaypointQuery <- waypoint "wp" nextWpIdx; - teleport self $ snd nextWaypointQuery; + let nextWaypointQuery = waypoint "wp" nextWpIdx in + teleport self $ nextWaypointQuery; wait 1000; visitNextWaypoint $ nextWpIdx + 1; end; -visitNextWaypoint 0; \ No newline at end of file +visitNextWaypoint 0; diff --git a/editors/emacs/swarm-mode.el b/editors/emacs/swarm-mode.el index 36b0ced8f..e7d58f913 100644 --- a/editors/emacs/swarm-mode.el +++ b/editors/emacs/swarm-mode.el @@ -92,11 +92,8 @@ "scout" "whereami" "locateme" - "waypoint" - "structure" + "structures" "floorplan" - "hastag" - "tagmembers" "detect" "resonate" "density" @@ -149,6 +146,10 @@ "\\b" (regexp-opt '( + "waypoint" + "waypoints" + "hastag" + "tagmembers" "self" "parent" "base" diff --git a/editors/vim/swarm.vim b/editors/vim/swarm.vim index aa3a9a7a2..4dc0e76a0 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 print erase swap atomic instant installkeyhandler teleport warp as robotnamed robotnumbered knows +syn keyword Builtins waypoint waypoints hastag tagmembers 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 structures floorplan 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 6cd7b56e7..89d1a7dff 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|print|erase|swap|atomic|instant|installkeyhandler|teleport|warp|as|robotnamed|robotnumbered|knows)\b + \b(waypoint|waypoints|hastag|tagmembers|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|structures|floorplan|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 diff --git a/src/swarm-engine/Swarm/Game/Step/Const.hs b/src/swarm-engine/Swarm/Game/Step/Const.hs index 0d8f7cba4..de3eda30e 100644 --- a/src/swarm-engine/Swarm/Game/Step/Const.hs +++ b/src/swarm-engine/Swarm/Game/Step/Const.hs @@ -11,6 +11,8 @@ -- Implementation of robot commands module Swarm.Game.Step.Const where +import Swarm.Game.Scenario (RecognizableStructureContent) + import Control.Applicative (Applicative (..)) import Control.Arrow ((&&&)) import Control.Carrier.State.Lazy @@ -545,22 +547,29 @@ execConst runChildProg c vs s k = do lm <- use $ landscape . worldNavigation Cosmic swName _ <- use robotLocation case M.lookup (WaypointName name) $ M.findWithDefault mempty swName $ waypoints lm of - Nothing -> throwError $ CmdFailed Waypoint (T.unwords ["No waypoint named", name]) Nothing - Just wps -> return $ mkReturn (NE.length wps, indexWrapNonEmpty wps idx) + Nothing -> raise Waypoint ["There are no waypoints named", quote name <> "."] + Just wps -> return $ mkReturn $ indexWrapNonEmpty wps idx _ -> badConst - Structure -> case vs of - [VText name, VInt idx] -> do + Waypoints -> case vs of + [VText name] -> do + lm <- use $ landscape . worldNavigation + Cosmic swName _ <- use robotLocation + let mwps = M.lookup (WaypointName name) $ M.findWithDefault mempty swName $ waypoints lm + return $ mkReturn $ maybe [] NE.toList mwps + _ -> badConst + Structures -> case vs of + [VText name] -> do registry <- use $ discovery . structureRecognition . foundStructures let maybeFoundStructures = M.lookup (StructureName name) $ foundByName registry - mkOutput mapNE = (NE.length xs, bottomLeftCorner) + structures :: [((Cosmic Location, AbsoluteDir), StructureWithGrid RecognizableStructureContent Entity)] + structures = maybe [] (NE.toList . NEM.toList) maybeFoundStructures + + bottomLeftCorner ((pos, _), struc) = topLeftCorner .+^ offsetHeight where - xs = NEM.toList mapNE - ((pos, _), struc) = indexWrapNonEmpty xs idx topLeftCorner = pos ^. planar offsetHeight = V2 0 $ negate (rectHeight (getNEGridDimensions $ extractedGrid $ entityGrid struc) - 1) - bottomLeftCorner :: Location - bottomLeftCorner = topLeftCorner .+^ offsetHeight - return $ mkReturn $ mkOutput <$> maybeFoundStructures + + return $ mkReturn $ map bottomLeftCorner structures _ -> badConst Floorplan -> case vs of [VText name] -> do @@ -580,11 +589,11 @@ execConst runChildProg c vs s k = do return $ mkReturn $ tName `S.member` (e ^. entityTags) _ -> badConst TagMembers -> case vs of - [VText tagName, VInt idx] -> do + [VText tagName] -> do tm <- use $ discovery . tagMembers case M.lookup tagName tm of Nothing -> throwError $ CmdFailed TagMembers (T.unwords ["No tag named", tagName]) Nothing - Just theMembers -> return $ mkReturn (NE.length theMembers, indexWrapNonEmpty theMembers idx) + Just theMembers -> return $ mkReturn theMembers _ -> badConst Detect -> case vs of [VText name, VRect x1 y1 x2 y2] -> do diff --git a/src/swarm-engine/Swarm/Game/Value.hs b/src/swarm-engine/Swarm/Game/Value.hs index a050e622f..3ff8be177 100644 --- a/src/swarm-engine/Swarm/Game/Value.hs +++ b/src/swarm-engine/Swarm/Game/Value.hs @@ -12,6 +12,7 @@ import Control.Lens (view) import Data.Either.Extra (maybeToEither) import Data.Int (Int32) import Data.List (uncons) +import Data.List.NonEmpty qualified as NE import Data.Text (Text) import Linear (V2 (..)) import Swarm.Game.Entity @@ -90,5 +91,8 @@ instance (Valuable a, Valuable b) => Valuable (Either a b) where instance Valuable a => Valuable [a] where asValue = asValue . uncons +instance Valuable a => Valuable (NE.NonEmpty a) where + asValue = asValue . NE.toList + instance Valuable AreaDimensions where asValue (AreaDimensions w h) = asValue (w, h) diff --git a/src/swarm-lang/Swarm/Language/Syntax/Constants.hs b/src/swarm-lang/Swarm/Language/Syntax/Constants.hs index dcde43417..9e4b813f3 100644 --- a/src/swarm-lang/Swarm/Language/Syntax/Constants.hs +++ b/src/swarm-lang/Swarm/Language/Syntax/Constants.hs @@ -148,15 +148,18 @@ data Const Whereami | -- | Get the current subworld and x, y coordinates LocateMe - | -- | Get the x, y coordinates of a named waypoint, by index + | -- | Get the x, y coordinates of a specific waypoints by name and index Waypoint - | -- | Get the x, y coordinates of southwest corner of a constructed structure, by index - Structure + | -- | Get the list of x, y coordinates of the waypoints for a given name + Waypoints + | -- | Get the list of x, y coordinates of the southwest corner of all + -- constructed structures of a given name + Structures | -- | Get the width and height of a structure template Floorplan | -- | Answer whether a given entity has the given tag HasTag - | -- | Cycle through the entity names that are labeled with a given tag + | -- | Get the list of entity names that are labeled with a given tag TagMembers | -- | Locate the closest instance of a given entity within the rectangle -- specified by opposite corners, relative to the current location. @@ -694,35 +697,32 @@ constInfo c = case c of (Set.singleton $ Query $ Sensing RobotSensing) "Get the current subworld and x, y coordinates." Waypoint -> - 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." + function 2 . doc (Set.singleton $ Query APriori) "Get the x, y coordinates of a waypoint, by name and index." $ + [ "Returns only waypoints in the same subworld as the calling robot." + , "Wraps around modulo the number of waypoints with the given name. Throws an exception if there are no waypoints with the given name." ] - Structure -> - 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." + Waypoints -> + function 1 . doc (Set.singleton $ Query APriori) "Get the list of x, y coordinates of a named waypoint" $ + [ "Returns only the waypoints in the same subworld as the calling robot." + , "Since waypoint names can have plural multiplicity, returns a list of (x, y) coordinates)." ] + Structures -> + command 1 Intangible . doc (Set.singleton $ Query $ Sensing EntitySensing) "Get the x, y coordinates of the southwest corner of all constructed structures of a given name" $ + ["Since structures can have multiple occurrences, returns a list of (x, y) coordinates."] Floorplan -> 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 (Set.singleton $ Query APriori) "Check whether the given entity has the given tag" $ + function 2 . 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 (Set.singleton $ Query APriori) "Get the entities labeled by a tag." $ - [ "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." - , "Item order is determined by definition sequence in the scenario file." + function 1 . doc (Set.singleton $ Query APriori) "Get the entities labeled by a tag." $ + [ "Returns a list of all entities with the given tag." + , "The order of the list is determined by the definition sequence in the scenario file." ] Detect -> command 2 Intangible . doc (Set.singleton $ Query $ Sensing EntitySensing) "Detect an entity within a rectangle." $ diff --git a/src/swarm-lang/Swarm/Language/Typecheck.hs b/src/swarm-lang/Swarm/Language/Typecheck.hs index 106655986..60ef0400e 100644 --- a/src/swarm-lang/Swarm/Language/Typecheck.hs +++ b/src/swarm-lang/Swarm/Language/Typecheck.hs @@ -1045,11 +1045,12 @@ inferConst c = run . runReader @TVCtx Ctx.empty . quantify $ case c of Scout -> [tyQ| Dir -> Cmd Bool |] Whereami -> [tyQ| Cmd (Int * Int) |] LocateMe -> [tyQ| Cmd (Text * (Int * Int)) |] - Waypoint -> [tyQ| Text -> Int -> Cmd (Int * (Int * Int)) |] - Structure -> [tyQ| Text -> Int -> Cmd (Unit + (Int * (Int * Int))) |] + Waypoint -> [tyQ| Text -> Int -> (Int * Int) |] + Waypoints -> [tyQ| Text -> (rec l. Unit + (Int * Int) * l) |] + Structures -> [tyQ| Text -> Cmd (rec l. Unit + (Int * Int) * l) |] Floorplan -> [tyQ| Text -> Cmd (Int * Int) |] - HasTag -> [tyQ| Text -> Text -> Cmd Bool |] - TagMembers -> [tyQ| Text -> Int -> Cmd (Int * Text) |] + HasTag -> [tyQ| Text -> Text -> Bool |] + TagMembers -> [tyQ| Text -> (rec l. Unit + Text * l) |] Detect -> [tyQ| Text -> ((Int * Int) * (Int * Int)) -> Cmd (Unit + (Int * Int)) |] Resonate -> [tyQ| Text -> ((Int * Int) * (Int * Int)) -> Cmd Int |] Density -> [tyQ| ((Int * Int) * (Int * Int)) -> Cmd Int |]