Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Counting flowers #2249

Merged
merged 3 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a (off-by-one) bug in my code that I finally diagnosed by entering creative mode to use my own resonate command. Perhaps that is the intended use of creative mode, but it may be fun to have an "in-scenario" method to double-check one's count. Perhaps some oblique way to obtain a gradiator that is an instant-lose condition, but nonetheless can help a player debug.

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:
byorgey marked this conversation as resolved.
Show resolved Hide resolved
- 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]
byorgey marked this conversation as resolved.
Show resolved Hide resolved
- [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
byorgey marked this conversation as resolved.
Show resolved Hide resolved
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
Loading