Remove test challenges and add some more interesting ones (#669)

Remove a couple challenges which were just there as placeholders (`test` and `drill_test`; the latter is now moved to `Testing`), and add more interesting `2048` and `hanoi` challenges.

Closes #635 .
This commit is contained in:
Brent Yorgey 2022-09-17 13:31:39 -05:00 committed by GitHub
parent 7942ac1215
commit 120b7b4b79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 646 additions and 49 deletions

View File

@ -1,5 +1,5 @@
test.yaml
chess_horse.yaml
drill_test.yaml
teleport.yaml
2048.yaml
hanoi.yaml
Mazes

View File

@ -0,0 +1,193 @@
name: "2048"
description: Make 2048!
objectives:
- goal:
- OK, OK, it's not really the same as the classic "2048" game.
However, your goal is still to make 2048! You start with a `1`
which regrows immediately when
it is harvested, so if you plant it, you can get as many as you want. Your
job is to combine `1`s in order to make a `2048`
entity.
- "Hint: the `format` command can turn numbers into strings!"
condition: |
try {
as base {has "2048"}
} { return false }
solution: |
def makeN : int -> cmd () = \n.
if (n == 1)
{harvest; return ()}
{makeN (n/2); makeN (n/2); make (format n)}
end;
place "1";
makeN 2048
entities:
- name: "1"
display:
attr: gold
char: "#"
description:
- This is a one. Maybe you can combine it with other ones
somehow.
properties: [growable, portable, infinite]
- name: "2"
display:
attr: gold
char: "#"
description:
- This is a two. Maybe you can combine it with other twos
somehow.
properties: [portable]
- name: "4"
display:
attr: gold
char: "#"
description:
- This is a four. You get the idea.
properties: [portable]
- name: "8"
display:
attr: gold
char: "#"
description:
- An eight.
properties: [portable]
- name: "16"
display:
attr: gold
char: "#"
description:
- A 16.
properties: [portable]
- name: "32"
display:
attr: gold
char: "#"
description:
- A 32.
properties: [portable]
- name: "64"
display:
attr: gold
char: "#"
description:
- A 64.
properties: [portable]
- name: "128"
display:
attr: gold
char: "#"
description:
- A 128.
properties: [portable]
- name: "256"
display:
attr: gold
char: "#"
description:
- A 256.
properties: [portable]
- name: "512"
display:
attr: gold
char: "#"
description:
- A 512.
properties: [portable]
- name: "1024"
display:
attr: gold
char: "#"
description:
- A 1024.
properties: [portable]
- name: "2048"
display:
attr: gold
char: "#"
description:
- A 2048.
properties: [portable]
recipes:
- in:
- [2, "1"]
out:
- [1, "2"]
- in:
- [2, "2"]
out:
- [1, "4"]
- in:
- [2, "4"]
out:
- [1, "8"]
- in:
- [2, "8"]
out:
- [1, "16"]
- in:
- [2, "16"]
out:
- [1, "32"]
- in:
- [2, "32"]
out:
- [1, "64"]
- in:
- [2, "64"]
out:
- [1, "128"]
- in:
- [2, "128"]
out:
- [1, "256"]
- in:
- [2, "256"]
out:
- [1, "512"]
- in:
- [2, "512"]
out:
- [1, "1024"]
- in:
- [2, "1024"]
out:
- [1, "2048"]
robots:
- name: base
dir: [1,0]
display:
attr: robot
char: "Ω"
devices:
- logger
- harvester
- dictionary
- clock
- workbench
- solar panel
- comparator
- calculator
- string
- life support system
- branch predictor
- strange loop
- lambda
inventory:
- [1, "1"]
known: [water, wavy water, flower, tree]
world:
default: [stone]
palette:
"Ω": [grass, null, base]
"┌": [stone, upper left corner]
"┐": [stone, upper right corner]
"└": [stone, lower left corner]
"┘": [stone, lower right corner]
"─": [stone, horizontal wall]
"│": [stone, vertical wall]
upperleft: [-1, 3]
map: |
┌─┐
│Ω│
└─┘

View File

@ -0,0 +1,62 @@
def repeat = \c. force c; repeat c end;
def i2e = \i.
if (i == 2) { "two" } {
if (i == 3) { "three" } {
fail $ "Fatal error: There should be only 2 or 3 entities placed at any time: " ++ format i
}}
end;
def cscan = \d.
s <- scan d;
if (s == inl ()) {return 0} {return 1}
end;
def count_column =
i <- cscan north;
j <- cscan down;
k <- cscan south;
// log "one column";
// wait 8;
// log (format i);
// wait 8;
// log (format j);
// wait 8;
// log (format k);
// wait 8;
return (i + j + k)
end;
repeat {
sum <- as self {
// left column
teleport self (-2,-2);
x <- count_column;
// middle column
teleport self (0,-2);
y <- count_column;
// right column
teleport self (2,-2);
z <- count_column;
// DEBUG
// log "all columns";
// wait 8;
// log (format x);
// wait 8;
// log (format y);
// wait 8;
// log (format z);
return $ i2e (x + y + z)
};
//let sum = i2e (x + y + z) in
teleport self (0,-6);
counted <- scan down;
//wait 8;
//log (format counted);
case counted (\e.
fail $ "Fatal error: there should always be a count entity at (0,-6)! " ++ format e ++ " " ++ format counted
) (\e.
if (e == sum) {} {swap sum; return ()}
)
}

View File

@ -0,0 +1,48 @@
// if
// 0. all (but max 3) disks in my column are sorted
// then
// - place "OK"
// else
// - try to grab "OK"
def null = inl () end;
def repeat = \c. c; repeat c end;
def toI = \e.
if (e == "one" || e == "blocked one") {1} {
if (e == "two" || e == "blocked two") {2} {
if (e == "three" || e == "blocked three") {3} {
fail $ "There should be no other placeable entity: " ++ e
}}}
end;
def f = \x.\y.
case x (\_. false) (\i.
case y (\_. false) (\j.
let xi = toI i in
let yj = toI j in
xi > yj
)
)
end;
w <- whereami;
// the middle of the column
let a = (fst w, snd w + 3) in
repeat (
o <- as self {
teleport self a;
x <- scan south;
y <- scan down;
z <- scan north;
if (z == null) {
if (y == null) {
return true
} {
return $ f x y
}
} {
return $ f x y && f y z
}
};
try {
if o {place "OK"} {grab; return ()}
} {}
)

View File

@ -0,0 +1,64 @@
def repeat = \c. c; repeat c end;
def isUnlocked = \e. e == "one" || e == "two" || e == "three" end;
def unlock = \e.
if (e == "blocked one") {"one"} {
if (e == "blocked two") {"two"} {
if (e == "blocked three") {"three"} {
fail $ "Can not unlock: " ++ e
}}}
end;
repeat (
me <- scan down;
case me (\_. return ()) (\e.
// if
// 0. I stand on unlocked X
// 1. place north of me is NOT empty
// or
// the count of all placed is NOT 3
// then
// - lock X
if (isUnlocked e)
{
northFullOrAllPlaced <- as self {
mn <- scan north;
case mn (\_.
teleport self (0,-6);
allPlaced <- ishere "three";
return (not allPlaced)
) (\_.
return true
);
};
if northFullOrAllPlaced {
swap ("blocked " ++ e); return ()
} {}
}
// if
// 0. I stand on locked X
// 1. place north of me is empty
// 2. all disks are placed
// 3. other columns are sorted (check "OK")
// then
// - unlock X
{
mn <- scan north;
case mn (\_.
wait 16;
allPlaced <- as self {
teleport self (0,-6);
ishere "three"
};
allSorted <- as self {
teleport self (-2,-5);
o1 <- ishere "OK";
teleport self (0,-5);
o2 <- ishere "OK";
teleport self (2,-5);
o3 <- ishere "OK";
return (o1 && o2 && o3)
};
if (allPlaced && allSorted) {swap (unlock e); return ()} {}
) (\_. return ())
}
))

View File

@ -0,0 +1,64 @@
def until = \p. \c. q <- p; if q {} {c; until p c} end;
def rep = \n. \c. if (n == 0) {} {c; rep (n-1) c} end;
def ifC = \p. \t. \e. res <- p; if res t e end;
def orC = \c1. \c2.
b1 <- c1; b2 <- c2; return (b1 || b2)
end;
def somethingHere =
res <- scan down;
return (res != inl ())
end;
def fwdToThing = until blocked move end;
def fwdToBlank =
move;
until (orC blocked somethingHere) move;
ifC somethingHere {turn back; move; turn back} {}
end;
def goBack = turn back; fwdToBlank; turn back end;
def getDisk =
fwdToThing;
d <- grab;
goBack;
return d
end;
def placeDisk = \d.
fwdToBlank;
place d;
goBack
end;
def moveToCol = \w.\x.
if (w < x) { turn east; rep (x - w) move }
{ if (w > x) { turn west; rep (w - x) move } {} };
turn south
end;
def hanoi :
int -> // The number of disks in each column
int -> // Current column (basically offset of all columns)
int -> // The offset to first column
int -> // The offset to second column
int -> // The offset to third column
cmd int
= \n. \o. \a. \b. \c.
if (n == 0) {return o}
{
o_new <- hanoi (n-1) o a c b;
moveToCol o_new a;
wait 8;
d <- getDisk;
moveToCol a c;
placeDisk d;
hanoi (n-1) c b a c;
}
end;
hanoi 3 0 (-2) 0 2

View File

@ -0,0 +1,190 @@
name: Towers of Hanoi
description: The classic Towers of Hanoi puzzle with three disks.
objectives:
- goal:
- Move all the numbers (traditionally, "disks") from the
left column to the right column.
- You may only pick up one disk at a time, and you may never
place a larger disk on top of a smaller one.
condition: |
teleport self (2,-1);
x <- ishere "one";
teleport self (2,-2);
y <- ishere "blocked two";
teleport self (2,-3);
z <- ishere "blocked three";
return (x && y && z)
solution: |
run "data/scenarios/Challenges/hanoi-solution.sw"
robots:
- name: base
dir: [0,-1]
devices:
- net
- treads
- logger
- grabber
- dictionary
- branch predictor
- comparator
- strange loop
- compass
- clock
- scanner
- ADT calculator
- name: invariant
dir: [0,0]
system: true
display:
invisible: true
devices:
- logger
inventory:
- [1, one]
- [1, two]
- [1, three]
- [1, blocked one]
- [1, blocked two]
- [1, blocked three]
program: |
// if
// 0. I stand on locked X
// 1. place north of me is empty
// 2. all disks are placed
// 3. other columns are sorted (check "OK")
// then
// - unlock X
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// if
// 0. I stand on unlocked X
// 1. place noth of me is NOT empty
// then
// - lock X
run "data/scenarios/Challenges/hanoi-invariant.sw"
- name: increasing
dir: [0,0]
system: true
display:
invisible: true
inventory:
- [1, OK]
devices:
- logger
program: |
// if
// 0. all (but max 3) disks in my column are sorted
// then
// - place "OK"
// else
// - try to grab "OK"
run "data/scenarios/Challenges/hanoi-increasing.sw"
- name: count
dir: [0,0]
system: true
display:
invisible: true
inventory:
- [1, two]
- [0, three]
devices:
- logger
program: |
// count all entities placed in columns
// the final count should be either 2 or 3
// place "two" or "three" at x=0 y=-6
run "data/scenarios/Challenges/hanoi-count.sw"
entities:
- name: bottom tee
display:
char: '┴'
attr: entity
description:
- A bottom tee wall.
properties: [unwalkable]
- name: three
display:
char: '3'
attr: gold
description:
- A disk of radius 3.
properties: [portable]
- name: two
display:
char: '2'
attr: gold
description:
- A disk of radius 2.
properties: [portable]
- name: one
display:
char: '1'
attr: gold
description:
- A disk of radius 1.
properties: [portable]
- name: blocked one
display:
char: '1'
attr: entity
description:
- A disk of radius 1.
properties: [unwalkable]
- name: blocked two
display:
char: '2'
attr: entity
description:
- A disk of radius 2.
properties: [unwalkable]
- name: blocked three
display:
char: '3'
attr: entity
description:
- A disk of radius 3.
properties: [unwalkable]
- name: OK
display:
char: 'O'
attr: gold
description:
- This entity signals that the column is sorted.
known:
- bottom tee
- OK
- one
- two
- three
- blocked one
- blocked two
- blocked three
world:
default: [grass, null]
palette:
' ': [grass]
'_': [stone]
'v': [stone, null, base]
'┌': [stone, upper left corner]
'┐': [stone, upper right corner]
'└': [stone, lower left corner]
'┘': [stone, lower right corner]
'─': [stone, horizontal wall]
'│': [stone, vertical wall]
'┴': [stone, bottom tee]
'1': [stone, one, invariant]
'2': [stone, blocked two, invariant]
'3': [stone, blocked three, invariant]
'.': [stone, null, invariant]
'^': [grass, null, increasing]
'X': [grass, three, count]
upperleft: [-3, 1]
map: |
┌─────┐
│__v__│
│1│.│.│
│2│.│.│
│3│.│.│
└─┴─┴─┘
^ ^ ^
X

View File

@ -1,39 +0,0 @@
name: Test challenge
description: A very difficult challenge that will test the utmost limits of your ability to 'move' and 'grab'. This is actually a placeholder challenge and will be replaced with more suitable challenges soon.
objectives:
- goal:
- Use your grabber to harvest a lambda.
condition: |
try {
as base {has "lambda"}
} { return false }
solution: |
move; move; grab
robots:
- name: base
loc: [0,0]
dir: [1,0]
devices:
- treads
- logger
- grabber
inventory:
- [0, lambda]
world:
default: [blank]
palette:
'.': [grass]
'┌': [stone, upper left corner]
'┐': [stone, upper right corner]
'└': [stone, lower left corner]
'┘': [stone, lower right corner]
'─': [stone, horizontal wall]
'│': [stone, vertical wall]
'λ': [grass, lambda]
upperleft: [-1, 1]
map: |
┌─────┐
│..λ..│
│....λ│
│...λ.│
└─────┘

View File

@ -1,3 +1,4 @@
373-drill.yaml
394-build-drill.yaml
428-drowning-destroy.yaml
475-wait-one.yaml

View File

@ -1,5 +1,5 @@
name: Test drill
description: This is a developer playground and will be replaced with more suitable challenges soon.
name: Drill test
description: A developer playground for drilling.
objectives:
- goal:
- Send robots to mine rock, iron and copper.
@ -10,10 +10,17 @@ objectives:
s <- as base {has "rock"};
return (i && c && s)
} { return false }
solution: |
build {
move; drill forward; turn back; move; turn left;
move; move; drill left;
move; drill forward;
turn back; move; move; move;
give base "iron ore"; give base "copper ore"; give base "rock"
}
robots:
- name: base
loc: [0,-2]
dir: [1,0]
dir: [0,1]
display:
char: Ω
attr: robot
@ -27,10 +34,14 @@ robots:
- [1, drill]
- [3, logger]
- [3, compass]
- [5, solar panel]
- [5, treads]
- [5, grabber]
known: [water, wavy water]
world:
default: [ice, water]
palette:
'Ω': [grass, null, base]
'.': [grass]
' ': [ice, water]
'~': [ice, wavy water]
@ -44,13 +55,14 @@ world:
'│': [stone, vertical wall]
'A': [stone, mountain]
'C': [stone, copper vein]
'@': [stone, boulder]
'I': [stone, iron vein]
upperleft: [-1, 1]
map: |
┌─────┐ ~~
│IAAT~ ~L~
│..AAA│ ~~
....C│ ~
│..@AA│ ~~
Ω...C│ ~
└─────┘ ~
entities:
- name: copper vein

View File

@ -1672,7 +1672,7 @@ execConst c vs s k = do
isDestroyable r = do
if r ^. robotID == 0
then throwError $ cmdExn c ["You consider destroying your base, but decide not to do it after all."]
else return True
else return . not $ r ^. systemRobot
getRobotWithinTouch ::
(Has (State Robot) sig m, Has (State GameState) sig m, Has (Error Exn) sig m) =>

View File

@ -156,8 +156,9 @@ testScenarioSolution _ci _em =
, testGroup
"Challenges"
[ testSolution Default "Challenges/chess_horse"
, testSolution Default "Challenges/test"
, testSolution Default "Challenges/teleport"
, testSolution (Sec 5) "Challenges/2048"
, testSolution (Sec 10) "Challenges/hanoi"
, testGroup
"Mazes"
[ testSolution Default "Challenges/Mazes/easy_cave_maze"
@ -170,6 +171,7 @@ testScenarioSolution _ci _em =
"Regression tests"
[ expectFailBecause "Awaiting fix (#394)" $
testSolution Default "Testing/394-build-drill"
, testSolution Default "Testing/373-drill"
, testSolution Default "Testing/428-drowning-destroy"
, testSolution' Default "Testing/475-wait-one" $ \g -> do
let t = g ^. ticks