Skip to content

Commit

Permalink
beekeeping scenario (#1599)
Browse files Browse the repository at this point in the history
Builds upon #1579

Requires player to constuct beehives to attract bees and make honey, which is then brewed as mead.

## Demo

    scripts/play.sh -i scenarios/Challenges/Ranching/beekeeping.yaml --autoplay

![image](https://github.com/swarm-game/swarm/assets/261693/f8c5c898-d865-4fe7-954d-c9e5b5f9a5c8)

Map:
![map](https://github.com/swarm-game/swarm/assets/261693/9c288edb-e71f-4a59-bd32-e1ecfdb1b60e)

## References

Mead hall inspiration: https://cartographyassets.com/assets/13507/the-mead-hall-of-the-clan-ulfgar-50-x-50/

![Mead-Hal-v1-No-Light-or-Shadow-with-grid-copy-scaled](https://github.com/swarm-game/swarm/assets/261693/a2d276f1-a522-499e-9a4b-7ce1df754107)
  • Loading branch information
kostmo authored Nov 28, 2023
1 parent 44c2e60 commit e03251c
Show file tree
Hide file tree
Showing 6 changed files with 1,123 additions and 1 deletion.
9 changes: 8 additions & 1 deletion data/entities.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,14 @@
attr: blue
char: 'B'
description:
- Locate and analyze structures placed in the world.
- This enables the `structure` 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.
- |
`floorplan : text -> cmd (int * int)`
- Gets the dimensions of a structure template.
properties: [portable]
capabilities: [structure]

Expand Down
1 change: 1 addition & 0 deletions data/scenarios/Challenges/Ranching/00-ORDER.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
beekeeping.yaml
capture.yaml
powerset.yaml
gated-paddock.yaml
298 changes: 298 additions & 0 deletions data/scenarios/Challenges/Ranching/_beekeeping/queenbee.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
// Spawns worker bees when structures are detected

def doN = \n. \f. if (n > 0) {f; doN (n - 1) f} {}; end;
def mod : int -> int -> int = \a. \b. a - (a/b)*b end;
def abs = \n. if (n < 0) {-n} {n} end;
def min = \x. \y. if (x < y) {x} {y} end;

def elif = \t. \then. \else. {if t then else} end
def else = \t. t end

def sumTuples = \t1. \t2.
(fst t1 + fst t2, snd t1 + snd t2);
end;

def mapTuple = \f. \t.
(f $ fst t, f $ snd t)
end;

def negateTuple = \t.
mapTuple (\x. -x) t;
end;

def subtractTuple = \t1. \t2.
sumTuples t1 $ negateTuple t2;
end;

// Deprecated
def moveTuple = \tup.
let x = fst tup in
let y = snd tup in
turn $ if (x > 0) {east} {west};
doN (abs x) move;

turn $ if (y > 0) {north} {south};
doN (abs y) move;
end;

def randomDir =
r <- random 4;
return $ if (r == 1) {north}
$ elif (r == 2) {west}
$ elif (r == 3) {south}
$ else {east};
end;

def moveHorizontal = \maxDirect. \dist.
turn $ if (dist > 0) {east} {west};
doN (min maxDirect $ abs dist) move;
end;

def moveVertical = \maxDirect. \dist.
turn $ if (dist > 0) {north} {south};
doN (min maxDirect $ abs dist) move;
end;

def randomStep =
randDir <- randomDir;
turn randDir;
move;
end;

def moveToward = \maxDirect. \goal.

currLocOrig <- whereami;
if (currLocOrig == goal) {} {

// Include some random motion
randomStep;

currLoc <- whereami;
let delta = subtractTuple goal currLoc in
let x = fst delta in
let y = snd delta in

moveHorizontal maxDirect x;
moveVertical maxDirect y;

moveToward maxDirect goal;
}
end;

def watchForHoneycombRemoval = \dist.
if (dist > 0) {
move;
honeycombHere <- ishere "honeycomb";
if honeycombHere {
watch down;
} {};

watchForHoneycombRemoval $ dist - 1;
} {};
end;

/**
Tries to find an open cell to deposit
the honeycomb. Gives up when distance
threshold exceeded.
*/
def depositHoneycomb = \dist.
if (dist < 5) {
emptyHere <- isempty;
if emptyHere {
place "honeycomb";
} {
move;
depositHoneycomb $ dist + 1;
};
} {
turn back;
watchForHoneycombRemoval dist;

// Hibernate
wait 2000;

// Alternative method to get rid of honeycomb
make "buzz";
};
end;

def goToHive = \hiveLoc.
let depositLoc = (fst hiveLoc - 1, snd hiveLoc) in
moveToward 2 depositLoc;
turn north;
depositHoneycomb 0;
end;

/**
Harvests an item when reached
*/
def takeStepTowardItem = \item.
// NOTE: Max radius is hard-coded to 256
// (see maxSniffRange in Syntax.hs)
direction <- chirp item;
if (direction == down) {
// Need a try block in case
// another bee gets here first
try {
harvest;
return ();
} {};
} {
// Include some random motion
r <- random 4;
if (r == 0) {
randomStep;
} {
turn direction;
move;
};

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;
}
);
} {
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);
}
);
end;

def mkBeeName = \structureLoc.
"bee" ++ format structureLoc;
end;

def workerProgramInit = \beename. \hiveIdx. \structureLoc.
setname beename;
appear "B";
workerProgram hiveIdx structureLoc;
end;

def createWorkerForStructure = \structureIdx. \fs.
// Build worker bee, assign ID, location
create "wax gland";
create "proboscis";

create "ADT calculator";
create "beaglepuss";
create "bitcoin";
create "branch predictor";
create "comparator";
create "compass";
create "detonator";
create "dictionary";
create "fast grabber";
create "GPS receiver";
create "harvester";
create "hourglass";
create "lambda";
create "net";
create "rolex";
create "scanner";
create "strange loop";
create "solar panel";
create "treads";
create "workbench";

teleport self $ snd fs;
let beename = mkBeeName (snd fs) in
build {
require 1 "wax gland";
workerProgramInit beename structureIdx $ snd fs;
};
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;

/**
Each tick, iterates through all hives,
and makes sure a "bee" robot is associated with
their location.
If a structure exists without such an association,
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;
);

// Wait at least 1 tick so that we do not spin infinitely until
// we saturate our computation quota for the tick.
wait 1;
observeHives;
end;

def go =
instant $ observeHives;
end;

go;
Loading

0 comments on commit e03251c

Please sign in to comment.