Skip to content

Commit

Permalink
Make structures, waypoints, and tagmembers commands return lists (
Browse files Browse the repository at this point in the history
#2260)

Change `structure`, `waypoint`, and `tagmembers` to return lists; also make `waypoint`, `tagmembers` and `hastag` pure functions, since their output within a given scenario will never change.

More specifically:

- Changes the type of `waypoint : Text -> Int -> (Int * Int)` to just return the waypoint with the given index (wrapping around), or throw an exception if there are no waypoints with the given name.
- Adds a new `waypoints : Text -> (rec l. Unit + Text * l)` function that returns the list of all waypoints with a given name.
- Change `structure` to `structures : Text -> Cmd (rec l. Unit + (Int * Int) * l)`, returning the list of all structure locations of the given name
- Change `tagmembers` to have type `tagmembers : Text -> (rec l. Unit + Text * l)`
- Change `hastag` to have type `hastag : Text -> Text -> Bool`

In the vast majority of cases, this greatly simplified the code for various scenario setups and solutions, though there is still a bit of code duplication for things like calculating the length of a list.  But even this can easily be improved once we have #495 and can make a standard library of list functions and import it within scenario code.

The reason I kept an indexing function for waypoints but not for structures or tags is that iterating through waypoints incrementally while doing other stuff in between seems like a common thing to want to do, and passing around a list of waypoints and manually indexing into it sounds tedious.  However, for structures and tags this is much less common; typically we just want to get a list and then do something with all of them right away.
  • Loading branch information
byorgey authored Jan 6, 2025
1 parent 0c1d5f2 commit e26bfe5
Show file tree
Hide file tree
Showing 46 changed files with 307 additions and 375 deletions.
8 changes: 4 additions & 4 deletions data/entities.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
118 changes: 38 additions & 80 deletions data/scenarios/Challenges/Ranching/_beekeeping/queenbee.sw
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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,
Expand All @@ -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.
Expand Down
40 changes: 20 additions & 20 deletions data/scenarios/Challenges/Ranching/_fishing/hauler.sw
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
24 changes: 6 additions & 18 deletions data/scenarios/Challenges/Ranching/_fishing/solution.sw
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions data/scenarios/Challenges/Ranching/beekeeping.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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'
Expand Down
68 changes: 27 additions & 41 deletions data/scenarios/Challenges/Ranching/fishing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
};
Expand Down Expand Up @@ -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:
Expand Down
Loading

0 comments on commit e26bfe5

Please sign in to comment.