World description DSL (#1376)

DSL for programming worlds, towards #1320 and #29 (and, indirectly, toward #50, since the world DSL should make a nice target for world saves) .  Eventually this should be able to recreate all the world description/building features we have, though there is still a long way to go.  But currently we can at least recreate the "classic" procedurally-generated world.  I think this is a solid foundation we can merge as a first step, and then work on adding more features in subsequent PRs.  Below are some notes that should help in reviewing.  Note that the large number of files changed is due in large part to the elimination of the `default` field in scenario descriptions; see the "changed files" section below for an overview of the important/interesting changes.

Issues split off from this one: #1394 #1395 #1396 #1397 

Major changes
============

- New `data/worlds` subdirectory
    - All `.world` files are parsed at load time and saved in a `WorldMap` which gets threaded through, similar to `EntityMap` (perhaps we should think about passing around a single record instead)
- Standard "classic" world
    - Used to be `testWorld2`, defined in Haskell code; now it is defined via the DSL in `worlds/classic.world`.  This should make it much easier to experiment with variations.
    - We can now automatically extract entities mentioned in a world DSL term with `extractEntities`.  There used to be an explicit list in `testWorld2Entities`, used to check pedagogy, generate documentation, etc., but it turns out it had (predictably) gotten out of date!  This can't happen anymore.
    - It is now referenced in several tutorials (backstory, farming, world101, speedruns, etc.)
- The `default` field of world descriptions is no more: one can use `dsl` to just specify a constant
    - Note in `Swarm.Game.State`, `dslWF` and `arrayWF` are combined using the `Monoid` instance to create `wf`.
- `Erasable`
    - It used to be the case that if some kind of default terrain + entity was specified (e.g. stone + water), any `map` would completely override the default.  However, we want to move towards combining everything with a `Monoid` instance.  But by default this means the default entity would show through anywhere the `map` did not specify an entity.  So we need a way to explicitly "erase" an entity from a lower layer.
    - If `e` is a `Semigroup`, then `Maybe e` is a `Monoid` where `Nothing` acts as an identity element.  Likewise, `Erasable e` is a `Monoid` but adds two new elements: `ENothing` to be an identity, and `EErase` to be an *annihilator*.  i.e. combining with `EErase` is like multiplying by zero.
    - We can now specify `erase` as an entity to override entity underneath.
    - There are several Haskell files with only changes related to `Erasable`, relating to e.g. the world editor, `PCells`, etc.; I'm not 100% sure I've always done the right thing here.

DSL overview
===========

- Integer, float, and Boolean literals.  Note that `3` is *always* an `int`, and `3.0` is a `float`.  It makes things much easier to not have to deal with `3` possibly being either `int` or `float`, though it does make things slightly more annoying for programmers.
- Standard boolean, arithmetic, and comparison operators
- `if ... then ... else ...`
- `<>` operator for combining via `Semigroup` instance
- Cell literals are enclosed in curly braces.  Unlike the previous awkward world description syntax with one, two, or three-element lists denoting terrain, terrain + entity, or terrain + entity + robot, there can now be any number of elements in any order.
    - `{foo}` will be resolved as either terrain, an entity, or a robot, whichever is successful.  So if the names are unambiguous one can just write `{tree}` or `{stone}`.
    - It is possible to explicitly indicate the type of cell value with syntax like `{entity: tree}` or `{terrain: stone}`.
    - Multiple items separated by commas is syntax sugar for combining with `<>`.  e.g. `{tree, entity: boulder, stone} = {tree} <> {entity: boulder} <> {stone}`.
- Ability to refer to the `seed`
- Refer to the current `x` or `y` coordinates or the `hash` of the current coordinates
- `let`-expressions for multiple variables: `let x1 = e1, x2 = e2, ... in ...`
- `overlay [e1, e2, ...]` layers `e1` on the bottom, `e2` on top of that, etc., using the `Semigroup` instance for world functions
- `"foo"` imports the DSL term in `worlds/foo.world`
- `perlin` function to generate perlin noise
- `mask` function to mask with a condition

Changed files
===========

- `Swarm.Util`: moved the `acquire` function here and gave it a more descriptive name.
- `Swarm.Doc.Gen`: can now extract mentioned entities directly.
- `Swarm.Game.Failure`: added new failure modes
- `Swarm.Game.Scenario.Topography.WorldDescription`: get rid of `defaultTerrain` field, add `worldProg` for DSL.
- `Swarm.Game.State`: see comment.
- `Swarm.Game.World`: a bit of reorganization.  Added a bunch of modules under this.
    - `Swarm.Game.World.Coords`: moved some code here from `Swarm.Game.World`.
    - `Swarm.Game.World.Gen`: moved some things here from `Swarm.Game.WorldGen` (also deleted a bunch of irrelevant code), and also added the `extractEntities` function to get all entities mentioned by a DSL term.
    - `Swarm.Game.World.Syntax`: raw, untyped syntax for world DSL terms.
    - `Swarm.Game.World.Parse`: parser for world DSL terms. Fairly standard.
    - `Swarm.Game.World.Typecheck`: takes raw, untyped terms produced by the parser and both typechecks and elaborates them into a simpler core language.  An interesting feature is that the core language is *type-indexed*, so that the Haskell type system is actually ensuring that our typechecker is correct; every typechecked world DSL term value has a type which is indexed by a Haskell type corresponding to the type of the underlying DSL term.  For example, `{entity: tree}` would have a type like `TTerm [] (World CellVall)` etc.  Once terms make it through the typechecker, there cannot possibly be any bugs in the rest of the pipeline which would result in a crash, because the Haskell type system.  (There could of course be *semantic* bugs.)  Understanding exactly how the typechecker works is not too important.  Of interest may be the `resolveCell` function, which determines how we decide what `Cell` is represented by a cell expression in curly braces.
    - `Swarm.Game.World.Abstract`: compile elaborated, typechecked world DSL terms down into an extremely simple core language with only constants and function application.  This gives us very fast evaluation of world DSL terms.  Understanding this module is not really necessary but there is a link to a blog post for those who are interested in how it works.
    - `Swarm.Game.World.Compile`: a further processing/compilation step after `Swarm.Game.World.Abstract`.  Currently we don't actually use this, since it doesn't seem like it makes a big efficiency difference.
    - `Swarm.Game.World.Interpret`: interpreter for abstracted world DSL terms.
    - `Swarm.Game.World.Eval`: just puts together the pieces of the pipeline to evaluate a typechecked world DSL term.
    - `Swarm.Game.World.Load`: just loading world DSL terms from disk.
This commit is contained in:
Brent Yorgey 2023-08-17 06:08:42 -05:00 committed by GitHub
parent 0179fa61b6
commit 888ee44d18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
170 changed files with 2537 additions and 593 deletions

View File

@ -26,6 +26,7 @@ import Swarm.Language.Pipeline (ProcessedTerm)
import Swarm.Language.Pipeline.QQ (tmQ)
import Swarm.TUI.Model (gameState)
import Swarm.TUI.Model.StateUpdate (classicGame0)
import Swarm.Util.Erasable
-- | The program of a robot that does nothing.
idleProgram :: ProcessedTerm
@ -87,7 +88,7 @@ mkGameState robotMaker numRobots = do
(mapM addTRobot robots)
( (initAppState ^. gameState)
& creativeMode .~ True
& multiWorld .~ M.singleton DefaultRootSubworld (newWorld (WF $ const (fromEnum DirtT, Nothing)))
& multiWorld .~ M.singleton DefaultRootSubworld (newWorld (WF $ const (fromEnum DirtT, ENothing)))
)
-- | Runs numGameTicks ticks of the game.

View File

@ -179,7 +179,8 @@ robots:
- [1, "1"]
known: [water, wavy water, flower, tree]
world:
default: [stone]
dsl: |
{stone}
palette:
"Ω": [grass, null, base]
"┌": [stone, upper left corner]

View File

@ -65,7 +65,8 @@ entities:
- The place you're trying to reach! You win by executing `grab` on this item.
properties: [known, portable]
world:
default: [ice]
dsl: |
{ice}
palette:
'Ω': [stone, null, base]
' ': [stone, null]

View File

@ -65,7 +65,8 @@ entities:
- The place you're trying to reach! You win by executing `grab` on this item.
properties: [known, portable]
world:
default: [ice]
dsl: |
{ice}
palette:
'Ω': [stone, null, base]
' ': [stone, null]

View File

@ -65,7 +65,8 @@ entities:
- The place you're trying to reach! You win by executing `grab` on this item.
properties: [known, portable]
world:
default: [grass]
dsl: |
{grass}
palette:
'Ω': [grass, null, base]
'.': [grass, null]

View File

@ -58,7 +58,8 @@ entities:
- The place you're trying to reach! You win by executing `grab` on this item.
properties: [known, portable]
world:
default: [grass]
dsl: |
{grass}
palette:
'Ω': [grass, null, base]
'.': [grass, null]

View File

@ -140,7 +140,8 @@ entities:
properties: [known]
known: [flower, tree]
world:
default: [grass]
dsl: |
{grass}
upperleft: [0, 0]
offset: false
palette:

View File

@ -506,14 +506,15 @@ seed: 0
solution: |
run "scenarios/Challenges/Ranching/_gated-paddock/fence-construction.sw"
world:
default: [dirt, water]
dsl: |
{dirt, water}
palette:
'B': [grass, null, base]
'.': [grass]
'B': [grass, erase, base]
'.': [grass, erase]
't': [dirt, tree]
'x': [stone, mountain]
'c': [stone, cabin]
's': [grass, null, sheep]
's': [grass, erase, sheep]
'%': [grass, clover, null]
'H': [stone, pier, null]
'~': [dirt, water]

View File

@ -179,7 +179,8 @@ entities:
properties: [known, growable, portable]
known: [sand]
world:
default: [grass]
dsl: |
{grass}
upperleft: [-1, -1]
offset: false
palette:

View File

@ -721,7 +721,8 @@ recipes:
time: 0
known: []
world:
default: [grass]
dsl: |
{grass}
upperleft: [-3, 2]
offset: false
palette:
@ -739,4 +740,4 @@ world:
..x....x..........
..xxxxxx..........
..................
zy................
zy................

View File

@ -66,12 +66,13 @@ entities:
properties: [known, unwalkable, portable]
known: [mountain, water, flower]
world:
default: [grass, water]
dsl: |
{grass, water}
upperleft: [-1, 1]
offset: false
palette:
'B': [grass, null, base]
'.': [grass]
'B': [grass, erase, base]
'.': [grass, erase]
'@': [grass, monolith]
'A': [grass, mountain]
'*': [grass, flower]

View File

@ -49,12 +49,13 @@ entities:
properties: [known, unwalkable, portable]
known: [mountain, water, flower]
world:
default: [grass, water]
dsl: |
{grass, water}
upperleft: [-1, 1]
offset: false
palette:
'B': [grass, null, base]
'.': [grass]
'B': [grass, erase, base]
'.': [grass, erase]
'@': [grass, monolith]
'A': [grass, mountain]
'*': [grass, flower]

View File

@ -101,13 +101,14 @@ entities:
properties: [known, unwalkable, portable]
known: [mountain, water, flower]
world:
default: [grass, water]
dsl: |
{grass, water}
upperleft: [-1, 1]
offset: false
palette:
'B': [ice, null, base]
'.': [grass]
'x': [dirt]
'B': [ice, erase, base]
'.': [grass, erase]
'x': [dirt, erase]
'@': [grass, monolith]
'A': [grass, mountain]
'*': [grass, flower]

View File

@ -95,12 +95,13 @@ entities:
properties: [known, unwalkable]
known: [mountain, water, 3D printer, flower]
world:
default: [grass, water]
dsl: |
{grass, water}
upperleft: [-21, 10]
offset: false
palette:
'B': [ice, null, base]
'.': [grass]
'B': [ice, erase, base]
'.': [grass, erase]
'*': [grass, flower]
'b': [grass, boat]
'3': [grass, 3D printer]
@ -108,8 +109,8 @@ world:
'c': [grass, crate]
'A': [grass, wall]
'w': [dirt, water]
'x': [stone]
'z': [dirt]
'x': [stone, erase]
'z': [dirt, erase]
map: |
..................3...A.
.................AAAA.A*

View File

@ -378,7 +378,8 @@ recipes:
- [1, drill]
known: []
world:
default: [dirt]
dsl: |
{dirt}
upperleft: [0, 0]
offset: false
palette:
@ -413,4 +414,4 @@ world:
...X****7****1...
........*........
B....p.p.p.p.p...
/.............../
/.............../

View File

@ -190,22 +190,23 @@ recipes:
known: [water]
seed: 0
world:
default: [stone, water]
dsl: |
{stone, water}
upperleft: [0, 0]
offset: false
palette:
'0': [stone, water]
'@': [stone, granite boulder]
'.': [grass]
'L': [stone]
'.': [grass, erase]
'L': [stone, erase]
'>': [stone, bind gt]
'=': [stone, bind eq]
H: [dirt]
H: [dirt, erase]
A: [grass, water, ccw_robot]
a: [grass, water, ccw_robot_down]
B: [grass, null, cw_robot]
b: [grass, null, cw_robot_down]
Ω: [grass, null, base]
B: [grass, erase, cw_robot]
b: [grass, erase, cw_robot_down]
Ω: [grass, erase, base]
f: [stone, Amulet of Yoneda]
x: [stone, locked door]
k: [grass, door key]
@ -225,4 +226,4 @@ world:
..@@@@@@@@@.@@@@@@@.@@@@@@@.@..
..Ω.........@.....@.........@..
@@@@@@@@@@@@@.>>=.@@@@@@@@@@@..
...............................
...............................

View File

@ -615,7 +615,6 @@ recipes:
known: [water, sand, flower, iron mine]
seed: 0
world:
default: [blank]
palette:
'.': [blank]
'/': [blank, left roof]

View File

@ -179,7 +179,8 @@ recipes:
known: [boulder, lignite mine]
seed: 0
world:
default: [grass]
dsl: |
{grass}
palette:
'B': [dirt, null, base]
'.': [dirt]

View File

@ -34,10 +34,11 @@ robots:
char: '♚'
known: [water]
world:
default: [ice, water]
dsl: |
{ice, water}
palette:
'.': [grass]
'#': [ice]
'.': [grass, erase]
'#': [ice, erase]
'┌': [stone, upper left corner]
'┐': [stone, upper right corner]
'└': [stone, lower left corner]

View File

@ -264,7 +264,6 @@ solution:
known: []
seed: 0
world:
default: [blank]
palette:
'B': [blank]
'Ω': [blank, null, base]
@ -307,4 +306,4 @@ world:
x.....x....x....x.....x
x.xxxxxxxx.x.xxxxxxxx.x
x.....................x
xxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxx

View File

@ -155,7 +155,8 @@ known:
- blocked two
- blocked three
world:
default: [grass, null]
dsl: |
{grass}
palette:
',': [grass]
'_': [stone]

View File

@ -206,7 +206,8 @@ recipes:
- [1, scoop]
known: []
world:
default: [grass]
dsl: |
{grass}
upperleft: [0, 0]
offset: false
palette:

View File

@ -143,7 +143,6 @@ recipes:
time: 0
known: []
world:
default: [blank]
upperleft: [-1, 1]
offset: false
palette:
@ -160,4 +159,4 @@ world:
.xxxxx.
.xxxxx.
z......

View File

@ -100,7 +100,8 @@ entities:
properties: [known, unwalkable]
known: [bitcoin]
world:
default: [grass]
dsl: |
{grass}
upperleft: [0, 0]
offset: false
palette:

View File

@ -56,15 +56,16 @@ robots:
);
known: [water, wavy water, flower, tree]
world:
default: [ice, water]
dsl: |
{ice, water}
palette:
',': [ice, water]
' ': [ice, water]
'~': [ice, wavy water]
'*': [grass, flower]
'T': [grass, tree]
'.': [grass]
'_': [stone]
'.': [grass, erase]
'_': [stone, erase]
'┌': [stone, upper left corner]
'┐': [stone, upper right corner]
'└': [stone, lower left corner]

