The problem was that we loaded an entire directory with `mapM loadScenarioItem` which caused the entire directory to fail if any single scenario did. Now we run each individual `loadScenarioItem` call with `runExceptT` and appropriately collect up the individual failures together with any warnings from the successfully loaded scenarios.
Fixes#1380.
Closes#1379
Related: #950
Also moves "directions" types/logic into its own module.
## Demo
scripts/play.sh --scenario data/scenarios/Testing/1379-single-world-portal-reorientation.yaml --autoplay
Closes#144.
This builds upon portals support (#1356)
# Demo
scripts/play.sh --scenario data/scenarios/Testing/144-subworlds/subworld-mapped-robots.yaml --autoplay --speed 2
[![asciicast](https://asciinema.org/a/vC13dW8M1S8t2b1J4XkW80U1q.svg)](https://asciinema.org/a/vC13dW8M1S8t2b1J4XkW80U1q)
# Future work
* Augment portal definitions with an optional "relative orientation" attribute, that can turn the player around when passing through the portal (#1379)
* Specify whether portal performs instant transportation or whether `move down` is required (#1368)
Support GHC 9.6 / `base-4.18`, `mtl-2.3`, `megaparsec-9.4`, `servant-0.20`, `servant-docs-0.13`, `servant-server-0.20`, `template-haskell-2.20`, `optparse-applicative-0.18`, fix a bunch of new warnings, and update CI to test on GHC 9.6.
---------
Co-authored-by: restyled-io[bot] <32688539+restyled-io[bot]@users.noreply.github.com>
Co-authored-by: Restyled.io <commits@restyled.io>
[Stackage LTS 21](https://www.stackage.org/blog/2023/06/announce-lts-21-nightly-ghc9.6) was just released, which is great news for us because it includes GHC 9.4.5 (GHC 9.4.4 was no longer supported by HLS) and we no longer have to rely on a specific "nightly" version. This PR updates a few things to build with LTS-21.
The biggest thing I *didn't* update was our `lsp` dependency: LTS-21 comes with `lsp-2.0` and `lsp-types-2.0`, but those apparently introduce some breaking changes and it wasn't immediately apparent to me what would need to change. I filed https://github.com/swarm-game/swarm/issues/1350 to track that issue.
A simulation of traffic on intersecting roads. This is a "dynamic vignette"---an animated, looping scene that a player might come upon in their exploration of the `swarm` world. Such scenes would presumably be "paused" until within range of the player.
Makes use of structure templates (#1332) and records (#1148).
scripts/play.sh --scenario data/scenarios/Vignettes/roadway.yaml
![image](https://github.com/swarm-game/swarm/assets/261693/8e52e206-be90-4d40-932f-446f87c80ef7)
Towards #1297 . In this PR:
- Moves some functions out of `Pipeline` and into more appropriate modules
- Adds a new type `Join` to represent two types or other things, one "expected" and one "actual", at the point where they join + we check whether they are equal; a bunch of refactoring to use this new type. This way we don't have to remember which argument is which when calling a function that takes one "expected" thing and one "actual" thing.
- Refactors all the `decomposeXXX` functions to take a `Syntax` node to use in error messages, and to take a `Sourced UType` (*i.e.* a `UType` along with whether it is "expected" or "actual") instead of just a `UType` so we can generate accurate error messages
- Introduce a basic typechecking stack that keeps track of "what we are currently in the middle of checking", so that when we encounter an error we can say things like "while checking the definition of `foo`". Currently this is just a basic proof of concept.
- A few other miscellaneous error message improvements.
This fixes#1267 in the best possible way (option 3): if a `wait` command is executed inside `instant`, then it goes into effect immediately, and any subsequent commands will be executed `instant`ly once the robot wakes up. For example, `instant (move; move; wait 1; move; move)` moves twice in one tick and then twice in the next tick. Even calls to `wait` nested inside recursive function calls inside a call to `instant` work fine.
Towards #558
I was motivated to build this after finding that editing scenario maps directly in the YAML file is rather constraining.
## What I've implemented so far
* A small, collapsible panel to the left of the REPL containing World Editing status/operations. Enter world-editing mode with CTRL+e to show the panel.
* This works only in `--cheat` mode
* Terrain selection
* A "picker"/"eye dropper" middle-click mechanism to select a terrain style to draw.
* A pop-up selector to choose between the 5 different types of terrain.
* Drawing terrain with the left mouse button
* Saving a rectangular section of the world map (terrain only) to a file with CTRL+s
* Code organization
* The complete state of the World Editor, including "painted overlays" of terrain, is contained within the `uiWorldEditor` field of `UIState` record.
* The bulk of the World Editor functionality shall be in new modules
* Some refactoring of `Controller.hs` and `View.hs` to extract functions utilized by the World Editor (towards #707)
## Vision
* The audience for this tooling is strictly envisioned to be Scenario authors.
* Though, if we eventually allow swarm-lang to program the UI, there may be some common code to extract.
* The World Editor is intended to be compatible with a workflow of editing maps in text form within YAML scenario files.
# Demos
## Round-trip with random world
stack run -- --scenario creative --seed 0 --cheat
Then Ctrl+e, tab down to the Save button, hit Enter to save the map
In another tab run:
stack run -- --scenario mymap.yaml
Toggle between tabs to compare, observe the derived map is an identical 41x21 subset.
Closes#358 and closes#866.
Allows specification of a seed value and/or the path of a script to run. Specifying a script to run in advance allows eligibility for code size scoring.
Some effort was invested into integrating the Brick `FileBrowser` widget and discovering its idiosyncrasies. This paves the way for more applications of `FileBrowser` within Swarm.
## Usage
From the scenario selection menu, press the `o` key to pop up a dialog for launch options.
![Screenshot from 2023-06-06 01-38-25](https://github.com/swarm-game/swarm/assets/261693/e306f2ce-db30-4906-9b02-db8e44bc5e99)
Any manually-selected initial-script or seed are persisted to disk and will pre-populate the launch configuration dialog upon the next play. If a certain scenario is subsequently launched the normal way (i.e. by pressing `Enter` instead of `o`), then this clears the saved script path/seed, and the next pop-up of the launch configuration dialog will not see its fields pre-populated.
## Warning: Save format changed
This PR changes the `ScenarioStatus` datatype, and therefore game status/progress saved previously to this PR will not be recognized. See https://github.com/swarm-game/swarm/pull/974#discussion_r1111335102 for discussion about this situation.
This is so that our PR's don't suddenly start failing when the restyled.io service bumps is `fourmolu` version.
## TODO
I wasn't actually able to install version `0.10.1.0`; both of these attempts failed:
stack install fourmolu-0.10.1.0
and
cabal install fourmolu-0.10.1.0
I believe that since #1277 the progress has not been saved when exiting a scenario back to the menu.
It is because `getNormalizedCurrentScenarioPath`, which is utilized by `saveScenarioInfoOnQuit`, was returning `Nothing`, in turn because `currentScenarioPath` was `Nothing`. This is because `scenarioToAppState` was clearing it immediately after being set inside `startGameWithSeed`.
Foreshadowed by this comment: https://github.com/swarm-game/swarm/pull/1243#pullrequestreview-1411539743 .
Improvements to type inference and type error messages. Includes:
- More informative error when record literal fields don't match the expected set of fields
- More informative type mismatch error which includes the term where the mismatch occurred.
- Reinstates a case for lambdas with explicit type annotations in inference mode (which was removed in #1283), which helps preserve better error messages in some cases.
Some refactoring related to type errors:
- Change the type of `addLocToTypeErr` to something more sensible, and get rid of some redundant calls
- Instead of storing a `SrcLoc` in every single `TypeErr` constructor, factor it out into a new type `ContextualTypeErr`. This also paves the way for additional contextual information which will help generate better type error messages.
This is 100% refactoring; there should be no behavioral changes.
Towards #1007
The `use` command adopts the same return type as `drill`:
use : text -> dir -> cmd (unit + text)
## Demo
scripts/play.sh --scenario data/scenarios/Testing/1007-use-command.yaml --autoplay
The basic idea is that we try as hard as possible to stay in "checking mode", where we have an "expected type" from the context and "push it down" through a term. We also try hard to avoid generating fresh unification type variables unless absolutely necessary. That way, when we come to something that doesn't match the expected type, we can immediately flag a specific error about that part of the code, rather than later having a generic unification error.
This is mostly just refactoring, but it already improves the type error messages somewhat:
* Instead of saying something like "can't unify int and text", it might say something like "expected type int, but got text" with a reference to a specific source code location.
* It's probably possible to still get it to sometimes fail with a "can't unify" type error, but with only this technique I don't think we can ever 100% get rid of that.
* Type errors now report a specific line and column number, not just a line number like they used to.
There is still a lot more I want to do to improve the type error messages but this is a start.
Closes#99.
All info that needs to be persistent (*e.g.* the set of standard/default entities and recipes) is now stored in the `RuntimeState`. The `GameState` record is now recreated completely from scratch every time upon starting a new scenario, so custom entities and recipes from the previous scenario do not persist into the next.
Fixes#516. Fixes#689.
Also a bunch of related refactoring/simplification relating to how we were initializing state for tests and benchmarks.
I also want to refactor the tests so we only load data from disk once, but I have split that out into a separate issue (#1279).
# Summary
This PR allows a recipe's effects to be applied within the same tick if `time: 0` is specified for the recipe.
The previous behavior was that if the `time` of the recipe was zero, it would nonetheless behave the same as `time: 1`, and the entity updates would not be applied until the next tick.
This new behavior is useful when "abusing" the `drill` command to emulate various other games via help from a system robot.
# Motivation
For the "Lights Out" game (#1273) I have implemented a system robot that `instant`-ly toggles the four adjacent lights and the center light when a center light is `drill`-ed by the player. The light entities are named `"off"` and `"on"`.
I did some experiments to see if the system robot would react "fast enough" if the player drilled quickly in succession.
# Experiments
The 5x5 light grid is initially `"off"`. I moved the player robot to the center of the grid, facing `east`. From this position, I tried three command sequence variations both *before* and *after* this PR. The recipes for toggling lights specify `time: 0`.
## Before this PR
### 1. Drilling twice in succession
drill down; drill left;
This resulted in an error:
![no-instant-error-message](https://github.com/swarm-game/swarm/assets/261693/f22a0a28-5df7-4fe8-ac18-97504d64e3c8)
so only the first `drill` got applied:
![no-instant](https://github.com/swarm-game/swarm/assets/261693/2d9e4ba7-c138-4dcb-94a6-3c7bb14ddd68)
This is because I believe the sequence of events was:
| Tick / Robot | Action |
| --- | --- |
| **Tick 1:** Player | Execute the first `drill` command, queue entity modification |
| **Tick 1:** System | No changes observed |
| **Tick 2:** Player | Entity modification from first `drill` is applied. Execute second `drill` command, queue second entity modifications |
| **Tick 2:** System | Observe modified entity, toggle the adjacent lights |
| **Tick 3:** Player | Try to apply entity modification from second `drill` command. Throw an error because the system robot has toggled the drill's target entity to `on` whereas the `drill` was initially executed when it was still `off`. |
### 2. Instant for both drills
instant $ (drill down; drill left)
In this case, both of the player's `drill` commands were executed, but the system robot then failed to doubly-invert one of the cells:
![instant-both-drills](https://github.com/swarm-game/swarm/assets/261693/65642fd6-3cea-4834-96fd-404da7628c66)
This is due to some kind of race condition in the system robot's logic. However, it is irrelevant because the `instant` command shall not be available to the player.
### 3. Instant for first drill only, then second drill
instant (drill down); drill left;
This accomplished what approach number 1 was supposed to; both drill commands were applied and the two overlapping cells got properly inverted:
![instant-first-drill](https://github.com/swarm-game/swarm/assets/261693/64aab681-140d-457a-9273-943ae5b4d58d)
**Explanation:** In this case, the entire drilling operation (including applying all effects to entities on the ground) was completed within the player's first tick, so the system robot updated the lights appropriately on its turn. Then, in the next tick the player robot applied another drill command, and again the system robot appropriately did another inversion on the two overlapping middle cells.
## After this PR
### 1. Drilling twice in succession
drill down; drill left;
Now works as expected:
![after-no-instant](https://github.com/swarm-game/swarm/assets/261693/538767d3-9832-4e5c-996e-12a9b7a2015c)
### 2. and 3.
Behaved identically to before the PR.
Closes#392 . Adds a command `halt : actor -> cmd unit` which halts the given robot if it is within a distance of 1 (no distance limit for system robots or in creative mode). `halt self` works too. Privileged robots (i.e. system robots, or when in creative mode) can halt any other robot. Unprivileged robots cannot halt system robots.