`time` now requires `CTimeabs`, and `wait` now requires `CTimerel`.
Introduced a new `hourglass` device to provide `CTimerel` capability.
The `clock` device remains backward compatible, providing both relative and absolute time capabilities.
Uses type variable names like `a`, `b`, `c`, ... instead of using `aNNN` with numbers based on the internal numbers used for fresh type variables. Closes#1050.
When supplying initial code with `--run` or `--autoplay`, a lot can happen in the first few moments of play such that it can be a tricky feat to smash CTRL+z fast enough to slow down the game to observe.
Now, one can pass e.g. `--speed 0` to begin the game at 1 tick per second:
scripts/play.sh --scenario data/scenarios/Challenges/maypole.yaml --autoplay --speed 0
When the inventory panel is focused, hit `/` to enter search mode (maybe "search" is a bit of a misnomer, it's really more of a "filter" mode). Then type stuff to modify the current search term, shown at the bottom of the inventory panel. You can still navigate the filtered inventory list with arrow keys etc. Hit Esc to exit search mode or Enter to pop out the focused item (and also exit search mode).
Closes#126.
"Stub" maps generated by the World Editor (#873) do not define any robots.
It is convenient to view these via:
scripts/play.sh --scenario stub.yaml --cheat
However, the static needs to be suppressed in creative mode when robots do not exist.
Ensures that the "played" status and the "best scores" get updated in the scenario menu after "Quit to menu" is selected from within a scenario.
This bug appeared after merging #974.
Closes#1145.
Instead of using simple black or white cells to display static, use cells with any one of the 16 possible arrangements of on/off quarter-blocks. This makes static appear at almost 2x resolution as before (though there is still a bit of blockiness since the quarter-blocks can't occur independently). See https://asciinema.org/a/582095 for a demonstration.
towards #866
NOTE: #1116 should be merged first so that the schema change of save files is less disruptive.
## Examples
Different criteria can have their own best score:
![image](https://user-images.githubusercontent.com/261693/219904496-fcd23ca0-b208-43e1-afc6-188acfe327cf.png)
All criteria share the same single best score:
![image](https://user-images.githubusercontent.com/261693/219904553-abe3011c-41b0-469c-b34d-95d84b91697a.png)
## Behavior notes
* As currently designed, the code size will only be scored if the the player has specified their code **before** the scenario begins. Furthermore, any input into the REPL will invalidate code size scoring for the current game.
* Because of this, the only way to score code so far is with a command-line argument of `--run` or `--autoplay`. However, #1010 shall implement code size scoring when a scenario is launched from the UI.
* In the "best scores" display, if multiple "best score" criteria were all from the same game, they will be consolidated. If all criteria are for the same game, the criteria labels will be omitted.
* The code size metrics will not be displayed if a "best score" was not obtained via `--run`.
## Caveats
### `run` command
Currently, the code entailed in a `run "somescript.sw"` command is not transitively included, so using `run` make the code size score meaningless.
## Testing
### Unit tests
Run the subset of unit tests:
scripts/run-tests.sh --test-arguments '--pattern "Tests.Precedence"'
### Manual integration tests
First, reset the score:
rm -f ~/.local/share/swarm/saves/Tutorials_grab.yaml
Saving the following to `grab-soln.sw`:
```
move;
move; grab; turn back; move; turn back; move;
move; grab; turn back; move; turn back; move;
move; grab; turn back; move; turn back; move;
move; grab; turn back; move; turn back; move;
move; grab; turn back; move; turn back; move;
move; grab;
```
Run as follows:
scripts/play.sh --scenario Tutorials/grab.yaml --run grab-soln.sw
This should establish a record for code size.
Then, play the Grab tutorial and immediately paste and run this in the REPL:
move; move; grab; move; grab; move; grab; move; grab; move; grab; move; grab;
This solution is faster in terms of time, but should not displace the code-length record, since no code length should be recorded from a REPL solution.
Closes#1234
A `"dozer blade"` is defined locally to provide `push` capability. To decouple this PR from bikeshedding, will defer the definition of a global entity that offers `push` capability to another PR.
Closes#625.
# Demo
## Running code
Start a creative game:
scripts/play.sh --scenario data/scenarios/creative.yaml
In another terminal window:
curl -H "Content-Type: text/plain; charset=utf-8" -i http://localhost:5357/code/run --data "move;"
Observe that the robot has moved, and that the command sent via `curl` has been added to the REPL history.
## Parsing and rendering code
Start swarm:
scripts/play.sh
Run curl:
curl --silent -H "Content-Type: text/plain; charset=utf-8" -i http://localhost:5357/code/render --data @data/test/language-snippets/warnings/unused-vars/multiple-lambda-first-unused.sw
See output:
```
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Date: Mon, 06 Mar 2023 10:16:12 GMT
Server: Warp/3.3.23
Content-Type: text/plain;charset=utf-8
def put = \x. \y. place y end
|
`- \x. \y. place y
|
`- \y. place y
|
`- place y
|
+- place
|
`- y
```
Closes#1218
scripts/play.sh --scenario data/scenarios/Testing/1218-stride-command.yaml --autoplay
I decided to implement this command with the same exception behaviors as the `move` command.
If any obstructions exist on the path, then an exception is thrown and no movement is made.
The implications of this are:
1. With `try`, one can learn in `O(1)` time whether any obstructions exist along a line of up to 64 units long
2. One can learn in `O(log N)` time the exact distance of the nearest obstruction along that line.
Ability to code your own input handler routines. Closes#102 . Fixes#1210 .
* Adds a new type `key` to represent keypresses
* Adds a primitive function `key : text -> key` which can handle usual letters, numbers, etc. as well as special keys like `"Down"` etc, as well as modifier key prefixes like `key "A-C-Del"`. `swarm generate keys` generates a list of all recognized special key names.
* New command `installKeyHandler : text -> (key -> cmd unit) -> cmd unit` which sets the "current key handler". The `text` value is a hint line to display in the secondary key hints menu while the handler is running. The global shortcut `M-k` toggles the currently installed handler.
* Add a `keyboard` device to provide these commands, as well as a `key` entity (the recipe for a `keyboard` is 16 `key`s + 1 `board`).
* Add a few examples in the `examples` folder.
* Add an installed `keyboard` to the `building-bridges` challenge.
Executing `scan` or `upload` may change the way the world is drawn, so we need to flag a redraw. Fixes#758.
I also included a few updated/improved comments I made while trying to understand various bits of code (though they were ultimately unrelated to this issue).
Closes#687
## Demo
scripts/play.sh --scenario data/scenarios/Testing/687-watch-command.yaml --autoplay
or use `run` instead of `--autoplay`, and monitor the state:
curl -s http://localhost:5357/robot/0 | jq ._watchedLocations
Towards #1171
The `resonate` command counts entities of a given type within a rectangle.
This PR also fixes a bug in `detect` when the rectangle coords are non-ascending.
May be able to reduce likelihood of #1086 by having a autogenerated table of the commands and the tutorials they are introduced in.
This work is related to #1106 in that robot commands should be given distinct markup (backticks).
## Demo
scripts/play.sh generate pedagogy
**Updated:** See [rendered output](https://gist.github.com/kostmo/d4af3a6814e65fc6d455fd34b41552d3).
## New tests
scripts/run-tests.sh --test-arguments '--pattern "Pedagogical soundness"'
Note that the test for `crash.yaml` is currently failing because the `Give` command (and some others) are utilized in this tutorial without having been mentioned in the description or in earlier tutorials.
Additionally, the "bug" described in [this comment](https://github.com/swarm-game/swarm/issues/1089#issuecomment-1418497686) **should** be caught by these tests (i.e. fail the tests) since the embedded `solution` code in the YAML file utilizes the `give` command. That is, if it currently weren't for [this issue](https://github.com/swarm-game/swarm/pull/1186/files#r1152789153).
Add a special built-in command `requirements` which can be applied to any expression. Typing `requirements e` at the REPL will cause a log message to be generated listing the requirements for the given expression. For example, `requirements (move; grab)` generates
```
Requirements for `(move; grab)`:
Equipment:
- fast grabber OR grabber OR harvester
- solar panel
- tank treads OR treads
```
Closes#922 .
The purpose of this refactoring is to decouple the key mapping from imperative list movement instructions. In the new API (a function named `navigateList`), one supplies a declarative mapping of key events to a sum type that represents the desired movement.
As a client of the API, one's code reduces to this:
```
navigateList isSep e $ \case
V.EvKey V.KUp [] -> Single Up
V.EvKey V.KDown [] -> Single Down
V.EvKey V.KHome [] -> Multi Up Max
V.EvKey V.KEnd [] -> Multi Down Max
V.EvKey V.KPageUp [] -> Multi Up Page
V.EvKey V.KPageDown [] -> Multi Down Page
_ -> None
```
towards #1171
## New tests
Unit tests:
scripts/run-tests.sh --test-arguments '--pattern "Relative direction"'
Integration tests for `sniff` and `chirp` demonstrate how to home in on an item using distance and orientation, respectively.
## Efficiency
For the sake of execution time, I have capped the max "diameter" (`N`) of both commands to `200` cells. In the worst case (the entity is not present), `O(N^2)` cells are inspected, which manifests as a perceptible delay when the command is run. I came across the `getElemsInArea` function that seems to suggest that an `O(N * log N)` search may be possible. Is that the case?
Otherwise we may be able to add some new data structures to the game state for efficient entity location querying.
Let's not worry about using reserved words as record fields for now. It would technically work OK but I'm not sure how important it is.
See #1177 for the fix to the root issue that let this through the merge process.
Add record types to the language: record values are written like `[x = 3, y = "hi"]` and have types like `[x : int, y : text]`. Empty and singleton records are allowed. You can project a field out of a record using standard dot notation, like `r.x`. If things named e.g. `x` and `y` are in scope, you can also write e.g. `[x, y]` as a shorthand for `[x=x, y=y]`.
Closes#1093 .
#153 would make this even nicer to use.
One reason this is significant is that record projection is our first language construct whose type cannot be inferred, because if we see something like `r.x` all we know about the type of `r` is that it is a record type with at least one field `x`, but we don't know how many other fields it might have. Without some complex stuff like row polymorphism we can't deal with that, so we just punt and throw an error saying that we can't infer the type of a projection. To make this usable we have to do a better job checking types, a la #99 . For example `def f : [x:int] -> int = \r. r.x end` would not have type checked before, since when checking the lambda we immediately switched into inference mode, and then encountered the record projection and threw up our hands. Now we work harder to push the given function type down into the lambda so that we are still in checking mode when we get to `r.x` which makes it work. But it is probably easy to write examples of other things where this doesn't work. Eventually we will want to fully implement #99 ; in the meantime one can always add a type annotation (#1164) on the record to get around this problem.
Note, I was planning to add a `open e1 in e2` syntax, which would take a record expression `e1` and "open" it locally in `e2`, so all the fields would be in scope within `e2`. For example, if we had `r = [x = 3, y = 7]` then instead of writing `r.x + r.y` you could write `open r in x + y`. This would be especially useful for imports, as in `open import foo.sw in ...`. However, it turns out to be problematic: the only way to figure out the free variables in `open e1 in e2` is if you know the *type* of `e1`, so you know which names it binds in `e2`. (In all other cases, bound names can be determined statically from the *syntax*.) However, in our current codebase there is one place where we get the free variables of an untyped term: we decide at parse time whether definitions are recursive (and fill in a boolean to that effect) by checking whether the name of the thing being defined occurs free in its body. One idea might be to either fill in this boolean later, after typechecking, or simply compute it on the fly when it is needed; currently this is slightly problematic because we need the info about whether a definition is recursive when doing capability checking, which is currently independent of typechecking.
I was also planning to add `export` keyword which creates a record with all names currently in scope --- this could be useful for creating modules. However, I realized that very often you don't really want *all* in-scope names, so it's not that useful to have `export`. Instead I added record punning so if you have several variables `x`, `y`, `z` in scope that you want to package into a record, you can just write `[x, y, z]` instead of `[x=x, y=y, z=z]`. Though it could still be rather annoying if you wanted to make a module with tons of useful functions and had to list them all in a record at the end...
Originally I started adding records because I thought it would be a helpful way to organize modules and imports. However, that would require having records that contain fields with polymorphic types. I am not yet sure how that would play out. It would essentially allow encoding arbitrary higher-rank types, so it sounds kind of scary. In any case, I'm still glad I implemented records and I learned a lot, even if they can't be used for my original motivation.
I can't think of a way to make a scenario that requires the use of records. Eventually once we have proper #94 we could make a scenario where you have to communicate with another robot and send it a value of some required type. That would be a cool way to test the use of other language features like lambdas, too.