Recently the test suite started failing for me with an error about not finding an `00-ORDER.txt` file in `doc-fragments`. I'm not sure why it never failed before, but the reason for the failure is clear enough: any folder under `data/scenarios` which does not start with an underscore is assumed to contain scenarios.
I noticed this when regenerating the recipe graph on the wiki and saw that `tea leaves` was sitting there with no input. `tea leaves` are obtained by harvesting `tea plant`s (which now occur naturally in the world) but we were not taking such "yield" pairs into account when creating the graph. This PR adds a directed edge for each entity that `yields` another.
The image is already updated on the wiki with the output from this PR:
https://github.com/swarm-game/swarm/wiki/Recipes-cheat-sheet
Towards #1797
Hosts an online repository of scenarios, against which solutions may be submitted. This is the foundational layer that may support more structured "tournaments", scenario ranking, or other social activity.
# Demo
## Live server
http://swarmgame.net/list-games.html
One can use the [`submit.sh`](https://github.com/swarm-game/swarm/pull/1798/files#diff-450877e3442a0ec1c5cbe964808a263d67f1e680d3aa3c3bf9ae6f51eca682fb) script and see valid uploads reflected live on the website.
## Local testing
### Automated tests
These are database-agnostic.
scripts/run-tests.sh swarm:test:tournament-host
### Manual tests
These test database interactions. It requires first setting up a local Postgres server.
1. Start `tournament/scripts/demo/server-native.sh` in one console
2. Run `tournament/scripts/demo/client/test-cases/local/good-submit.sh` in another
# Features
* Upload and validates scenarios
* Download scenarios with solution redacted
* Submit, validate, execute, and score solutions
# Key components
* Servant server
* Hosted on AWS in a Docker container
* Stores to a Postgres database in Amazon RDS
* Shares some code with the integration tests for evaluating scenarios and solutions
The production database uses IAM to manage logins. The web app uses the AWS API to fetch a "token" which can be used to log in instead of a password. This avoids having to store a password on the server.
# TODO
- [ ] User authentication (GitHub OpenID?)
Closes#1684Closes#1262
# Demo
A simple "puzzle" that makes use of consumables:
scripts/play.sh -i data/scenarios/Testing/1777-capability-cost.yaml --autoplay
Demo of enabled commands and costs display in left pane:
scripts/play.sh -i data/scenarios/Testing/1262-display-device-commands.yaml
![Screenshot from 2024-03-01 22-39-12](https://github.com/swarm-game/swarm/assets/261693/03fc0e4f-d219-4aa1-8775-cb5112eb0b90)
# In this PR
* Supports specifying capabilities both as plain lists and as maps from capabilities to ingredients in YAML
* JSON Schema support for both capability specification styles
* New integration test
* Removed redundant `tshow` implementation from `Swarm.Doc.Util`
# Entity lookup approaches
The cost of exercising a capability in terms of "ingredients" is specified by the `_entityCapabilities` field within an `Entity` definition. Each ingredient itself is an entity, specified by name.
For many purposes, having ingredients just of type `EntityName` is sufficient. But for `Swarm.Game.Recipe.findLacking` in particular, the ingredients list must be actual `Entity` objects, not just names of entities. So at some point the names need to be looked up in the global entity map to be promoted to `Entity` objects.
The full list of entities is not available at `Entity` parse time to look up an ingredient entity by name, so we cannot store ingredients lists of type `(Count, Entity)` within a parent `Entity` object.
Approaches considered were:
* Store a copy of the `entityMap` in `RobotR`, for use by the `equippedDevices` lens
* Introduce a type parameter to `Entity` representing a "parse phase"
* **Allow a redundant "entity lookup" (and validation) at command execution time**
## Store `entityMap` in `RobotR`
One approach explored was to add a field to `RobotR`:
```
, _globalEntityMap :: EntityMap
```
This allowed the `equippedDevices` lens implementation to promote the `EntityName`s to `Entity`s when setting the value of the `_robotCapabilities` field. However, it was rather invasive as it entailed threading the `EntityMap` through many new code paths.
## `Entity` type parameter
Currently, `Entity` has a field:
```
_entityCapabilities :: SingleEntityCapabilities EntityName
```
This would entail a huge refactoring, with:
```
data Entity e = Entity
...
, _entityCapabilities :: SingleEntityCapabilities e
```
At initial parse time we would obtain a list of `Entity EntityName`, but somewhere later during `Scenario` parse time, we can do another pass to obtain `Entity Entity` objects.
This would at least have the advantage of doing the entity lookup/validation on ingredient lists in exactly one place, at parse time.
## Defer `EntityName -> Entity` promotion to command execution time
This is what is implemented in this PR. The global set of capability costs is validated at scenario parse time. But it is also redundantly validated in the `payExerciseCost` function, which is not ideal.
Towards #1797
Combines `TerrainEntityMaps` and `WorldMap` into a single record, `ScenarioInputs`.
Establishes a Matroska-style arrangement of records to represent various scopes of state, eliminating several redundant fields.
Closes#1641
The `data/terrain.yaml` file is now the authoritative source of terrains, though `BlankT` is still a hard-coded special case.
I have not changed the underlying integer representation of terrain in the world function, which means that the `terrainIndexByName` Map in the `TerrainMap` record is needed for translating between `Int` and `TerrainType`.
# Demo
scripts/play.sh -i data/scenarios/Testing/1775-custom-terrain.yaml
![Screenshot from 2024-02-22 16-51-53](https://github.com/swarm-game/swarm/assets/261693/1d263c8b-4e9c-40bf-bdc8-bf5ba8e33c4d)
# Changes
* There used to be a function called `integrateScenarioEntities` that combined the `EntityMap` stored in the `Scenario` record with the global entity map. However, the global entity map is accessible at parse time of the `Scenario`, so we do the combining there and only ever store the combined map in the `Scenario` record.
* JSON Schema for terrain
* Removed the distinction between "World" attributes and "Terrain" attributes
* Unit tests for scenario-defined terrain and related validations
* Validate existence of referenced terrain at scenario parse time
* Validate attributes referenced by terrains at parse time
Opens a live-reloading preview of the world in VS Code.
The renderer has been modified to optionally render a blank image instead of crashing upon invalid YAML.
## Prerequisites:
Install inotify tools:
sudo apt install inotify-tools
## Usage:
scripts/preview-world-vscode.sh data/scenarios/Fun/horton.yaml
Once the VS Code editor tabs are opened, one can press <kbd>CTRL</kbd> + <kbd>\\</kbd> (backslash) with the image selected to split the editor pane horizontally.
One may then navigate to the left-pane's copy of the image preview with <kbd>CTRL</kbd> + <kbd>PageUp</kbd>, and then <kbd>CTRL</kbd> + <kbd>w</kbd> will close the redundant image preview.
## Screenshot
![Screenshot from 2024-01-29 18-53-55](https://github.com/swarm-game/swarm/assets/261693/63a4728c-0ccb-4c08-8cde-61d65e8322b4)
Closes#1715
Extract scene renderer to its own binary with `swarm-scenario` as its only dependency.
This was mainly enabled by moving the `Landscape` sub-record out of the `swarm-engine` sublibrary and into `swarm-scenario`, and refactoring rendering functions to only depend on `Landscape` rather than the entire `GameState`.
# Demo
stack build --fast swarm:swarm-scene && stack exec swarm-scene -- data/scenarios/Fun/horton.yaml --png -w 180 -h 150
![output](https://github.com/swarm-game/swarm/assets/261693/e2df3aad-3b65-46bf-8485-352facdffc76)
Towards #1715 and #1043.
This refactoring step is a prerequisite for #1719 to extricate references to the `CESK` module from the base `RobotR` definition.
In this PR:
* `Swarm.Game.CESK` is imported qualified to more easily track usages
* A new `RobotMachine` type family is added to parameterize the `_machine` field.
* `CESK` is a new parameter to `addTRobot`
Closes#1623.
Note that we still have to list both `vty-unix` and `vty-windows` in `stack.yaml` but that's OK; it just means they are both made available as extra dependencies, but only whichever is needed will actually be installed. In the codebase itself, we now get to avoid CPP on imports, and the code is quite a bit simpler.
Closes#1443.
Also added `-Wunused-packages` to clean up dependencies.
## Demo
This still works as usual:
stack run
Output editor keywords:
stack run swarm-docs -- editors --emacs
Towards #1415.
## Uses
This capability could be used to quickly iterate on DSL world descriptions, e.g. when tuning noise parameters.
## Implementation notes
* For the hard-coded ANSI terminal color names, I chose RGB triples that matched my own terminal settings. This means that a rendered PNG might not exactly match one's own terminal colors.
* `Blank` terrain corresponds to a transparent pixel.
* Implemented parse-time validation of `attr` references. Previously, referencing a nonexistent `attr` by an entity would fail silently at runtime.
* Normalization: strings like `"rock"` now only exist once; the string is shared via toplevel variable definitions
* Entities and terrain have TUI-independent color definitions from which VTY Attrs are derived, but all TUI user-interface colors are defined only as VTY Attrs.
## Demos
Each pixel in the output image correponds to one world cell. To enlarge, can use [imagemagick](https://legacy.imagemagick.org/Usage/resize/#scale):
stack run -- map data/scenarios/classic.yaml --seed 0 --png -w 300 -h 200 && convert output.png -scale 800% out2.png
![out2](https://github.com/swarm-game/swarm/assets/261693/51794b63-7d78-4738-b20a-8b4f4352f006)
stack run -- map data/scenarios/Challenges/bridge-building.yaml --png && convert output.png -scale 800% out2.png
![image](https://github.com/swarm-game/swarm/assets/261693/b04895a2-eb61-4499-a122-ae8444f7e4fb)
Closes#1436.
The schema `.json` files are now the authoritative source of truth for documentation.
Wrote a very simple parser for JsonSchema to extract the documentation from JSON.
Split the README.md into [static](c314cc50a1/data/scenarios/README.md) and [auto-generated](c314cc50a1/data/scenarios/doc-fragments/SCHEMA.md) parts.
Added a custom `"footers"` key to schema files to support inclusion of other markdown files for each object description.
# Schema doc regeneration
./scripts/regenerate-schema-docs.sh
* generate Web API endpoint docs in CLI - `swarm generate endpoints`
* use default port in API docs using `renderCurlBasePath`
* fix code rendering in Web API so that the tree nodes try to fit one line
* try adding some API samples
* hand craft `ToJSON Robot` instance to match `FromJSONE` more closely
* default values are skipped
* inventory and devices are shortened to names and counts
Closes#353
Also adds a new top-level command to render a scenario map to the console.
Most of the work for this feature entailed identifying the subset of `GameState` that is actually needed for rendering the world, so that the required information can be retrieved from just the `Scenario` rather than having to instantiate an entire `GameState`.
# Potential follow-ups
- [ ] There is some noticeable lag when using the up/down arrow to navigate to any of the largely "procedurally generated" scenarios, e.g. `classic` or `creative`. May want to do caching of some kind. The other "challenge scenarios" render without perceptible delay.
- [ ] The heuristic for choosing the view center could be improved, possibly by defining new "hints" as part of the scenario schema.
- [ ] Rendering to the console could be augmented with color.
- [ ] Could render to other image formats like PNG or SVG
- [ ] account for a user-selected seed in the scenario launch parameters dialog
# Demos
## Scenario selection preview
![image](https://github.com/swarm-game/swarm/assets/261693/7c54c6bb-fb02-461f-98a1-06eccbbfc450)
## Command-line map rendering
```
~/github/swarm$ scripts/play.sh map data/scenarios/Challenges/ice-cream.yaml
OO
▒▒▒ ▒▒▒▒ OOOO
┌─┐▒┌──┐ MMMM
│B V6│ \ZZ/
└──────┘ \/
```
and
```
stack run -- map data/scenarios/Challenges/hackman.yaml
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒••••••••••▒••••••••••▒
▒o▒▒▒•▒▒▒▒•▒•▒▒▒▒•▒▒▒o▒
▒•▒▒▒•▒▒▒▒•▒•▒▒▒▒•▒▒▒•▒
▒•••••••••••••••••••••▒
▒•▒▒▒•▒•▒▒▒▒▒▒▒•▒•▒▒▒•▒
▒•••••▒••••▒••••▒•••••▒
▒▒▒▒▒•▒▒▒▒ ▒ ▒▒▒▒•▒▒▒▒▒
▒•▒ ▒•▒
▒▒▒▒▒•▒ ┌──=──┐ ▒•▒▒▒▒▒
• │ │ •
▒▒▒▒▒•▒ └─────┘ ▒•▒▒▒▒▒
▒•▒ ▒•▒
▒▒▒▒▒•▒ ▒▒▒▒▒▒▒ ▒•▒▒▒▒▒
▒••••••••••▒••••••••••▒
▒•▒▒▒•▒▒▒▒•▒•▒▒▒▒•▒▒▒•▒
▒o••▒•••••••••••••▒••o▒
▒▒▒•▒•▒•▒▒▒▒▒▒▒•▒•▒•▒▒▒
▒•••••▒••••▒••••▒•••••▒
▒•▒▒▒▒▒▒▒▒•▒•▒▒▒▒▒▒▒▒•▒
▒•••••••••••••••••••••▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
```
* add option `--width` to `format` CLI
* if width is not specified use terminal width
* if terminal width is unknown, use default (100)
This should help with testing (not so) long layouts like in #1473.
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
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.
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).
This PR introduces an `--autoplay` flag that causes the solution stored in the `solutions` field of a scenario file (which was selected on the command line via the `--scenario` option) to be automatically run when the game starts, identically to the `--run` option.
The purpose is to:
1. more easily showcase scenarios to the community
2. facilitate a quick a sanity check that the solution text in the yaml file is correct
# Test procedure
stack run -- --scenario data/scenarios/Tutorials/conditionals.yaml --autoplay
This has the same effect as manually copying the solution stored in `conditionals.yaml` to `myfile.sw`, and then running:
stack run -- --scenario data/scenarios/Tutorials/conditionals.yaml --run myfile.sw
<a href="https://asciinema.org/a/mGB5VDKhsLLPdrItZWi3qsHUV" target="_blank"><img src="https://asciinema.org/a/mGB5VDKhsLLPdrItZWi3qsHUV.svg" width="640" /></a>
It is probably safer to have Template Haskell GitInfo in Main and not depend on it in Swarm modules.
Depending on how the build system and TH interact we were either recompiling too often or not often enough.
- if the git info was evaluated once and not again after making a commit, then it was not up to date
- if the git info was reevaluated on every commit then we would needlessly recompile dependent modules
I believe it was only the former, but this helps even in that case (any code change recompiles Main).
- when `datadir` is not available, try using the XDG data directory
This way the game can be installed as an executable and data files unpacked to `~/.local/share/swarm/data`.
Notice that the XDG data folder is `~/.local/share/swarm`; inside it is the unpacked `data`.
The alternative approach is to use the environment variable `swarm_datadir` and set that to the unpacked data folder.
That works (even after this change) but is not very beginner friendly.
Ideally, we would like to set this in Cabal when building executable, for example to `/usr/share/swarm/<version>`.
Reading through haskell/cabal#5997, it looks like that is not supported.
- query GitHub Releases for a new version
- add `swarm version` command
- show the version notification in the main menu
- add a new app-wide logging queue
- show the app-wide logs in the main menu as Messages
- catch and show the web API failure in the Messages
- closes#66
- closes#627
This change introduce a new web API for the game.
The initial goal is to expose the internal state using the
`/robots` and `/robot/$id` endpoint to query the json representation
of the robots.
A new command line argument is added:
- When `--port 0` is set, the web service is disabled.
- When `--web port` is set, the game won't start if the web service fail to bind the port.
- Otherwise, when `--web` is not set, the service may start on 5357 if the port is available.
Example usage:
```
swarm --web 5357
curl localhost:5357/robots | jq
```
Future improvement may include:
- Ability to load program to the base (to send command from external text editor)
- Serve a html interface for alternative visualization
- add `generate` subparser to the executable
- create a generator for a Graphviz entity dependencies graph
- part of #344
You can interactively test this with:
```bash
$ cabal run swarm:swarm -- generate recipes \
| sed -n '/^digraph/,$p' > docs/recipes.dot
$ xdot docs/recipes.dot
```
Generalize challenges + various modes to all be "scenarios" which are described by `.yaml` files in `data/scenarios`.
- Both challenges and classic/creative modes are now subsumed under the more general notion of "scenarios".
- A scenario describes how to set up the world etc. when starting a game; all scenarios are stored in a `.yaml` file in `data/scenarios`.
- "New game" menu item now lets the user choose a scenario.
- Some small improvements to the way seeds are handled.
See #296. This will enable #35 and #25 .
Fixes#82.
Right now it only works in classic mode. It wouldn't be too hard to
make it work with challenge mode as well, but that would require some
more careful thinking about whether every challenge is required to
have a 'base' and how we identify which robot that is, so that we run
the given file on the right robot.
- adds CLI option `--seed` and propagates it to `testWorld2` (Closes#14)
- moves the base in the tree shade (Closes#90)
- makes the `random` command depend on initial seed (Closes#13)