From 0fda99f93b19617f749aaf42a4ff9255bd4febf1 Mon Sep 17 00:00:00 2001 From: Karl Ostmo Date: Mon, 14 Oct 2024 16:12:25 -0700 Subject: [PATCH] DNA copying challenge (#1031) A challenge that utilizes a stack. Closes #932. Demo: ``` scripts/play.sh --scenario data/scenarios/Challenges/dna.yaml --autoplay ``` ![Screenshot from 2024-10-13 17-06-06](https://github.com/user-attachments/assets/678984b0-f83d-4e0d-8958-dfcd33461ce6) --- data/scenarios/Challenges/00-ORDER.txt | 1 + data/scenarios/Challenges/_dna/lab.sw | 177 ++++++ data/scenarios/Challenges/_dna/mirrorbot.sw | 99 ++++ data/scenarios/Challenges/_dna/resetter.sw | 48 ++ data/scenarios/Challenges/_dna/solution.sw | 262 ++++++++ data/scenarios/Challenges/_dna/topchecker.sw | 107 ++++ data/scenarios/Challenges/dna.yaml | 593 +++++++++++++++++++ test/integration/Main.hs | 1 + 8 files changed, 1288 insertions(+) create mode 100644 data/scenarios/Challenges/_dna/lab.sw create mode 100644 data/scenarios/Challenges/_dna/mirrorbot.sw create mode 100644 data/scenarios/Challenges/_dna/resetter.sw create mode 100644 data/scenarios/Challenges/_dna/solution.sw create mode 100644 data/scenarios/Challenges/_dna/topchecker.sw create mode 100644 data/scenarios/Challenges/dna.yaml diff --git a/data/scenarios/Challenges/00-ORDER.txt b/data/scenarios/Challenges/00-ORDER.txt index eb26f5eec..c1c1f6c3b 100644 --- a/data/scenarios/Challenges/00-ORDER.txt +++ b/data/scenarios/Challenges/00-ORDER.txt @@ -16,6 +16,7 @@ wave.yaml gallery.yaml wolf-goat-cabbage.yaml blender.yaml +dna.yaml friend.yaml pack-tetrominoes.yaml dimsum.yaml diff --git a/data/scenarios/Challenges/_dna/lab.sw b/data/scenarios/Challenges/_dna/lab.sw new file mode 100644 index 000000000..6e7aa2d6b --- /dev/null +++ b/data/scenarios/Challenges/_dna/lab.sw @@ -0,0 +1,177 @@ +def doN = \n. \f. if (n > 0) {f; doN (n - 1) f} {}; end; + +def getBaseForNumber = \n. + if (n == 0) { + "guanine"; + } { + if (n == 1) { + "cytosine"; + } { + if (n == 2) { + "adenine"; + } { + "thymine"; + }; + }; + }; + end; + +def getNumberForBase = \n. + if (n == "guanine") { + 0; + } { + if (n == "cytosine") { + 1; + } { + if (n == "adenine") { + 2; + } { + 3; + }; + }; + }; + end; + +/** Toggle the lowest bit */ +def getComplementNumber = \n. + if (n == 0) { + 1; + } { + if (n == 1) { + 0; + } { + if (n == 2) { + 3; + } { + 2; + }; + }; + }; + end; + +def waitUntilSomethingExists = + maybeItemHere <- scan down; + case maybeItemHere (\_. + watch down; + wait 1000; + waitUntilSomethingExists; + ) return; + end; + +def waitUntilHere = \item. + hereNow <- ishere item; + if hereNow {} { + watch down; + wait 1000; + waitUntilHere item; + }; + end; + +def waitUntilOccupied = + stillEmpty <- isempty; + if stillEmpty { + watch down; + wait 1000; + waitUntilOccupied; + } {}; + end; + +def myStandby = \receptacleLoc. + teleport self receptacleLoc; + entToClone <- grab; + teleport self (36, -11); + turn back; + return $ inR entToClone; + end; + +def placeBase = \standbyFunc. \n. + + if (n > 0) { + idx <- random 4; + let entTemp = getBaseForNumber idx in + let ent = entTemp in + create ent; + place ent; + move; + + clonedOrganism <- placeBase standbyFunc $ n - 1; + + // Unwinds the stack; verifies the original placement order + placedEnt <- instant waitUntilSomethingExists; + let isGood = ent == placedEnt in + move; + + if isGood { + return clonedOrganism; + } { + return $ inL (); + } + } { + // Returns the clonedOrganism + standbyFunc; + }; + end; + +def makeDnaStrand = \receptacleLoc. + teleport self (5, -2); + + dims <- floorplan "DNA decoder"; + let decoderWidth = fst dims in + eitherClonedOrganism <- placeBase (myStandby receptacleLoc) decoderWidth; + + case eitherClonedOrganism (\_. + create "pixel (R)"; + ) (\clonedItem. + instant $ ( + teleport self (0, -11); + waitUntilHere "switch (on)"; + + bottomWaypointQuery <- waypoint "receiver" 1; + let receptacleLoc2 = snd bottomWaypointQuery in + teleport self receptacleLoc2; + + sow clonedItem; + create clonedItem; + k <- robotnamed "keeper"; + give k clonedItem; + + let slideBox = "slide box" in + create slideBox; + give base slideBox; + say $ "You got a new \"" ++ slideBox ++ "\""; + ); + ); + end; + +def waitForCloneableOrganism = + + waypointQuery <- waypoint "receiver" 0; + let receptacleLoc = snd waypointQuery in + organism <- instant ( + teleport self receptacleLoc; + + waitUntilOccupied; + + thingHere <- scan down; + case thingHere (\x. return $ inL x) (\item. + isOrganism <- hastag item "organism"; + return $ inR item; + ); + ); + + case organism (\_. + say "Not a cloneable organism!"; + waitForCloneableOrganism; + ) (\item. + create "pixel (G)"; + makeDnaStrand receptacleLoc; + ); + end; + +def go = + waitForCloneableOrganism; + turn east; + go; + end; + +go; diff --git a/data/scenarios/Challenges/_dna/mirrorbot.sw b/data/scenarios/Challenges/_dna/mirrorbot.sw new file mode 100644 index 000000000..c9ce6abef --- /dev/null +++ b/data/scenarios/Challenges/_dna/mirrorbot.sw @@ -0,0 +1,99 @@ +def doN = \n. \f. if (n > 0) {f; doN (n - 1) f} {}; end; + +def getBaseForNumber = \n. + if (n == 0) { + "guanine"; + } { + if (n == 1) { + "cytosine"; + } { + if (n == 2) { + "adenine"; + } { + "thymine"; + }; + }; + }; + end; + +def getNumberForBase = \n. + if (n == "guanine") { + 0; + } { + if (n == "cytosine") { + 1; + } { + if (n == "adenine") { + 2; + } { + if (n == "thymine") { + 3; + } {-1}; + }; + }; + }; + end; + +/** Toggle the lowest bit */ +def getComplementNumber = \n. + if (n == 0) { + 1; + } { + if (n == 1) { + 0; + } { + if (n == 2) { + 3; + } { + 2; + }; + }; + }; + end; + +def placeComplementOf = \item. + let baseNumber = getNumberForBase item in + if (baseNumber >= 0) { + let complementNumber = getComplementNumber baseNumber in + let newItem = getBaseForNumber complementNumber in + move; + create newItem; + place newItem; + } { + let sludge = "organic sludge" in + create sludge; + place sludge; + } + end; + +def waitUntilHere = + watch down; + wait 1000; + + maybeItemDown <- scan down; + case maybeItemDown (\_. waitUntilHere) (\itemHere. + placeComplementOf itemHere; + ); + end; + +def waitUntilEmpty = + watch down; + wait 1000; + emptyHere <- isempty; + if emptyHere { + // reset the position + turn back; + move; + turn back; + } { + waitUntilEmpty; + } + end; + +def go = + instant waitUntilHere; + waitUntilEmpty; + go; + end; + +go; diff --git a/data/scenarios/Challenges/_dna/resetter.sw b/data/scenarios/Challenges/_dna/resetter.sw new file mode 100644 index 000000000..a88379175 --- /dev/null +++ b/data/scenarios/Challenges/_dna/resetter.sw @@ -0,0 +1,48 @@ +def doN = \n. \f. if (n > 0) {f; doN (n - 1) f} {}; end; + +/** +Assumes we are on the left edge of a row of nucleobases +*/ +def resetPlayfieldRow = + dims <- floorplan "DNA decoder"; + let decoderWidth = fst dims in + doN decoderWidth (grab; move;); + end; + +def resetPlayfield = + teleport self (5, -2); + resetPlayfieldRow; + teleport self (5, -3); + resetPlayfieldRow; + + teleport self (5, -11); + resetPlayfieldRow; + teleport self (5, -12); + resetPlayfieldRow; + end; + +def watchSwitch = \lastState. + watch down; + wait 1000; + found <- scan down; + case found return (\item. + if (item != lastState) { + if (item == "switch (off)") { + loc <- whereami; + resetPlayfield; + teleport self loc; + } { + }; + } {}; + watchSwitch item; + ); + end; + +def go = + instant $ ( + found <- scan down; + case found return watchSwitch; + ); + end; + +go; diff --git a/data/scenarios/Challenges/_dna/solution.sw b/data/scenarios/Challenges/_dna/solution.sw new file mode 100644 index 000000000..d63f598b3 --- /dev/null +++ b/data/scenarios/Challenges/_dna/solution.sw @@ -0,0 +1,262 @@ +def doN = \n. \f. if (n > 0) {f; doN (n - 1) f} {}; end; + +def moveUntilBlocked = + blockedHere <- blocked; + if blockedHere {} { + move; + moveUntilBlocked; + } + end; + +def getBaseForNumber = \n. + if (n == 0) { + "guanine"; + } { + if (n == 1) { + "cytosine"; + } { + if (n == 2) { + "adenine"; + } { + "thymine"; + }; + }; + }; + end; + +def getNumberForBase = \n. + if (n == "guanine") { + 0; + } { + if (n == "cytosine") { + 1; + } { + if (n == "adenine") { + 2; + } { + 3; + }; + }; + }; + end; + +/** Toggle the lowest bit */ +def getComplementNumber = \n. + if (n == 0) { + 1; + } { + if (n == 1) { + 0; + } { + if (n == 2) { + 3; + } { + 2; + }; + }; + }; + end; + +def waitWhileHere = \item. + stillHere <- ishere item; + if stillHere { + watch down; + wait 1000; + waitWhileHere item; + } {}; + end; + +def waitUntilHere = \item. + hereNow <- ishere item; + if hereNow {} { + watch down; + wait 1000; + waitUntilHere item; + }; + end; + +def moveToPattern = + turn back; + doN 5 move; + turn left; + doN 4 move; + turn left; + move; + end; + +def moveToOtherRow = + turn right; + doN 2 move; + turn right; + doN 4 move; + turn left; + doN 4 move; + turn left; + doN 4 move; + turn right; + doN 2 move; + turn right; + move; + end; + +def waitForItem : Dir -> Cmd Text = \d. + item <- scan d; + case item (\_. + watch d; + wait 1000; + waitForItem d; + ) return; + end; + +def waitForSpecificItem = \item. \d. + itemIsHere <- ishere item; + if itemIsHere { + } { + watch d; + wait 1000; + waitForSpecificItem item d; + } + end; + +def placeComplementOf = \item. + let baseNumber = getNumberForBase item in + let complementNumber = getComplementNumber baseNumber in + let newItem = getBaseForNumber complementNumber in + place newItem; + end; + +/** +Store the observed entities in the recursion stack. +*/ +def replicatePattern = \standbyFunc. \n. + if (n > 0) { + thingTemp <- waitForItem left; + let thing = thingTemp in + placeComplementOf thing; + move; + replicatePattern standbyFunc $ n - 1; + + place thing; + move; + } { + standbyFunc; + } + end; + +/** +Position self at entrance +*/ +def pickFlowerAndWater = + doN 6 move; + dahlia <- grab; + + turn left; + doN 8 move; + turn right; + doN 31 move; + clover <- grab; + turn back; + doN 35 move; + d <- grab; + doN 10 move; + + turn left; + + doN 18 move; + use "siphon" forward; + turn left; + doN 7 ( + move; + use "siphon" right; + ); + doN 4 move; + turn right; + doN 16 move; + + mushroom <- grab; + + turn back; + doN 23 move; + + turn right; + return dahlia; + + // return mushroom; + // return d; + end; + + +def waitUntilOccupied = + stillEmpty <- isempty; + if stillEmpty { + watch down; + wait 1000; + waitUntilOccupied; + } {}; + end; + +def returnToInputReceptacle = + turn back; + doN 5 move; + turn left; + moveUntilBlocked; + turn left; + doN 29 move; + turn right; + moveUntilBlocked; + turn left; + doN 4 move; + turn right; + doN 6 move; + turn right; + moveUntilBlocked; + end; + +def completeDnaTask = \sentinel. + place sentinel; + make "specimen slide"; + + doN 16 $ make "cytosine"; + + waitWhileHere sentinel; + moveToPattern; + replicatePattern moveToOtherRow 32; + + // Activate the switch + doN 3 move; + drill forward; + wait 2; + drill forward; + + turn left; + doN 6 move; + turn left; + doN 32 move; + turn left; + doN 2 move; + turn left; + move; + + waitUntilOccupied; + grab; + + 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 go = + _sentinel <- pickFlowerAndWater; + moveUntilBlocked; + + iterateOrganisms 0; + end; + +go; diff --git a/data/scenarios/Challenges/_dna/topchecker.sw b/data/scenarios/Challenges/_dna/topchecker.sw new file mode 100644 index 000000000..abb4b63da --- /dev/null +++ b/data/scenarios/Challenges/_dna/topchecker.sw @@ -0,0 +1,107 @@ +def doN = \n. \f. if (n > 0) {f; doN (n - 1) f} {}; end; + +def getBaseForNumber = \n. + if (n == 0) { + "guanine"; + } { + if (n == 1) { + "cytosine"; + } { + if (n == 2) { + "adenine"; + } { + "thymine"; + }; + }; + }; + end; + +def getNumberForBase = \n. + if (n == "guanine") { + 0; + } { + if (n == "cytosine") { + 1; + } { + if (n == "adenine") { + 2; + } { + if (n == "thymine") { + 3; + } {-1}; + }; + }; + }; + end; + +/** Toggle the lowest bit */ +def getComplementNumber = \n. + if (n == 0) { + 1; + } { + if (n == 1) { + 0; + } { + if (n == 2) { + 3; + } { + 2; + }; + }; + }; + end; + +def waitUntilHere = \remainingCount. + if (remainingCount > 0) { + maybeItemHere <- scan down; + case maybeItemHere (\_. + watch down; + wait 1000; + waitUntilHere remainingCount; + ) (\itemHere. + + maybeItemAbove <- scan left; + case maybeItemAbove (\_. fail "Expected an item here.") (\itemAbove. + let num = getNumberForBase itemAbove in + if (num >= 0) { + let complementNum = getComplementNumber num in + let complementItem = getBaseForNumber complementNum in + if (complementItem == itemHere) { + move; + waitUntilHere $ remainingCount - 1; + } { + create "pixel (R)"; + } + } { + fail "Expected nonnegative item index." + } + ); + ); + } { + create "pixel (G)"; + }; + end; + +def waitUntilEmpty = + watch down; + wait 1000; + emptyHere <- isempty; + if emptyHere {} { + waitUntilEmpty; + } + end; + +def waitForReset = + backup; + waitUntilEmpty; + end; + +def go = \startingLoc. + instant $ waitUntilHere 32; + waitForReset; + teleport self startingLoc; + go startingLoc; + end; + +loc <- whereami; +go loc; diff --git a/data/scenarios/Challenges/dna.yaml b/data/scenarios/Challenges/dna.yaml new file mode 100644 index 000000000..369510ff4 --- /dev/null +++ b/data/scenarios/Challenges/dna.yaml @@ -0,0 +1,593 @@ +version: 1 +name: DNA +author: Karl Ostmo +description: | + Copy strands of DNA in the lab to replicate organisms +creative: false +seed: 9 +attrs: + - name: floorattr + bg: "#111111" + - name: wall2 + fg: "#88ee22" + - name: pink + fg: "#ff99bb" + - name: lemon + fg: "#eeee99" + - name: clover + bg: "#225522" + - name: beige + fg: "#f5f5dc" + - name: soup + fg: "#338822" + bg: "#774422" +terrains: + - name: floor + attr: floorattr + description: | + Laboratory floor +objectives: + - id: place_flower + teaser: Place subject + goal: + - | + The laboratory building ahead is neatly landscaped, though + its decorative `pond`{=structure} exudes a peculiar quality. + There may be a device you can `use` to sample its contents. + + The DNA lab has two large apparatus inside. + The first is a `DNA sequencer`{=structure} that + determines the arrangement of ATCG pairs within an `organism`{=tag}'s genome. + + The `DNA decoder`{=structure} in the lab's south wing can reconstitute + an `organism`{=tag} from its genome. + + To get started, borrow a `dahlia`{=entity} from the flowerbed at the lab + entrance and place it in the receptacle directly to your east. + condition: | + r <- robotnamed "lab"; + as r { + has "pixel (G)"; + } + - id: place_complements + teaser: Complement strand + prerequisite: place_flower + goal: + - | + The `DNA sequencer`{=structure} will begin to decode the subject's DNA. + The subject will be dematerialized once the top row + of the "double helix" has been decoded. + + You must then complete the double helix (fill in the second row within the device) + with complementary base pairs. + Use your `soup strainer`{=entity} to obtain ingredients. + Consult your *Compendium* for appropriate pairings. + + Note that `scan`ning has a cost, and you have a limited supply of + `specimen slide`{=entity}s (in a `slide box`{=entity}) with which to + `scan` DNA bases using your `microscope`{=entity}. + condition: | + r <- robotnamed "topchecker"; + as r { + has "pixel (G)"; + } + - id: create_clone + prerequisite: place_complements + teaser: Create clone + goal: + - | + Now let's make a clone! Recreate the top half of the + DNA strand in the south wing's `DNA decoder`{=structure} + (you remember the sequence, right?). + + Each element you `place` will be automatically complemented. + + 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; + + k <- robotnamed "keeper"; + as k { + checkMember 0; + } + - prerequisite: create_clone + teaser: More clones + goal: + - | + Toggle the switch off to reset the lab. + - | + 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; + + k <- robotnamed "keeper"; + as k { + c <- countMembers 0 0; + return $ c >= 4; + } +robots: + - name: base + dir: east + loc: [-8, 4] + devices: + - antenna + - ADT calculator + - barcode reader + - branch predictor + - comparator + - compass + - dictionary + - feeler + - grabber + - hearing aid + - lambda + - logger + - microscope + - rolex + - siphon + - soup strainer + - strange loop + - switch puller + - treads + - welder + - workbench + inventory: + - [1, slide box] + - name: lab + system: true + dir: east + display: + invisible: true + attr: 'robot' + devices: + - switch puller + inventory: + - [1, branch predictor] + - [1, treads] + - [1, beaglepuss] + - [1, antenna] + - [1, comparator] + - [1, clock] + - [1, workbench] + - [1, grabber] + - [1, ADT calculator] + - [1, dictionary] + - [1, lambda] + - [1, logger] + - [1, welder] + - [1, hearing aid] + - [1, scanner] + - [1, strange loop] + - [1, solar panel] + program: | + run "data/scenarios/Challenges/_dna/lab.sw" + - name: topchecker + system: true + dir: east + display: + invisible: true + attr: 'robot' + program: | + run "data/scenarios/Challenges/_dna/topchecker.sw" + - name: mirrorbot + system: true + dir: south + display: + invisible: true + attr: 'robot' + program: | + run "data/scenarios/Challenges/_dna/mirrorbot.sw" + - name: resetter + description: resets the apparatus after completion by watching the switch + system: true + dir: east + display: + invisible: true + attr: 'robot' + program: | + run "data/scenarios/Challenges/_dna/resetter.sw" + - name: keeper + description: keeps track of cloned specimens + system: true + dir: south + display: + invisible: true + attr: 'robot' +solution: | + run "data/scenarios/Challenges/_dna/solution.sw" +structures: + - name: pond + recognize: + - north + structure: + mask: '.' + palette: + 'x': [grass, rock] + 'w': [dirt, pond broth] + map: | + ...xxxxxxxx.. + ..xwwwwwwwwx. + ..xwwwwwwwwwx + ..xwwwwwwwwwx + .xwwwwwwxxxx. + xwwwwwwx..... + xwwwwwwx..... + .xxxxxx...... + - name: fairy ring + structure: + mask: '.' + palette: + 'm': [grass, mushroom] + map: | + ....mm.m... + ..m........ + .........m. + ..m........ + ...m...m... + - name: receptacle + structure: + mask: '.' + palette: + 'x': [floor, wall] + 'w': [floor, wall2] + 'f': + cell: [floor, erase] + waypoint: + name: receiver + map: | + ww........ + fwxxxxxxxx + ww.x.x.x.x + - name: decoder base + structure: + palette: + '┬': [stone, down and horizontal wall] + '┴': [stone, up and horizontal wall] + ':': [floor, erase] + map: | + ┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬ + :::::::::::::::::::::::::::::::: + :::::::::::::::::::::::::::::::: + ┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴ + - name: copybots overlay + structure: + mask: '.' + palette: + 'm': [floor, erase, mirrorbot] + map: | + ................................ + mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm + ................................ + ................................ + - name: decoder overlay + structure: + mask: '.' + palette: + 'c': [floor, erase, topchecker] + map: | + ................................ + ................................ + c............................... + ................................ + - name: DNA decoder + recognize: + - north + structure: + placements: + - src: decoder base + - src: copybots overlay + - src: receptacle + offset: [18, -4] + orient: + up: south + map: "" + - name: DNA sequencer + recognize: + - north + structure: + placements: + - src: decoder base + - src: decoder overlay + - src: receptacle + offset: [4, 3] + map: "" +entities: + - name: barcode reader + display: + attr: red + char: 'S' + description: + - Reads the 'tag' of an item + properties: [pickable] + capabilities: [hastag, tagmembers] + - name: dahlia + display: + char: '*' + attr: pink + description: + - Brightly colored flower + properties: [known, pickable, growable] + growth: + duration: [20, 30] + tags: [organism] + - name: daisy + display: + char: 'x' + attr: lemon + description: + - Pretty flower + properties: [pickable, growable] + growth: + duration: [15, 30] + tags: [organism] + - name: clover + display: + char: 'c' + attr: clover + description: + - A tiny, 3-leaf plant + properties: [pickable, growable] + growth: + duration: [10, 20] + tags: [organism] + - name: mushroom + display: + char: 'm' + attr: beige + description: + - Invasive fungus + properties: [pickable, growable] + growth: + duration: [15, 25] + spread: + radius: 2 + density: 0.5 + biomes: [floor, stone] + tags: [organism] + - name: wall2 + display: + char: 'w' + attr: wall2 + description: + - Just a wall + properties: [known, unwalkable, boundary] + - name: primordial soup + display: + char: 's' + attr: soup + description: + - Can be decomposed into nucleic acids + properties: [pickable, liquid] + - name: pond broth + display: + char: 's' + attr: soup + description: + - Can be decomposed into nucleic acids + properties: [pickable, liquid] + - name: soup strainer + display: + char: 'r' + description: + - Extracts nucleic acids from `primordial soup`{=entity} + properties: [known, pickable] + - name: feeler + display: + char: 'm' + description: + - Senses presence of specific entities + properties: [known, pickable] + capabilities: [blocked, ishere, isempty] + - name: microscope + display: + char: 'm' + description: + - Scan for the cost of 1 `specimen slide`{=entity} + properties: [known, pickable] + capabilities: + - capability: scan + cost: + - [1, "specimen slide"] + - name: specimen slide + display: + char: 'i' + attr: device + description: + - Can take a sample of DNA for scanning + - name: slide box + display: + char: 'b' + attr: device + description: + - Contains `specimen slides`{=entity} + - name: guanine + display: + char: 'G' + attr: red + description: + - One of the four nucleobases in DNA. + - Is paired with `cytosine`{=entity}. + properties: [known] + - name: cytosine + display: + char: 'C' + attr: blue + description: + - One of the four nucleobases in DNA. + - Is paired with `guanine`{=entity}. + properties: [known] + - name: adenine + display: + char: 'A' + attr: green + description: + - One of the four nucleobases in DNA. + - Is paired with `thymine`{=entity}. + properties: [known] + - name: thymine + display: + char: 'T' + attr: gold + description: + - One of the four nucleobases in DNA. + - Is paired with `adenine`{=entity}. + properties: [known] + - name: switch (off) + display: + attr: red + char: '•' + description: + - A control in the deactivated position. + properties: [known] + - name: switch (on) + display: + attr: green + char: '•' + description: + - A control in the activated position. + properties: [known] + - name: organic sludge + display: + attr: green + char: 'S' + description: + - A repulsive, shuddering mass of slime. + properties: [pickable] + - name: switch puller + display: + char: 'P' + attr: gold + capabilities: [drill] + description: + - Can pull switches + - name: siphon + display: + char: 's' + attr: device + capabilities: [drill] + description: + - Can retrieve liquid +recipes: + - in: + - [1, slide box] + out: + - [32, specimen slide] + - in: + - [1, primordial soup] + out: + - [4, cytosine] + - [4, guanine] + - [4, adenine] + - [4, thymine] + required: + - [1, soup strainer] + - in: + - [1, switch (off)] + out: + - [1, switch (on)] + required: + - [1, switch puller] + - in: + - [1, switch (on)] + out: + - [1, switch (off)] + required: + - [1, switch puller] + - in: + - [1, pond broth] + out: + - [8, primordial soup] + required: + - [1, siphon] +known: [flower, rock] +world: + dsl: | + let + flowerNoise = perlin seed 1 0.15 0.0, + soupNoise = perlin seed 2 0.04 1.0, + + flowers = flowerNoise > 0.70, + soup = soupNoise > 0.9 + in + overlay + [ {grass} + , mask (flowers && (x - y) % 3 == 0) {daisy} + , mask soup {clover} + ] + upperleft: [-2, 4] + mask: 'q' + palette: + 'z': [stone, wall] + '.': [stone, erase] + 'G': + cell: [stone, erase] + 'o': [stone, erase] + 'd': [dirt, dahlia] + 'v': [stone, erase, lab, keeper] + '0': + cell: [stone, switch (off), resetter] + waypoint: + name: switch + 'D': + structure: + name: DNA decoder + cell: [floor, erase] + 'E': + structure: + name: DNA sequencer + cell: [floor, erase] + placements: + - src: pond + offset: [-17, -10] + - src: fairy ring + offset: [-5, -25] + map: | + dzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz + dzv.........................................z + zzz........oo...............................z + ............o...............................z + zzz........oo...............................z + dz.....Eooooooooooooooooooooooooooooooo.....z + dz.....o..............................o.....z + dz.....o..............................o.....z + dz.....oooooooooooooooooooooooooooooooo.....z + qz..........................................z + qzzzzzzzzz.zzzzzzzzzzzzzzzzzzzzzzzz.zzzzzzzzz + qqqqqqqqqzGzqqqqqqqqqqqqqqqqqqqqqqzGzqqqqqqqq + qzzzzzzzzz.zzzzzzzzzzzzzzzzzzzzzzzz.zzzzzzzzz + qz..........................................z + qzz....D....................................z + qz0.........................................z + qzz.........................................z + qz..........................................z + qz..........................................z + qz..........................................z + qz..........................................z + qz..........................................z + qzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz diff --git a/test/integration/Main.hs b/test/integration/Main.hs index 714e53eb9..67341427f 100644 --- a/test/integration/Main.hs +++ b/test/integration/Main.hs @@ -241,6 +241,7 @@ testScenarioSolutions rs ui key = , testSolution (Sec 10) "Challenges/gopher" , testSolution (Sec 5) "Challenges/hackman" , testSolution (Sec 5) "Challenges/blender" + , testSolution (Sec 10) "Challenges/dna" , testSolution (Sec 10) "Challenges/hanoi" , testSolution (Sec 3) "Challenges/lights-out" , testSolution (Sec 10) "Challenges/Sliding Puzzles/3x3"