View File

@ -98,7 +98,6 @@ entities:
known: [water, boulder]
seed: 0
world:
default: [blank]
palette:
'A': [stone, boulder]
'B': [stone, boulder, base]
@ -122,4 +121,4 @@ world:
AAA~~~~~~~~~~~~~~~AAA
AAAAA~~~~~~~~~~~AAAAA
AAAAAAAA~~~~~AAAAAAAA
AAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAA

View File

@ -349,7 +349,8 @@ recipes:
- [1, highlighter]
known: [boulder]
world:
default: [dirt]
dsl: |
{dirt}
upperleft: [0, 0]
offset: false
palette:

View File

@ -58,7 +58,8 @@ robots:
waitUntil (t <- time; return (mod t 0x20 == 0))
)
world:
default: [ice]
dsl: |
{ice}
palette:
'o': [ice, rock, cell]
'.': [ice, null, cell]

View File

@ -56,7 +56,6 @@ robots:
known: [boulder, tree, water, wavy water]
world:
default: [blank]
upperleft: [0, 0]
offset: false
palette:

View File

@ -55,14 +55,14 @@ solution: |
known: [water, boulder, flower]
seed: 0
world:
default: [stone, water]
dsl: |
{stone, water}
upperleft: [0, 0]
offset: false
palette:
'@': [stone, boulder]
'.': [grass]
G: [stone, null, gate]
Ω: [grass, null, base]
'.': [grass, erase]
G: [stone, erase, gate]
Ω: [grass, erase, base]
f: [grass, flower]
map: |
.....
@ -78,4 +78,3 @@ world:
.....
..Ω..
.....

