Skip to content

Commit

Permalink
Counting flowers (#2249)
Browse files Browse the repository at this point in the history
Another challenge scenario to showcase the use of `print`/`read`.
  • Loading branch information
byorgey authored Jan 5, 2025
1 parent f5c0700 commit 0c1d5f2
Show file tree
Hide file tree
Showing 5 changed files with 329 additions and 0 deletions.
1 change: 1 addition & 0 deletions data/scenarios/Challenges/00-ORDER.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ friend.yaml
pack-tetrominoes.yaml
dimsum.yaml
telephone.yaml
flower-count.yaml
Mazes
Ranching
Sokoban
Expand Down
35 changes: 35 additions & 0 deletions data/scenarios/Challenges/_flower-count/judge.sw
Original file line number Diff line number Diff line change
@@ -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
114 changes: 114 additions & 0 deletions data/scenarios/Challenges/_flower-count/solution.sw
Original file line number Diff line number Diff line change
@@ -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;
178 changes: 178 additions & 0 deletions data/scenarios/Challenges/flower-count.yaml
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions test/integration/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 0c1d5f2

Please sign in to comment.