Previously, the logic to wrap horizontally was hand-coded, both for the player (using a dedicated monitor robot) and for ghosts (who, as system robots, implemented the teleportation themselves).
Now that we can use built-in portal functionality (#1356), no custom logic is needed to handle the teleportation. A (much-simplified) monitor robot still observes the base robot to award the hidden "World wrap" goal.
## Demo
scripts/play.sh -i data/scenarios/Challenges/hackman.yaml --autoplay --speed 6
Add another subrecord to `GameState` dedicated to robot fields. This shrinks the size of `GameState` by 8 more fields. It's getting reasonable now!
The `_viewCenter*` fields were put there too, for now, because the manually-defined `setter` of the `viewCenterRule` lens needs access to the `robotMap` field.
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)
Structure recognition already supports overlapping bounding boxes between two structures (where one of the structure templates has vacant cells). This PR adds test coverage for this.
More importantly, this PR makes sure that structures can be recognized even with non-participating entities within their bounding box. The same integration test (`1575-bounding-box-overlap.yaml`) covers this case as well.
A "non-participating entity" is an entity that is not an ingredient to any structures participating in structure recognition. Such entities are "masked out" when the world cells (i.e. the "haystack") are queried to apply the Aho-Corasick matcher to.
## Example
Given a structure defined as `boulder`s (`@`) in a backwards "L" shape, it will be recognized despite a `mountain` (`A`) within its bounding box:
![image](https://github.com/swarm-game/swarm/assets/261693/1c559a50-e9bd-4f97-87a1-20d4f964cfa9)
This is because `mountain` is not a member of any recognizable structures.
## Caveats
This PR strictly increases the situations in which valid structures may be recognized.
However, it is still the case that encroaching entities that **are** members of some structure template will thwart structure recognition. One consequence of this is that the order in which structures are completed can matter.
If some partially-built but incomplete structure lies within the bounding box of another candidate structure, the candidate structure will not be recognized as "complete" unless (1) the offending entities are removed first, or (2) the other structure is completed first.
Closes#1631
## Design
* Entities have a new property: a `Set` of textual tags.
* Two new commands are introduced:
* `HasTag` checks whether a single entity has a given tag
* `TagMembers` allows cycling through all members with a given tag
* `TagMembers` may be considered more powerful than `HasTag`, so has its own separate capability (`CTagmembers`).
* A map is computed at scenario initialization to facilitate `TagMembers` lookups.
* Tag names are highlighted in yellow in markdown.
## Demo
scripts/play.sh -i scenarios/Testing/1631-tags.yaml --autoplay
## Other changes
* Incidentally, changed `knownEntities` from a list to a `Set` so that `Set.member` can be used instead of `elem`.
Closes#1624.
Note, however, that currently (until #1623) this will not work on Windows, since I explicitly added `vty-unix` as an `extra-dep` in the `stack.yaml` file. However, I expect we will get #1623 soon and then we can remove it.
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.
* Highlight occurrences of structures in markdown in red
* Add a new `blueprint` entity to provide `floorplan` and `structure` commands
* new `floorplan` command to get the dimensions of a structure
* structure location is southwest instead of northwest corner
* handle structures with "holes" for statically-recognized structures and when modifying interior cells post-recognition
Each of these is its own commit for reviewability's sake.
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
This allows the validation to work in VS Code if one happens to open the symlinked version of a scenario file, which is easy to do accidentally via CTRL+P.
* 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
Towards #1494.
Replaced/restricted uses of `Prelude.tail` and `Prelude.!!`. Quarantined `undefined`.
Introduced a new function `listEnumsNonempty` that is guaranteed safe.
Should benchmark this to see if it's faster than `Integer`.
# `stack bench` output
Personally, I'm not sure what the best way to benchmark this particular change, but here's the output of `stack bench` anyway:
## Before
```
benchmarking run 1000 game ticks/idlers/10
time 2.634 ms (2.629 ms .. 2.641 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 2.636 ms (2.634 ms .. 2.640 ms)
std dev 10.45 μs (6.667 μs .. 15.88 μs)
benchmarking run 1000 game ticks/idlers/20
time 2.660 ms (2.653 ms .. 2.668 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 2.661 ms (2.657 ms .. 2.667 ms)
std dev 18.53 μs (12.24 μs .. 30.77 μs)
benchmarking run 1000 game ticks/idlers/30
time 2.660 ms (2.653 ms .. 2.670 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 2.685 ms (2.678 ms .. 2.696 ms)
std dev 29.12 μs (20.64 μs .. 42.37 μs)
benchmarking run 1000 game ticks/idlers/40
time 2.693 ms (2.686 ms .. 2.700 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 2.694 ms (2.689 ms .. 2.705 ms)
std dev 26.61 μs (17.81 μs .. 45.78 μs)
benchmarking run 1000 game ticks/trees/10
time 2.627 ms (2.623 ms .. 2.632 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 2.625 ms (2.622 ms .. 2.629 ms)
std dev 12.55 μs (8.749 μs .. 18.38 μs)
benchmarking run 1000 game ticks/trees/20
time 2.647 ms (2.642 ms .. 2.653 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 2.648 ms (2.645 ms .. 2.656 ms)
std dev 17.86 μs (10.58 μs .. 31.07 μs)
benchmarking run 1000 game ticks/trees/30
time 2.672 ms (2.667 ms .. 2.676 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 2.673 ms (2.668 ms .. 2.683 ms)
std dev 22.85 μs (14.67 μs .. 38.46 μs)
benchmarking run 1000 game ticks/trees/40
time 2.697 ms (2.693 ms .. 2.700 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 2.699 ms (2.694 ms .. 2.708 ms)
std dev 23.96 μs (16.47 μs .. 38.01 μs)
benchmarking run 1000 game ticks/circlers/10
time 229.9 ms (229.0 ms .. 230.8 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 230.3 ms (230.0 ms .. 230.8 ms)
std dev 639.7 μs (458.4 μs .. 842.9 μs)
variance introduced by outliers: 11% (moderately inflated)
benchmarking run 1000 game ticks/circlers/20
time 434.7 ms (433.9 ms .. 435.2 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 435.7 ms (435.2 ms .. 436.5 ms)
std dev 800.9 μs (432.2 μs .. 1.072 ms)
variance introduced by outliers: 14% (moderately inflated)
benchmarking run 1000 game ticks/circlers/30
time 637.5 ms (634.5 ms .. 641.2 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 640.5 ms (639.2 ms .. 641.6 ms)
std dev 1.553 ms (1.244 ms .. 1.907 ms)
variance introduced by outliers: 16% (moderately inflated)
benchmarking run 1000 game ticks/circlers/40
time 851.5 ms (NaN s .. 864.0 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 852.1 ms (850.0 ms .. 853.4 ms)
std dev 2.138 ms (657.7 μs .. 2.908 ms)
variance introduced by outliers: 19% (moderately inflated)
benchmarking run 1000 game ticks/movers/10
time 382.4 ms (379.9 ms .. 384.1 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 383.9 ms (382.8 ms .. 385.1 ms)
std dev 1.601 ms (973.2 μs .. 2.420 ms)
variance introduced by outliers: 14% (moderately inflated)
benchmarking run 1000 game ticks/movers/20
time 721.9 ms (712.2 ms .. 729.8 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 722.6 ms (720.4 ms .. 725.9 ms)
std dev 3.234 ms (1.128 ms .. 4.250 ms)
variance introduced by outliers: 19% (moderately inflated)
benchmarking run 1000 game ticks/movers/30
time 1.076 s (1.063 s .. 1.095 s)
1.000 R² (1.000 R² .. 1.000 R²)
mean 1.071 s (1.067 s .. 1.074 s)
std dev 4.317 ms (2.285 ms .. 5.464 ms)
variance introduced by outliers: 19% (moderately inflated)
benchmarking run 1000 game ticks/movers/40
time 1.440 s (1.436 s .. 1.446 s)
1.000 R² (NaN R² .. 1.000 R²)
mean 1.436 s (1.435 s .. 1.438 s)
std dev 1.923 ms (544.1 μs .. 2.572 ms)
variance introduced by outliers: 19% (moderately inflated)
```
## After
```
benchmarking run 1000 game ticks/idlers/10
time 2.559 ms (2.515 ms .. 2.626 ms)
0.996 R² (0.994 R² .. 0.999 R²)
mean 2.557 ms (2.539 ms .. 2.583 ms)
std dev 83.13 μs (63.17 μs .. 120.2 μs)
variance introduced by outliers: 20% (moderately inflated)
benchmarking run 1000 game ticks/idlers/20
time 2.518 ms (2.508 ms .. 2.528 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 2.534 ms (2.526 ms .. 2.546 ms)
std dev 36.80 μs (30.33 μs .. 51.03 μs)
benchmarking run 1000 game ticks/idlers/30
time 2.544 ms (2.527 ms .. 2.565 ms)
0.999 R² (0.999 R² .. 1.000 R²)
mean 2.540 ms (2.533 ms .. 2.551 ms)
std dev 35.25 μs (23.47 μs .. 54.64 μs)
benchmarking run 1000 game ticks/idlers/40
time 2.598 ms (2.578 ms .. 2.627 ms)
0.997 R² (0.995 R² .. 0.999 R²)
mean 2.693 ms (2.660 ms .. 2.733 ms)
std dev 135.0 μs (110.1 μs .. 169.4 μs)
variance introduced by outliers: 38% (moderately inflated)
benchmarking run 1000 game ticks/trees/10
time 2.590 ms (2.538 ms .. 2.642 ms)
0.998 R² (0.998 R² .. 0.999 R²)
mean 2.548 ms (2.538 ms .. 2.566 ms)
std dev 49.94 μs (39.27 μs .. 67.19 μs)
benchmarking run 1000 game ticks/trees/20
time 2.617 ms (2.568 ms .. 2.679 ms)
0.998 R² (0.997 R² .. 0.999 R²)
mean 2.562 ms (2.541 ms .. 2.585 ms)
std dev 78.13 μs (63.71 μs .. 95.38 μs)
variance introduced by outliers: 19% (moderately inflated)
benchmarking run 1000 game ticks/trees/30
time 2.559 ms (2.533 ms .. 2.591 ms)
0.999 R² (0.999 R² .. 1.000 R²)
mean 2.555 ms (2.544 ms .. 2.573 ms)
std dev 49.00 μs (34.54 μs .. 65.61 μs)
benchmarking run 1000 game ticks/trees/40
time 2.820 ms (2.729 ms .. 2.906 ms)
0.994 R² (0.991 R² .. 0.996 R²)
mean 2.620 ms (2.594 ms .. 2.666 ms)
std dev 121.2 μs (86.30 μs .. 167.3 μs)
variance introduced by outliers: 34% (moderately inflated)
benchmarking run 1000 game ticks/circlers/10
time 230.6 ms (228.8 ms .. 232.5 ms)
1.000 R² (0.999 R² .. 1.000 R²)
mean 233.5 ms (231.7 ms .. 236.0 ms)
std dev 3.275 ms (1.853 ms .. 5.093 ms)
variance introduced by outliers: 11% (moderately inflated)
benchmarking run 1000 game ticks/circlers/20
time 448.8 ms (433.6 ms .. 464.6 ms)
0.999 R² (0.999 R² .. 1.000 R²)
mean 441.1 ms (437.3 ms .. 446.1 ms)
std dev 5.991 ms (3.879 ms .. 8.282 ms)
variance introduced by outliers: 14% (moderately inflated)
benchmarking run 1000 game ticks/circlers/30
time 655.5 ms (646.9 ms .. 666.9 ms)
1.000 R² (NaN R² .. 1.000 R²)
mean 652.3 ms (650.6 ms .. 654.5 ms)
std dev 2.448 ms (1.442 ms .. 3.702 ms)
variance introduced by outliers: 16% (moderately inflated)
benchmarking run 1000 game ticks/circlers/40
time 863.8 ms (861.6 ms .. 868.3 ms)
1.000 R² (1.000 R² .. NaN R²)
mean 865.5 ms (864.4 ms .. 867.3 ms)
std dev 1.833 ms (84.64 μs .. 2.435 ms)
variance introduced by outliers: 19% (moderately inflated)
benchmarking run 1000 game ticks/movers/10
time 397.4 ms (382.4 ms .. 412.7 ms)
0.999 R² (0.996 R² .. 1.000 R²)
mean 395.7 ms (391.2 ms .. 400.5 ms)
std dev 6.397 ms (4.350 ms .. 8.877 ms)
variance introduced by outliers: 14% (moderately inflated)
benchmarking run 1000 game ticks/movers/20
time 721.3 ms (698.4 ms .. 742.5 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 722.5 ms (719.8 ms .. 725.4 ms)
std dev 3.533 ms (1.527 ms .. 4.427 ms)
variance introduced by outliers: 19% (moderately inflated)
benchmarking run 1000 game ticks/movers/30
time 1.053 s (1.014 s .. 1.114 s)
1.000 R² (0.999 R² .. 1.000 R²)
mean 1.067 s (1.059 s .. 1.081 s)
std dev 13.55 ms (1.042 ms .. 17.01 ms)
variance introduced by outliers: 19% (moderately inflated)
benchmarking run 1000 game ticks/movers/40
time 1.392 s (1.333 s .. 1.421 s)
1.000 R² (1.000 R² .. 1.000 R²)
mean 1.439 s (1.417 s .. 1.481 s)
std dev 42.42 ms (222.2 μs .. 49.25 ms)
variance introduced by outliers: 19% (moderately inflated)
```
Extension of #1484 that partially mitigates #1558.
Let's not bother to track the activity levels of system robots when we're not in creative mode, because they won't even show up in the `F2` dialog. This mitigation can be substantial, as we generally expect there to be many more system robots than player robots.
It doesn't have quite the same "warm up" drawback as the potential mitigation described in https://github.com/swarm-game/swarm/issues/1558#issuecomment-1741794123, because player robots will always be warmed up, and system robots will have been warmed up if we've been playing in creative mode. We wouldn't necessarily expect frequent toggling in-and-out of creative mode while trying to observe performance in the `F2` dialog.
* 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
Toward #1547.
I decided to simply fix the error in this PR. At #1547 there is discussion of various other means to prevent this kind of error in the future (both for developers and players), but I will leave implementing some of those ideas to a future PR.
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