View File

@ -26,7 +26,7 @@ request](https://github.com/swarm-game/swarm/blob/main/CONTRIBUTING.md)!
The "blessed" scenarios that come with Swarm are stored in
`data/scenarios` and can be accessed via the "New game" menu.
However, other scenarios can be loaded directly from a file: simply
run swarm with the `--scenario` flag (`-c` for short) and point it to
run swarm with the `--scenario` flag (`-i` for short) and point it to
a specific `.yaml` file containing a scenario. For example:
```
@ -212,7 +212,6 @@ and `drill`.
| `required` | `[]` | `(int × string) list` | A list of catalysts required by the recipe. They are neither consumed nor produced, but must be present in order for the recipe to be carried out. It is a list of [count, entity name] tuples just like `in` and `out`. |
| `time` | 1 | `int` | The number of ticks the recipe takes to perform. For recipes which take more than 1 tick, the robot will `wait` for a number of ticks until the recipe is complete. For example, this is used for many drilling recipes. |
| `weight` | 1 | `int` | Whenever there are multiple recipes that match the relevant criteria, one of them will be chosen at random, with probability proportional to their weights. For example, suppose there are two recipes that both output a `widget`, one with weight `1` and the other with weight `9`. When a robot executes `make "widget"`, the first recipe will be chosen 10% of the time, and the second recipe 90% of the time. |
| | | | |
### World
@ -220,14 +219,14 @@ The top-level `world` field contains a key-value mapping describing the
world, that is, a description of the terrain and entities that exist
at various locations.
| Key | Default? | Type | Description |
|--------------|----------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `default` | `null` | `string list` | A tuple representing the contents of a default cell (see [Cells](#cells), except that the default cell may not contain a robot). If this key is present, it means that the whole world besides the part specified with the `map` will be filled with this default cell. If omitted, the world besides the part specified with the `map` will be procedurally generated. |
| `offset` | `False` | `boolean` | Whether the `base` robot's position should be moved to the nearest "good" location, currently defined as a location near a tree, in a 16x16 patch which contains at least one each of `tree`, `copper ore`, `bit (0)`, `bit (1)`, `rock`, `lambda`, `water`, and `sand`. The `classic` scenario uses `offset: True` to make sure that the it is not unreasonably difficult to obtain necessary resources in the early game. See https://github.com/swarm-game/swarm/blob/main/src/Swarm/Game/WorldGen.hs#L204 . |
| `scrollable` | `True` | `boolean` | Whether players are allowed to scroll the world map. |
| `palette` | `{}` | `object` | The `palette` maps single character keys to tuples representing contents of cells in the world, so that a world containing entities and robots can be drawn graphically. See [Cells](#cells) for the contents of the tuples representing a cell. |
| `map` | `""` | `string` | A rectangular string, using characters from the `palette`, exactly specifying the contents of a rectangular portion of the world. Leading spaces are ignored. The rest of the world is either filled by the `default` cell, or by procedural generation otherwise. Note that this is optional; if omitted, the world will simply be filled with the `default` cell or procedurally generated. |
| `upperleft` | `[0,0]` | `int × int` | A 2-tuple of `int` values specifying the (x,y) coordinates of the upper left corner of the `map`. |
| Key | Default? | Type | Description |
|--------------|----------|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `dsl` | `null` | `string` | An expression of the [Swarm world description DSL](../worlds/README.md). If specified, this will be used as the base layer for the world. |
| `offset` | `False` | `boolean` | Whether the `base` robot's position should be moved to the nearest "good" location, currently defined as a location near a tree, in a 16x16 patch which contains at least one each of `tree`, `copper ore`, `bit (0)`, `bit (1)`, `rock`, `lambda`, `water`, and `sand`. The `classic` scenario uses `offset: True` to make sure that the it is not unreasonably difficult to obtain necessary resources in the early game. See https://github.com/swarm-game/swarm/blob/main/src/Swarm/Game/WorldGen.hs#L204 . |
| `scrollable` | `True` | `boolean` | Whether players are allowed to scroll the world map. |
| `palette` | `{}` | `object` | The `palette` maps single character keys to tuples representing contents of cells in the world, so that a world containing entities and robots can be drawn graphically. See [Cells](#cells) for the contents of the tuples representing a cell. |
| `map` | `""` | `string` | A rectangular string, using characters from the `palette`, exactly specifying the contents of a rectangular portion of the world. Leading spaces are ignored. The rest of the world is either filled by the `default` cell, or by procedural generation otherwise. Note that this is optional; if omitted, the world will simply be filled with the `default` cell or procedurally generated. |
| `upperleft` | `[0,0]` | `int × int` | A 2-tuple of `int` values specifying the (x,y) coordinates of the upper left corner of the `map`. |
#### Cells

View File

@ -33,5 +33,7 @@ robots:
- [50, scanner]
- [5, toolkit]
world:
seed: null
offset: true
scrollable: false
dsl: |
"classic"

View File

@ -33,5 +33,7 @@ robots:
- [50, scanner]
- [5, toolkit]
world:
seed: null
offset: true
scrollable: false
dsl: |
"classic"

View File

@ -33,5 +33,7 @@ robots:
- [50, scanner]
- [5, toolkit]
world:
seed: null
offset: true
scrollable: false
dsl: |
"classic"

View File

@ -37,6 +37,7 @@
1256-halt-command.yaml
1295-density-command.yaml
1138-structures
1320-world-DSL
1356-portals
144-subworlds
1379-single-world-portal-reorientation.yaml

View File

@ -65,7 +65,6 @@ recipes:
- [1, gate key]
known: [flower]
world:
default: [blank]
palette:
'Ω': [grass, null, base]
'.': [grass]

View File

@ -37,7 +37,6 @@ robots:
- solar panel
- treads
world:
default: [blank]
palette:
'>': [grass, null, base]
'Å': [stone, copper mine]

View File

@ -179,7 +179,6 @@ entities:
properties: [known]
robots: []
world:
default: [blank]
palette:
'.': [blank]
'1': [blank, color1]
@ -207,4 +206,4 @@ world:
.1234567..Rzzy...IIy
.1234567y.R.z.......
.abcdefg......C..BBz
.abcdefgyy.yy..Cz.z.
.abcdefgyy.yy..Cz.z.

View File

@ -8,7 +8,6 @@ robots:
dir: [1, 0]
known: [flower, bit (0), bit (1)]
world:
default: [blank]
palette:
'.': [grass]
'*': [stone, flower]

View File

@ -8,7 +8,6 @@ robots:
dir: [1, 0]
known: [tree, flower, bit (0), bit (1)]
world:
default: [blank]
palette:
'.': [grass]
'*': [stone, flower]

View File

@ -8,7 +8,6 @@ robots:
dir: [1, 0]
known: [water, sand]
world:
default: [blank]
palette:
'.': [grass]
upperleft: [-1, 1]
@ -86,4 +85,4 @@ world:
............
............
............
............
............

View File

@ -35,7 +35,6 @@ robots:
- ADT calculator
known: []
world:
default: [blank]
palette:
'Ω': [grass, null, base]
'.': [grass]

View File

@ -22,7 +22,6 @@ robots:
- ADT calculator
known: []
world:
default: [blank]
palette:
'Ω': [grass, null, base]
'.': [grass]
@ -56,4 +55,4 @@ recipes:
- [1, gumball]
required:
- [1, drill]
time: 1
time: 1

View File

@ -33,7 +33,6 @@ robots:
- treads
known: []
world:
default: [blank]
palette:
'Ω': [grass, null, base]
'.': [grass]

View File

@ -37,11 +37,12 @@ robots:
char: J
known: []
world:
default: [blank, boulder]
dsl: |
{blank, boulder}
palette:
'Ω': [grass, null, base]
'J': [grass, null, judge]
'.': [grass]
'Ω': [grass, erase, base]
'J': [grass, erase, judge]
'.': [grass, erase]
upperleft: [4, -1]
map: |
J........

View File

@ -42,7 +42,6 @@ robots:
- treads
known: []
world:
default: [blank]
palette:
'Ω': [grass, null, base]
'.': [grass]

View File

@ -69,7 +69,6 @@ robots:
attr: robot
known: [tree, flower, boulder]
world:
default: [blank]
palette:
'Ω': [grass, null, base]
'b': [grass, null, bot]

View File

@ -78,7 +78,6 @@ entities:
capabilities: [movemultiple]
known: [tree, flower, boulder, water]
world:
default: [blank]
palette:
'Ω': [grass, null, base]
'.': [grass]

View File

@ -51,7 +51,6 @@ entities:
properties: [known, portable, unwalkable]
known: [tree, flower, boulder, water]
world:
default: [blank]
palette:
'Ω': [grass, null, base]
'j': [stone, null, judge]

View File

@ -41,7 +41,6 @@ robots:
def forever = \c. c ; forever c end;
forever ( turn right )
world:
default: [blank]
palette:
'Ω': [grass, null, base]
'^': [grass, null, infinitebot]

View File

@ -35,7 +35,8 @@ robots:
char: J
known: []
world:
default: [blank, boulder]
dsl: |
{blank, boulder}
palette:
'Ω': [grass, tree, base]
'J': [grass, tree, judge]

View File

@ -0,0 +1,3 @@
constant.yaml
erase.yaml
override.yaml

View File

@ -0,0 +1,23 @@
version: 1
name: Constant (uniform) world description
description: |
Test that we can describe a uniform world by giving a
single cell value.
objectives:
- condition: |
as base { n <- count "tree"; return (n >= 4) }
goal:
- Get 4 trees
solution: |
grab; move; grab; move; grab; move; grab
robots:
- name: base
loc: [0,0]
dir: [1,0]
devices:
- logger
- treads
- grabber
world:
dsl: |
{terrain: ice} <> {entity: tree}

View File

@ -0,0 +1,30 @@
version: 1
name: Overlay with erasure
description: |
Test that we can erase entities when overlaying
objectives:
- condition: |
as base { n <- count "tree"; return (n == 0) }
goal:
- Get rid of your trees.
solution: |
place "tree"; move; move;
place "tree"; move; move;
place "tree"; move; move;
place "tree"
robots:
- name: base
loc: [0,0]
dir: [1,0]
devices:
- logger
- treads
- grabber
inventory:
- [4, tree]
world:
dsl: |
overlay
[ {terrain: ice} <> {entity: tree}
, if (x + y) % 2 == 0 then {erase} else {blank}
]

View File

@ -0,0 +1,25 @@
version: 1
name: Overlay with overriding
description: |
Test that later entities override earlier ones when overlaying
objectives:
- condition: |
as base { n <- count "tree"; return (n == 1) }
goal:
- Get a tree.
solution: |
grab
robots:
- name: base
loc: [0,0]
dir: [1,0]
devices:
- logger
- treads
- grabber
world:
dsl: |
overlay
[ {terrain: ice} <> {entity: rock}
, {entity: tree}
]

View File

@ -18,7 +18,6 @@ robots:
known: [flower, boulder]
world:
upperleft: [-1, 1]
default: [blank]
palette:
'.': [grass]
'*': [stone, flower]

View File

@ -32,7 +32,6 @@ robots:
known: [tree, flower, sand, bit (0), bit (1)]
world:
upperleft: [-4, 7]
default: [blank]
palette:
'.': [grass]
'*': [stone, flower]

View File

@ -50,7 +50,6 @@ robots:
- treads
known: [flower, bit (0), bit (1), bitcoin]
world:
default: [blank]
palette:
'.': [grass]
'*': [stone, flower]

View File

@ -25,7 +25,6 @@ robots:
- logger
known: [water]
world:
default: [blank, null]
palette:
'.': [grass]
'~': [dirt, water]

View File

@ -26,7 +26,6 @@ robots:
inventory:
- [1, boat]
world:
default: [blank, null]
palette:
'.': [grass]
'~': [dirt, knownwater]

View File

@ -28,7 +28,6 @@ robots:
- [1, treads]
known: [water]
world:
default: [blank, null]
palette:
'.': [grass]
'~': [dirt, water]

View File

@ -30,7 +30,6 @@ robots:
- [1, grabber]
- [1, logger]
world:
default: [blank, null]
palette:
'.': [grass]
'┌': [stone, upper left corner]

View File

@ -28,7 +28,6 @@ robots:
- [1, grabber]
- [1, logger]
world:
default: [blank, null]
palette:
'.': [grass]
'┌': [stone, upper left corner]

View File

@ -46,7 +46,6 @@ robots:
- [50, rock]
known: [water]
world:
default: [blank, null]
palette:
'.': [grass]
'~': [dirt, water]

View File

@ -46,7 +46,6 @@ robots:
- [50, rock]
known: [water]
world:
default: [blank, null]
palette:
'.': [grass]
'~': [dirt, water]

View File

@ -40,10 +40,11 @@ robots:
- [5, grabber]
known: [water, wavy water]
world:
default: [ice, water]
dsl: |
{ice, water}
palette:
'Ω': [grass, null, base]
'.': [grass]
'Ω': [grass, erase, base]
'.': [grass, erase]
' ': [ice, water]
'~': [ice, wavy water]
'L': [grass, Linux]

View File

@ -46,7 +46,6 @@ robots:
- [10, solar panel]
- [0, harvester]
world:
default: [blank]
palette:
'Ω': [grass, null, base]
'.': [grass]

View File

@ -41,7 +41,6 @@ robots:
inventory:
- [1, detonator] # used to mark win
world:
default: [blank]
palette:
'.': [grass]
'M': [stone, mountain]

View File

@ -28,7 +28,6 @@ robots:
- [1, treads]
- [1, solar panel]
world:
default: [blank]
palette:
'Ω': [grass, null, base]
'.': [grass]

View File

@ -35,7 +35,6 @@ robots:
program: "move"
known: [water]
world:
default: [blank]
palette:
'.': [grass]
' ': [ice, water]

View File

@ -32,7 +32,6 @@ robots:
program: |
log "I shall sleep"; wait 1; log "I have awoken"
world:
default: [blank]
palette:
'.': [grass]
'┌': [stone, upper left corner]

View File

@ -77,7 +77,6 @@ entities:
- You win!
properties: [known, portable]
world:
default: [blank]
palette:
'.': [grass]
upperleft: [0,0]

View File

@ -59,7 +59,6 @@ entities:
properties: [known, portable, growable]
growth: [1,2]
world:
default: [blank]
palette:
'.': [grass]
upperleft: [0,0]

View File

@ -26,7 +26,6 @@ robots:
- grabber
- boat
world:
default: [blank]
palette:
'.': [grass]
'T': [stone, tree]

View File

@ -23,7 +23,6 @@ robots:
inventory:
- [1, tree]
world:
default: [blank]
palette:
'.': [grass]
'┌': [stone, upper left corner]

View File

@ -37,9 +37,10 @@ robots:
- [1, ADT calculator]
known: [water]
world:
default: [ice, water]
dsl: |
{ice, water}
palette:
'.': [grass]
'.': [grass, erase]
' ': [ice, water]
'┌': [stone, upper left corner]
'┐': [stone, upper right corner]

View File

@ -24,4 +24,5 @@ robots:
inventory:
- [1, rock]
world:
default: [grass, null]
dsl: |
{grass}

View File

@ -42,9 +42,10 @@ robots:
- [0, bit (1)]
known: [water, wavy water]
world:
default: [ice, water]
dsl: |
{ice, water}
palette:
'.': [grass]
'.': [grass, erase]
' ': [ice, water]
'~': [ice, wavy water]
'L': [grass, Linux]
@ -58,7 +59,7 @@ world:
'A': [stone, magnetic vein]
'o': [stone, lodestone]
'0': [grass, bit (0)]
'B': [grass, null, base]
'B': [grass, erase, base]
upperleft: [-1, 1]
map: |
┌─────┐ ~~

View File

@ -45,7 +45,6 @@ robots:
)
)
world:
default: [blank]
palette:
'┌': [stone, upper left corner]
'┐': [stone, upper right corner]

View File

@ -64,7 +64,6 @@ robots:
doN 7 (move; wait 4; place "tree";);
known: []
world:
default: [blank]
palette:
'Ω': [grass, null, base]
p : [grass, null, planter]

View File

@ -29,7 +29,6 @@ robots:
program: |
try {move} {say "Fatal error: two was unable to move into a boulder even though it is system robot!"}
world:
default: [blank]
palette:
'@': [stone, boulder]
'B': [grass, null, base]

View File

@ -35,7 +35,6 @@ robots:
program: |
try {move} {say "Fatal error: three was unable to move into water even though it is system robot!"}
world:
default: [blank]
palette:
'~': [stone, water]
'B': [grass, null, base]

View File

@ -33,7 +33,6 @@ robots:
dir: [0,-1]
system: true
world:
default: [blank]
palette:
'@': [stone, boulder]
'B': [grass, null, base]

View File

@ -23,7 +23,6 @@ objectives:
solution: |
move;move;move; move;move;move; move;move;move;
world:
default: [blank]
palette:
'.': [blank]
# FIRST ROOM

View File

@ -42,7 +42,6 @@ robots:
- [2, board]
- [5, rock]
world:
default: [blank]
palette:
'x': [grass, null, base]
upperleft: [0, 0]

View File

@ -34,7 +34,6 @@ robots:
- [2, board]
- [5, rock]
world:
default: [blank]
palette:
'x': [grass, null, base]
upperleft: [0, 0]

View File

@ -53,7 +53,6 @@ robots:
- [2, board]
- [5, rock]
world:
default: [blank]
palette:
'x': [grass, flower, base]
upperleft: [0, 0]

View File

@ -41,7 +41,6 @@ robots:
- [2, board]
- [5, rock]
world:
default: [blank]
palette:
'x': [grass, flower, base]
upperleft: [0, 0]

View File

@ -26,11 +26,10 @@ solution: |
grab;
known: [tree]
world:
default: [blank]
palette:
'B': [grass, null, base]
'w': [grass, tree]
upperleft: [0, 0]
map: |-
w
B
B

View File

@ -25,7 +25,6 @@ solution: |
place "tree";
known: [tree]
world:
default: [blank]
palette:
'B': [grass, null, base]
'w': [grass]

View File

@ -23,11 +23,10 @@ solution: |
grab;
known: [tree]
world:
default: [blank]
palette:
'B': [grass, null, base]
'w': [grass, tree]
upperleft: [0, 0]
map: |-
w
B
B

View File

@ -32,7 +32,6 @@ robots:
- name: other
dir: [1,0]
world:
default: [blank]
palette:
'.': [grass]
'Ω': [grass, null]

View File

@ -17,7 +17,6 @@ robots:
- treads
- compass
world:
default: [blank]
palette:
'^': [grass, null, base]
'.': [grass]

View File

@ -47,7 +47,6 @@ robots:
y <- random 11;
teleport base (x-5, y-5)
world:
default: [blank]
palette:
'.': [grass]
upperleft: [-5, 5]

View File

@ -39,7 +39,6 @@ entities:
- A thing that everyone needs!
properties: [portable]
world:
default: [blank]
palette:
'.': [grass]
'>': [grass, tree, base]

View File

@ -32,7 +32,6 @@ robots:
- [1, wheels]
- [1, solar panel]
world:
default: [blank]
palette:
'Ω': [grass, null, base]
'.': [grass]

View File

@ -15,7 +15,6 @@ entities:
description:
- Your scooter
world:
default: [blank]
palette:
'x': [grass, null, base]
upperleft: [0, 0]

View File

@ -30,7 +30,6 @@ robots:
known: [tree]
world:
upperleft: [-1, 1]
default: [blank]
palette:
'.': [grass]
'B': [grass, null, base]

View File

@ -30,7 +30,6 @@ robots:
known: [tree]
world:
upperleft: [1, -1]
default: [blank]
palette:
'.': [grass]
'B': [grass, null, base]

View File

@ -20,7 +20,6 @@ robots:
known: [tree]
world:
upperleft: [1, -1]
default: [blank]
palette:
'.': [grass]
'B': [grass, null, base]

Some files were not shown because too many files have changed in this diff Show More