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
Update to use the `lsp-2.4` API. Closes#1350.
Initially I hoped that any `lsp-2.x` would work. However, the `SeverDefinition` record changed in `2.2` so I initially set that as a lower bound. But then it turns out that `2.4` changed which module it is importing `Rope` from; since we work with ropes in the `Hover` module it matters since we have to import the matching module. Updating to the new `Rope` type also required some changes as the API provided by the new `Rope.Mixed` module is a bit different than the old module, so we would not even be able to easily put in CPP to conditionally depend on the right rope module depending on the `lsp` version. Finally, this means dropping support for GHC 9.0 since `lsp-2.4` does not support it.
Along the way I also fixed a minor issue related to showing type information returned by the LSP server, so that it uses `prettyTypeLine` to display the type on a single line (in my editor, when the type does not use a single line it gets cut off). For comparison see also #1610.
This refactoring was a big pain because a lot of things (names of types and constructors, locations of exports, etc...) changed from 1.x to 2.x, but there was not much in the way of documenting what had changed. =( I am pretty sure that all functionality has been preserved but I would appreciate independent confirmation.
This is also a prerequisite for updating other dependencies such as the `base` version (I will open a follow-up PR soon) since the old `lsp-1.x` versions do not allow many newer versions of various dependencies.
In support of #1598 (for #1739).
Runs the `idle` benchmark for many more ticks and with many more robots to try to emphasize the effect of regressions and mitigate jitter.
Also factors out common code from the `benchmark-against-parent.sh` script.
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)
Measures the volume of an enclosed space.
A useful alternative to the `path` command for goal checking.
## Demo
scripts/play.sh -i data/scenarios/Testing/1747-volume-command.yaml --autoplay --speed 2
Closes#1741
Remaining blocker was to move logging to concrete robot.
Most notably, the `swarm-scenario` sublibrary has been purged of its temporal component, corroborated by the removal of the `time` package dependency.
Towards #1715 and #1043
Creates a new `swarm-scenario` sublibrary intended for scenario data that is independent of game state.
# Planned follow-ups
This PR is already pretty large, but there is still more that can be done regarding sublibrary reorganization/splitting.
* May want to pare-down a sublibrary exclusively for world-generation without all the other baggage of scenarios.
* `Swarm.Game.ScenarioInfo`, `Swarm.Game.Tick`, and `Swarm.Game.Scenario.Status` could probably be moved from `swarm-scenario` to `swarm-engine`.
Continuation of #1731.
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:
* Extracts the `RobotContext` type to a new module
* Introduces a type family for the `RobotContext` field
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.
Towards #1043
Extracts `Swarm.Game.*` modules to their own sublibrary.
There was already pretty good separation along this boundary; just had to move three functions into a new module `Swarm.Util.Content`.
Towards #1415.
Potential candidate for a web-based world display
Uses [pixi.js](https://pixijs.com/).
# Demo
1. Run swarm: `scripts/play.sh -i scenarios/Fun/horton.yaml --autoplay --speed 6`
1. Visit http://localhost:5357/play.html
Closes#1644.
The `"recognize"` property in scenario `.yaml` files is changed from a boolean to a list of "up" directions.
The structure recognizer adds a rotated copy of each supported orientation to its automaton. Rotational symmetry is accounted for to avoid duplicate work in the recognizer.
Also in this PR:
* Add cardinal directions to the JSON schema
* Tetromino packing challenge scenario
## Demos
scripts/run-tests.sh --test-arguments '--pattern "1644-rotated"'
### Structures dialog
![Screenshot from 2023-12-10 18-47-01](https://github.com/swarm-game/swarm/assets/261693/3904b66e-dd22-455b-8b68-5913021f806a)
### Tetromino packing
scripts/play.sh -i data/scenarios/Challenges/pack-tetrominoes.yaml --autoplay
![Screenshot from 2023-12-09 23-11-00](https://github.com/swarm-game/swarm/assets/261693/0ad7c0ce-3553-4ad5-a927-82bbfdbe63d8)
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
This is a continuation of #1652.
Most of the sub-records are bundled into `Swarm.Game.State.Substate`, but we create a `Swarm.Game.State.Robot` module just for robots.
We introduce a `zoomRobots` function so that applicable functions can operate directly on `Robots` state instead of `GameState`.
## Size comparison
### Before
| File | Lines |
| --- | --- |
| `State.hs` | 1569 |
### After
| File | Lines |
| --- | --- |
| `State.hs` | 812 |
| `Substate.hs` | 497 |
| `Robot.hs` | 395 |
| `Config.hs` | 21 |
## For follow-up PR:
- [ ] Remove exports of `_viewCenter` and `_focusedRobotID` from `Swarm.Game.State.Robot`
Both `execConst` and `stepCESK` are huge case statements. This refactor puts them in different modules.
Also many supporting functions for implementing commands in `execConst` are moved to their own modules.
Whereas `Step.hs` previously had `2931` lines, the new linecounts are:
| File | lines |
| --- | --- |
| `Step.hs` | 848 |
| `Arithmetic.hs` | 124 |
| `Const.hs` | 1687 |
| `Command.hs` | 417 |
The only tricky thing was that `execConst` is mutually recursive with `runCESK`. So to get them into different modules, I pass a wrapper of `runCESK` as an argument to `execConst`.
API docs are moved under `api/`, and the landing page for http://localhost:5357/ is now a static `index.html` page.
This paves the way for a JS-enabled web frontend demo.
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#1569
## Performance
Path cache invalidation upon world modifications (i.e. entities inserted or removed) entails iterating over all of the previously-cached paths [here](https://github.com/swarm-game/swarm/pull/1595/files#r1390158411). For efficiency's sake, we avoid iterating over "all existing robots".
Any scenario that does not use the `path` command is entirely unaffected by this change.
## Demo
Previously, this demo was virtually unplayable, since when moving between widely-spaced "clusters" of flowers, an expensive A-star search was invoked at almost every tick. Now, the vast majority of moves utilize the cache, and the demo exhibits minimal stuttering (e.g. when a single A-star search is performed when moving between distant clusters).
scripts/play.sh -i scenarios/Fun/horton.yaml --autoplay --speed 7
### Event log
An event log specific to the path cache is maintained with its own ring buffer:
scripts/play.sh -i scenarios/Testing/1569-pathfinding-cache/1569-harvest-batch.yaml --autoplay
and view http://localhost:5357/paths/log
Added support for running on Windows, in command line or Powershell terminals.
Currently terminal emulators such as mintty, ConEmu, alacritty, etc are not supported.
Addresses issues #1607 and #53.
Closes#1575
Implements structure recognition.
## Features
* Structure browsing dialog (`F6`) that becomes available if a scenario declares any recognizable structures
* Automatically recognizes statically-placed structures upon scenario initialization, accounting for occlusion by other entity/structure placement
* New `structure` command for querying location of recognized structures (primarily intended for system robots and goal checking)
* Efficiently recognizes structures immediately upon completion
* Accounts for removal of structures
* Several new integration tests
* Structured web-interface log to help understand/debug the recognition process
* Re-uses much of the functionality built previously for defining structures (#1332)
Other changes:
* Improved validation for static structure placement (ensure valid structure names instead of failing silently)
* Moved a few functions (`getNeighborLocs`, `zoomWorld`, `entityAt`, `robotWithID`, `robotWithName`) out of `Step.Util` and into `State` so that recognizer initialization, which becomes a field in `GameState`, could use them
* split `scenarioToGameState` into pure and non-pure functions
## Optimizations
Scenarios that do not make use of structure recognition are entirely unaffected, performance-wise.
Some optimizations include:
* Structure definitions must "opt-in" to participate in automatic recognition
* Aho-Corasick automatons optimized by:
* only initiate structure search if a placed entity exists on a participating structure template
* initializing different automatons for each type of "placed entity"
* pruning inapplicable row candidates for 2-D search
The row-level structure recognition cache described in #1575 was not implemented; it's probably not worth the complexity cost.
# UI Demo
scripts/play.sh -i scenarios/Testing/1575-structure-recognizer/1575-browse-structures.yaml --autoplay
1. Press `F6` for Structure Browser dialog
2. View http://localhost:5357/recognize/log and http://localhost:5357/recognize/found
![image](https://github.com/swarm-game/swarm/assets/261693/e32d3572-7e53-42d6-84cd-393c57a8aeac)
# Future improvements
* Refactor `State.hs` so that the new helper functions can live elsewhere
* Support non-rectangular recognizable structures
* Allow flip/rotate of recognizable structures
* Structure ownership by robots
* Consolidate code between the Goals and Structures modal dialogs, and the Achievements browser
* Enforce minimum/maximum dimensions for structure definitions
* add wave program and parametrise it to compare inlined/generic version
* use [`tasty-bench`](https://hackage.haskell.org/package/tasty-bench) library to show comparison
* move benchmarks to test folder as they can now share tasty code
* closes#1574
Using the recursive definition with ifs leads to a 3x slowdown:
```
wavesInlined
10: OK
361 ms ± 29 ms
20: OK
718 ms ± 35 ms
30: OK
1.066 s ± 28 ms
40: OK
1.437 s ± 37 ms
wavesWithDef
10: OK
1.052 s ± 51 ms, 2.92x
20: OK
2.117 s ± 34 ms, 2.95x
30: OK
3.144 s ± 80 ms, 2.95x
40: OK
4.191 s ± 91 ms, 2.92x
```
But if we just inline and simplify the code, we can remove the runtime overhead completely.
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
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▒
▒▒▒•▒•▒•▒▒▒▒▒▒▒•▒•▒•▒▒▒
▒•••••▒••••▒••••▒•••••▒
▒•▒▒▒▒▒▒▒▒•▒•▒▒▒▒▒▒▒▒•▒
▒•••••••••••••••••••••▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
```
In preparation for #1483. `LogEntry` started life as something specific to robot logs. It then evolved to be used in the system log as well (see #1039 and #652), but in a sort of hacky way. This PR refactors `LogEntry` to be more generic.
- Move `Swarm.Game.Log` -> `Swarm.Log` since it's not specific to gameplay.
- Rename `ErrorLevel` to `Severity`, add a new `Info` level, and add a top-level `leSeverity` field
- Rename `leRobotName` to just `leName`, since it was already being used to name both robots and system components anyway
- Move robot-specific fields (*e.g.* robot ID) into the new `RobotLogSource` type, and add `LogSource` to differentiate between robot and system logs
- Various other minor improvements and tweaks
This adds two files. The first is the syntax file responsible for basic highlighting. The highlight categories are based roughly on those seen in the Emacs file ([swarm-mode.el](https://github.com/swarm-game/swarm/blob/main/editors/emacs/swarm-mode.el)). The second is the file for configuring the language server. Since it is based on Neovim's native LSP client, it only works with Neovim and not Vim.
`README.md` in the `editor` folder has also been updated to include instructions for setting these up.