diff --git a/data/scenarios/Challenges/00-ORDER.txt b/data/scenarios/Challenges/00-ORDER.txt index 453eee71d..f3620e926 100644 --- a/data/scenarios/Challenges/00-ORDER.txt +++ b/data/scenarios/Challenges/00-ORDER.txt @@ -21,6 +21,7 @@ friend.yaml pack-tetrominoes.yaml dimsum.yaml telephone.yaml +flower-count.yaml Mazes Ranching Sokoban diff --git a/data/scenarios/Challenges/_flower-count/judge.sw b/data/scenarios/Challenges/_flower-count/judge.sw new file mode 100644 index 000000000..ad6d5e75b --- /dev/null +++ b/data/scenarios/Challenges/_flower-count/judge.sw @@ -0,0 +1,35 @@ +def win = + try { + firestarter <- robotNamed "firestarter"; + halt firestarter + } {}; + try { + fire <- robotNamed "fire"; + halt fire; wait 1; + reprogram fire { selfdestruct }; + } {}; + create "gold" +end + +def judgeCount : Int -> Cmd Unit = \actual. + watch down; + wait 1024; + s <- scan down; + case s + (\_. return ()) + (\p. + try { + let c = (read p : Int) in + if (c == actual) { win } {} + } {} + ) +end + +def forever = \c. c; forever c end + +def judge = + numFlowers <- resonate "flower" ((-59,-19),(60,20)); + forever (judgeCount numFlowers); +end; + +judge diff --git a/data/scenarios/Challenges/_flower-count/solution.sw b/data/scenarios/Challenges/_flower-count/solution.sw new file mode 100644 index 000000000..437a37750 --- /dev/null +++ b/data/scenarios/Challenges/_flower-count/solution.sw @@ -0,0 +1,114 @@ +def doN : Int -> Cmd a -> Cmd Unit = \n. \c. + if (n == 0) {} {c; doN (n-1) c} +end + +def abs : Int -> Int = \n. + if (n < 0) {-n} {n} +end + +// Go to the given absolute coordinates. End facing east. +def goto : Int * Int -> Cmd Unit = \dest. + cur <- whereami; + let x = fst cur in + let y = snd cur in + let dx = fst dest - x in + let dy = snd dest - y in + if (dx < 0) {turn west} {turn east}; + doN (abs dx) move; + if (dy < 0) {turn south} {turn north}; + doN (abs dy) move; + turn east; +end + +def liftA2 : (a -> b -> c) -> Cmd a -> Cmd b -> Cmd c = \f. \ca. \cb. + a <- ca; + b <- cb; + return (f a b) +end + +def add : Cmd Int -> Cmd Int -> Cmd Int = liftA2 (\x. \y. x + y) end + +def countCell : Cmd Int = + s <- scan down; + return $ case s + (\_. 0) + (\t. if (t == "flower") {1} {0}) +end + +tydef List a = rec l. Unit + (a * l) end + +def sum : List Int -> Int = \l. + case l + (\_. 0) + (\cons. fst cons + sum (snd cons)) +end + +def for : Int -> (Int -> Cmd a) -> Cmd (List a) = \n. \k. + if (n == 0) {return (inl ())} {a <- k n; b <- for (n-1) k; return (inr (a,b))} +end + +def countRow : Int -> Cmd Int = \w. + ns <- for (w-1) (\_. n <- countCell; move; return n); + last <- countCell; + return (sum ns + last) +end + +def isEven : Int -> Bool = \n. (n / 2) * 2 == n end + +def around : Dir -> Cmd Unit = \d. turn d; move; turn d end + +// countFlowers (w,h) (x,y) counts the number of flowers +// in the w by h rectangle with lower-left corner at (x,y) +def countFlowers : Int * Int -> Int * Int -> Cmd Int = \size. \ll. + goto ll; + let w = fst size in + let h = snd size in + cnts <- for (h-1) (\i. + cnt <- countRow w; + if (isEven i) { around right } { around left }; + return cnt + ); + last <- countRow w; + return (sum cnts + last) +end + +def acquire : Cmd Text = + thing <- atomic (b <- isempty; if b {return ""} {grab}); + if (thing == "") {acquire} {return thing} +end + +def countAndReport : Int * Int -> Int * Int -> Cmd Unit = \size. \ll. + cnt <- countFlowers size ll; + goto (0,0); + paper <- acquire; + let soFar = (read paper : Int) in + erase paper; + newPaper <- print "paper" (format (soFar + cnt)); + place newPaper; +end + +def until : Cmd Bool -> Cmd a -> Cmd Unit = \test. \body. + b <- test; if b {} {body; until test body} +end + +def acquireFlower : Cmd Unit = + until (ishere "flower") move; grab; return () +end + +def go = + for 4 (\r. + for 3 (\c. + build {countAndReport (40,10) (-59 + 40*(c-1), -19 + 10*(r-1))} + ) + ); + print "paper" "0"; + turn left; move; place "paper: 0"; + wait 1024; + acquireFlower; + turn back; + goto (20,0); + res <- meet; + case res (\_. return ()) (\truelove. give truelove "flower") +end; + +go; diff --git a/data/scenarios/Challenges/flower-count.yaml b/data/scenarios/Challenges/flower-count.yaml new file mode 100644 index 000000000..2dbda868f --- /dev/null +++ b/data/scenarios/Challenges/flower-count.yaml @@ -0,0 +1,178 @@ +version: 1 +name: Flower Count +author: Brent Yorgey +description: | + Count the flowers quickly... or else! +creative: false +objectives: + - id: count_flowers + teaser: Count the flowers! + goal: + - | + Your evil nemesis, the sadistic arithmomaniac supervillain Count Nemesis, is + at it again! They have wired up some explosives to your + `True Love`{=robot} and lit the fuse, but promised to stop it... IF you can + count all the flowers in time! + - | + Specifically, Count Nemesis demands that you count the + total number of flowers in the 120x40 field bounded by walls, + `print` the number on a piece of `paper`{=entity}, and place + the paper at the origin, `(0,0)`. If the number is correct, + the countdown stops and your True Love is saved. If the + number is incorrect... nothing happens, but the fuse continues + to burn! + - | + And watch out, Count Nemesis won't abide anyone picking his flowers. + condition: | + judge <- robotNamed "judge"; + as judge { has "gold" } + - id: pick_flower + hidden: true + optional: true + teaser: You just don't listen, do you + goal: + - | + I told you not to pick any flowers! Now your True Love is + dead... because of YOU! + - | + Perhaps you would like to go back in time and try again. + condition: | + f <- as base { has "flower" }; + judge <- robotNamed "judge"; + g <- as judge { has "gold" }; + return (not g && f) + - id: out_of_time + hidden: true + optional: true + teaser: Not fast enough + goal: + - | + You were not fast enough, and now your True Love is + dead... because of YOU! + - | + Perhaps you would like to go back in time and try again. + condition: | + truelove <- robotNamed "truelove"; + as truelove { b <- ishere "fuse"; return (not b) } + - id: joinpoint + hidden: true + teaser: Follow instructions! + prerequisite: + logic: + and: + - count_flowers + - not: pick_flower + - not: out_of_time + condition: | + return true + - id: win + teaser: Give a flower + prerequisite: joinpoint + goal: + - | + Congratulations! You foiled the plan of evil Dr. Nemesis and + saved your True Love! The only thing left is to give your + True Love a `flower`{=entity}! + condition: | + truelove <- robotNamed "truelove"; + as truelove { has "flower" } +solution: | + run "scenarios/Challenges/_flower-count/solution.sw" +robots: + - name: base + dir: north + devices: + - solar panel + - treads + - antenna + - comparator + - ADT calculator + - workbench + - grabber + - dictionary + - lambda + - logger + - welder + - scanner + - strange loop + - typewriter + - 3D printer + - branch predictor + - clock + - GPS receiver + - compass + - counter + inventory: + - [12, solar panel] + - [12, dictionary] + - [12, lambda] + - [12, treads] + - [12, branch predictor] + - [12, comparator] + - [12, hyperloop] + - [12, compass] + - [12, scanner] + - [12, logger] + - [12, GPS receiver] + - [12, string] + - [12, typewriter] + - [12, rubber band] + - [12, grabber] + - [12, parsley] + - [1, paper] + - name: igniter + system: true + dir: east + devices: + - logger + program: 'ignite down' + - name: judge + system: true + dir: east + devices: + - logger + program: | + run "scenarios/Challenges/_flower-count/judge.sw" + - name: truelove + system: true + display: + invisible: false + attr: red + char: '♥' + description: Your One True Love. +attrs: + - name: fuse + fg: '#cccccc' + bg: '#002f00' +entities: + - name: fuse + display: + attr: fuse + char: '-' + description: + - Slow-burning fuse + properties: [combustible, known, boundary] + combustion: + ignition: 20 + duration: [64, 64] + delay: 63 + product: ash +known: [flower, wall, ash] +world: + dsl: | + overlay + [ if (hash % 7 <= 2) then {grass, flower} else {grass} + , mask ((x == 61 || x == (-60)) && y <= 21 && y >= -20) {wall, stone} + , mask ((y == 21 || y == (-20)) && x <= 61 && x >= -60) {wall, stone} + ] + upperleft: [0, 0] + offset: false + palette: + 'B': [grass, null, base] + 'J': [grass, erase, judge] + '.': [grass] + '-': [grass, fuse] + 'L': [grass, fuse, truelove] + 'I': [grass, fuse, igniter] + map: | + JB..I---------------L diff --git a/test/integration/Main.hs b/test/integration/Main.hs index 7230e0c42..ca4966fd1 100644 --- a/test/integration/Main.hs +++ b/test/integration/Main.hs @@ -250,6 +250,7 @@ testScenarioSolutions rs ui key = , testSolution (Sec 10) "Challenges/dimsum" , testSolution (Sec 15) "Challenges/gallery" , testSolution (Sec 10) "Challenges/telephone" + , testSolution (Sec 10) "Challenges/flower-count" , testGroup "Mazes" [ testSolution Default "Challenges/Mazes/easy_cave_maze"