Commit Graph

210 Commits

Author SHA1 Message Date
Karl Ostmo
5f53082971
Render command matrix (#1658)
## Demo

1. Run `scripts/play.sh`
2. Load http://localhost:5357/command-matrix.html

The rows are sortable by column.

### Also

    stack build swarm:swarm-docs --fast && stack exec swarm-docs -- cheatsheet --matrix



## Screenshot

![Screenshot from 2024-01-21 21-32-56](https://github.com/swarm-game/swarm/assets/261693/f92f5ac9-8440-4aac-9a4b-9e5edac616f2)
2024-01-26 01:02:14 +00:00
Karl Ostmo
101b17c882
Improve separation of engine and scenario sublibraries (#1743)
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.
2024-01-26 00:48:20 +00:00
Karl Ostmo
3720e94c0a
move activity counts into swarm-engine (#1742)
Towards #1741
2024-01-24 23:47:44 +00:00
Karl Ostmo
05613dfba4
split scenario construction into separate sublibrary (#1719)
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`.
2024-01-24 21:11:14 +00:00
Karl Ostmo
9320a985e4
Use type family for RobotContext field (#1732)
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
2024-01-20 21:35:27 +00:00
Brent Yorgey
ded3c24223
Depend only on vty-crossplatform (#1727)
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.
2024-01-13 23:47:17 +00:00
Karl Ostmo
a94ab9d97c
autogenerated sublibrary diagram (#1720)
Towards #1689

![sublibrary-graph](https://github.com/swarm-game/swarm/assets/261693/b300fb0d-be59-4fc9-a042-04ceaf4909b2)
2024-01-08 20:16:00 +00:00
Karl Ostmo
3cfc3c4ee9
extract game engine sublibrary (#1714)
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`.
2024-01-05 18:26:06 +00:00
Karl Ostmo
be34d0c027
sort module list (#1705) 2024-01-04 13:23:15 -08:00
Karl Ostmo
e4487aba0f
move 'Swarm.Language.*' modules to sublibrary (#1704) 2024-01-04 13:21:02 -08:00
Karl Ostmo
979c72b72c
bump cabal-build version, fix haddock script (#1703)
Encountered this bug: https://github.com/commercialhaskell/stack/issues/5254#issuecomment-1874622685

Fixed by using `cabal` to generate haddocks instead of `stack`.
Also bumped `cabal-version` to latest and adjusted `swarm.cabal` to conform.
2024-01-03 20:47:00 +00:00
Karl Ostmo
1da74a79dd
move ReadableIORef to util sublibrary (#1700)
As title.
2024-01-01 02:29:07 +00:00
Karl Ostmo
f6f01b264c
web sublibrary (#1697)
Towards #1043

Creates a new sub-library for the web service.  Also moved the `Swarm.App` module from the `swarm` library to the `swarm` executable.
2024-01-01 02:06:58 +00:00
Karl Ostmo
358e60bb87
demo pixi.js (#1650)
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
2023-12-20 06:41:38 +00:00
Karl Ostmo
13ae996306
recognize structures with rotation (#1678)
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)
2023-12-13 21:21:08 +00:00
Karl Ostmo
1bcd050780
Inventory requirements for child robots of system robots (#1677)
Towards #1664.

This PR adds a failing unit test.
2023-12-13 16:49:33 +00:00
Karl Ostmo
36a0046ece
unit test for recipe coverage (#1676)
Towards #1268

## Demo

    scripts/run-tests.sh --test-arguments '--pattern "Recipe coverage"'
2023-12-12 15:32:38 +00:00
Karl Ostmo
938aa2cbce
split utils into sublibrary (#1675)
Towards #1043

This is the first of potentially more granular sub-library splits.
2023-12-07 17:21:03 +00:00
Karl Ostmo
b8d37a9364
extract doc generator to separate executable (#1671)
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
2023-12-04 03:45:07 +00:00
Karl Ostmo
3094abd565
Offload subrecords of GameState to other modules (#1667)
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`
2023-11-30 21:57:39 +00:00
Karl Ostmo
b244a4223c
Split Step.hs (#1665)
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`.
2023-11-28 17:41:14 +00:00
Karl Ostmo
724650d8d9
serve static index page (#1648)
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.
2023-11-21 17:10:46 +00:00
Karl Ostmo
bba15a7337
split cheatsheet generation to separate module (#1640)
Towards #1546.

This is a no-op refactoring to reduce the size of `Gen.hs` by about 200 lines.
2023-11-21 03:43:00 +00:00
Karl Ostmo
01a5b070e7
render map to PNG format (#1632)
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)
2023-11-20 04:37:49 +00:00
persik
a306d05f61
Add TimeEffect effect for getting current time (#1620)
fixes #1502

Add TimeEffect effect for getting current time.
2023-11-15 06:17:09 +00:00
Karl Ostmo
f9ef0094f0
path caching (#1595)
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
2023-11-14 21:15:00 +00:00
Chris Hackett
ca1918a443
Update to support running on Windows (#1617)
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.
2023-11-14 20:58:34 +00:00
Karl Ostmo
5a91c384e1
Extract pure walkability logic (#1614)
This simplifies the implementation of #1595.
2023-11-10 15:37:16 +00:00
Brent Yorgey
96d3062550
update to megaparsec-9.6.1 (#1609)
* Update to `megaparsec-9.6.1`.  This allows us to remove any annoying workaround. 
* Bump stack resolver to LTS-21.19.
* Drop support for GHC 8.10.
2023-11-08 12:26:23 -06:00
Karl Ostmo
d63e7d81ef
Structure browser and recognizer (#1579)
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
2023-11-08 06:44:27 +00:00
Brent Yorgey
990195f44c
0.5 release (#1606)
Closes #1543 .
2023-11-01 10:18:16 +00:00
Karl Ostmo
3a39873437
Use rich entity modification info (#1604)
This is a refactoring that is a prerequisite for both #1579 and #1595.
2023-10-29 20:38:16 +00:00
Ondřej Šebek
2c3fc525c9
Add wave program to benchmarks (#1576)
* 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.
2023-10-09 04:45:27 +00:00
Karl Ostmo
4e886e0c3c
Autogenerate scenario schema doc (#1441)
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
2023-10-09 02:11:30 +00:00
Karl Ostmo
93bbbe3dd0
ping command (#1541)
Closes #1535
# Demo

    scripts/play.sh -i scenarios/Testing/1535-ping/1535-in-range.yaml --autoplay
2023-09-24 07:49:48 +00:00
Karl Ostmo
e06e04f622
Pathfinding command (#1523)
Closes #836

# Tests
    scripts/run-tests.sh  --test-arguments '--pattern "Pathfinding"'

# Demo

    scripts/play.sh -i data/scenarios/Testing/836-pathfinding/836-automatic-waypoint-navigation.yaml --autoplay
2023-09-19 06:21:03 +00:00
Brent Yorgey
4366026d64
allow megaparsec-9.5 (#1525)
Tests all passed with `cabal test --constraint='megaparsec >= 9.5'`.
2023-09-15 10:10:41 +00:00
Karl Ostmo
a9ded587eb
Render map preview on scenario selection screen (#1515)
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▒
▒▒▒•▒•▒•▒▒▒▒▒▒▒•▒•▒•▒▒▒
▒•••••▒••••▒••••▒•••••▒
▒•▒▒▒▒▒▒▒▒•▒•▒▒▒▒▒▒▒▒•▒
▒•••••••••••••••••••••▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

```
2023-09-15 06:11:47 +00:00
Brent Yorgey
85b33ef5c9
Refactor LogEntry type (#1513)
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
2023-09-15 03:08:25 +00:00
Gagan Chandan
c150b05fb0
Add syntax highlighting and LSP configuration for Vim/Neovim (#1518)
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.
2023-09-13 22:11:29 +00:00
Karl Ostmo
f9a1c31c7f
Fix build error regarding 'tasty' bounds (#1505)
[Build error](https://github.com/swarm-game/swarm/actions/runs/6133922036/job/16646223425?pr=1503) was:
```
Test/Tasty/QuickCheck.hs:30:43: error:
    Module ‘Test.Tasty.Runners’ does not export ‘emptyProgress’
   |
30 | import Test.Tasty.Runners (formatMessage, emptyProgress)
   |                                           ^^^^^^^^^^^^^
Error: cabal-3.10.1.0: Failed to build tasty-quickcheck-0.10.3. See the build log above for details.
```
2023-09-10 01:04:28 +00:00
Karl Ostmo
98ebf74cfe
more documentation tweaks (#1493)
Also:
* adds a script to view locally-generated Haddocks.
* Describes module organization as per https://github.com/swarm-game/swarm/pull/1069#issue-1565024308 in the toplevel cabal package description
2023-09-07 07:00:44 +00:00
Karl Ostmo
1aa92d01ea
Robot activity counts in F2 menu (#1484)
Towards #1341.

    scripts/play.sh -i data/scenarios/Testing/1341-command-count.yaml --autoplay --speed 1

![image](https://github.com/swarm-game/swarm/assets/261693/f658bb9c-6bb8-494c-b204-6d5bb0106b92)
2023-09-07 06:00:07 +00:00
Brent Yorgey
15dd824c6a
Improvements to scrolling (#1481)
- Add scrollbars on both the inventory and info panels
- Get rid of `. . .` at top and bottom of info panel, since we now have scrollbar as a visual indicator when there is more content
- Allow scrolling the REPL history (closes #60)
    - PgUp/PgDown can be used to scroll (Shift+PgUp/Dn were not recognized on my system)
    - Hitting any other key causes the view to jump back to the very bottom
    - A computation finishing + printing an output also causes the view to jump to the bottom
    - The REPL history is cached so that it only gets re-rendered whenever a new history entry (i.e. input or output) is added; this is needed since the history could get quite large.
- Also, fix the height of the key hint menus to 2 lines, even when the panel-specific menu (second line) is blank, so the world panel does not keep resizing as we move the focus between panels.

Thanks to @jtdaugherty for releasing `brick-1.10` with a new ability to specify blank space to the side of scrollbars; see https://github.com/jtdaugherty/brick/discussions/484 .

Also towards #1461 .
2023-09-03 04:51:01 +00:00
Ondřej Šebek
2fe7181fc4
Allow specific format width (#1476)
* 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.
2023-08-28 19:11:26 +00:00
Brent Yorgey
fb7d2eaeb9
0.4 release blog post (#1444)
Blog post highlighting cool features in the newest release.

Also fix the version number in the .cabal file from 0.4 to 0.4.0.0. It's too late for the release (it always reports there is a new version available since 0.4.0.0 does not match 0.4) but oh well.
2023-08-22 12:35:05 -05:00
Karl Ostmo
da6ad0c874
combustion (#1432)
Closes #1355

# Demo

    scripts/play.sh -i data/scenarios/Testing/1355-combustion.yaml --autoplay

![image](https://github.com/swarm-game/swarm/assets/261693/eda5d1c7-35fa-4fce-865d-a87c83923c61)
2023-08-21 02:23:52 +00:00
Brent Yorgey
e5e8ea5dac
0.4 release (#1321)
Closes #1316 .
2023-08-18 02:35:43 +00:00
Brent Yorgey
888ee44d18
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.
2023-08-17 11:08:42 +00:00
Brent Yorgey
8aea6a24be
Refactor to more consistently use "capability style" in loading + initializing code (#1392)
Closes #1122 .  General principles:

- Use `SystemFailure` as error rather than `Text` as much as possible, and use `prettyFailure` only at the very top level.
- Replace `ExceptT` with `Has (Throw SystemFailure)` constraint.
- Use `Accum (Seq SystemFailure)` constraints to accumulate warnings that should not abort computation, rather than returning a pair of a list of warnings + result.
- Use `Has (Lift IO)` constraint instead of `MonadIO`, which means using `sendIO` instead of `liftIO`.
- In general, use `runThrow` to dispatch a `Throw` constraint (results in returning an `Either`, just like `runExceptT`), and `runM` to dispatch a final `Lift IO` constraint to result in an `IO` computation.
- Use `withThrow` to adapt from one type of error to another.
2023-08-06 22:12:41 +00:00