Commit Graph

1000 Commits

Author SHA1 Message Date
Karl Ostmo
adcb2c75fc
traffic vignette (#1334)
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)
2023-06-13 04:43:56 +00:00
Karl Ostmo
06db9e8677
structure templates (#1332)
Closes #1138.

Supports all of:
* Nesting
* Transparency
* Flip
* Rotate

![image](https://github.com/swarm-game/swarm/assets/261693/4b175ea5-9081-496c-9161-58876849faa2)

![image](https://github.com/swarm-game/swarm/assets/261693/1f7358eb-c75d-492b-8e54-7492685cebdb)

![image](https://github.com/swarm-game/swarm/assets/261693/4481597f-c531-428c-a310-633e711e84d4)


## Demo

    scripts/play.sh --scenario scenarios/Testing/1138-structures/nested-structure.yaml
    scripts/play.sh --scenario scenarios/Testing/1138-structures/flip-and-rotate.yaml
    scripts/play.sh --scenario data/scenarios/Testing/1138-structures/sibling-precedence.yaml
2023-06-12 18:11:35 +00:00
Brent Yorgey
a85318e32d
Better type error messages when there are unification variables involved (#1318)
Say things like "expecting `xyz` to be a function" instead of "expecting `xyz` to have type `u3 -> u4`".  Closes #1313.
2023-06-11 02:59:47 +00:00
Karl Ostmo
94588efc8d
wrap TickNumber in newtype (#1330)
newtypes > aliases
2023-06-10 18:45:58 +00:00
Brent Yorgey
001ac195ae
More typechecking error message improvements (#1308)
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.
2023-06-10 01:22:50 +00:00
Brent Yorgey
a83aa99eb4
Don't step robots that are waiting, even during instant (#1322)
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.
2023-06-10 00:18:47 +00:00
Karl Ostmo
987ddd6c04
world editor prototype (#873)
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.
2023-06-09 18:14:41 +00:00
Brent Yorgey
6691300f5f
Make launch options message take up a whole line, even when blank (#1315)
This way the new game menu doesn't wiggle up and down depending on whether the launch options message is displayed.
2023-06-09 17:20:44 +00:00
Karl Ostmo
b382494f7b
Scenario launch options selection (#1010)
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.
2023-06-09 06:38:51 +00:00
Karl Ostmo
873ee19cf4
peg fourmolu version (#1311)
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
2023-06-09 06:18:38 +00:00
Karl Ostmo
f39bd1bc37
ensure progress gets saved (#1305)
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 .
2023-06-05 01:18:42 +00:00
Karl Ostmo
bdea72f6aa
Cleanup 'use' command code (#1299)
From review feedback in #1287
2023-06-04 16:12:24 +00:00
Brent Yorgey
abe0ae88a6
Better type error messages (#1298)
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.
2023-06-03 12:22:06 +00:00
Brent Yorgey
4509a3b378
Refactor to factor out type error SrcLoc (#1294)
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.
2023-06-01 19:59:51 +00:00
Karl Ostmo
67871a59d2
Density command (#1296)
Closes #1295
2023-06-01 16:28:10 +00:00
Karl Ostmo
4bcf1b9317
'use' command (#1287)
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
2023-05-30 17:47:14 +00:00
Karl Ostmo
c1a1a67480
Extract drilling logic (#1288)
Just a simple code relocation with no functional changes.

Towards #1007.
2023-05-29 16:02:02 +00:00
Brent Yorgey
b2747a6fce
Rework type checker using "propagate types inward" trick (#1283)
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.
2023-05-28 02:28:15 +00:00
Brent Yorgey
a2cd731c83
Recreate GameState from scratch when starting a scenario (#1277)
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).
2023-05-25 11:24:05 +00:00
Karl Ostmo
2633403a97
lights out (#1273)
See https://en.wikipedia.org/wiki/Lights_Out_(game)

![image](https://github.com/swarm-game/swarm/assets/261693/e61cf515-f3c6-4d06-ab4f-01b79261a64a)

The seed is not fixed; random valid games are generated and can be solved automatically via the provided solution.

One can use the solution strategy described [here](https://en.wikipedia.org/wiki/Lights_Out_(game)#Light_chasing).
2023-05-22 17:54:50 +00:00
Karl Ostmo
07340ad10f
use isPrivilegedBot more (#1275) 2023-05-21 00:08:09 +00:00
Karl Ostmo
60441451cc
Allow zero-tick recipes to apply immediately (#1272)
# 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.
2023-05-20 20:02:29 +00:00
Karl Ostmo
39ae4ae40d
sokoban levels (#1269)
Three simple (demonstration) levels plus one serious challenge, `foresight.yaml`.

![image](https://github.com/swarm-game/swarm/assets/261693/f1f298e1-13ee-4f8d-b153-8c50f5d53ba8)
2023-05-19 19:28:17 +00:00
Brent Yorgey
2487737d1d
halt command (#1256)
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.
2023-05-19 13:06:20 +00:00
Brent Yorgey
8ada24de48
Abstract out common lens generation code (#1266)
Just some simple housekeeping, abstracting out some common lens generation code to reduce the amount of repeated boilerplate.
2023-05-17 18:57:55 +00:00
Brent Yorgey
4ec9770d6f
mergify: forgo 'Enforce issue references' for non-.hs (#1265)
Closes #1264.
2023-05-16 13:12:24 -05:00
Karl Ostmo
0e286c0133
Pig capturing scenario (#1258)
scripts/play.sh --scenario data/scenarios/Challenges/capture.yaml

![Screenshot from 2023-05-13 15-17-31](https://github.com/swarm-game/swarm/assets/261693/f849a946-f51a-4f49-abd6-c2b59448c3db)
2023-05-15 22:34:58 +00:00
Karl Ostmo
167a797d0f
canonical devices for push and watch (#1263) 2023-05-15 14:29:02 -07:00
Karl Ostmo
e040264070
decouple relative and absolute time capabilities (#1261)
`time` now requires `CTimeabs`, and `wait` now requires `CTimerel`.
Introduced a new `hourglass` device to provide `CTimerel` capability.

The `clock` device remains backward compatible, providing both relative and absolute time capabilities.
2023-05-15 19:27:31 +00:00
ussgarci
ea0f9135fc
Make panes collapsible (#1076)
Add Ctrl-s shortcut to collapse/expand REPL panel

closes #813
2023-05-15 18:26:33 +00:00
Brent Yorgey
4384e1b747
Better type variable names (#1252)
Uses type variable names like `a`, `b`, `c`, ... instead of using `aNNN` with numbers based on the internal numbers used for fresh type variables. Closes #1050.
2023-05-14 22:26:24 +00:00
Karl Ostmo
03a5f14694
CLI option to set initial speed (#1255)
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
2023-05-14 04:00:14 +00:00
Karl Ostmo
a6abebda52
Display higher clock resolution at lower speeds (#1253)
Useful for understanding what is happening in each tick.
2023-05-13 19:03:14 +00:00
Brent Yorgey
07fc47e619
Inventory search/filter mode (#1250)
When the inventory panel is focused, hit `/` to enter search mode (maybe "search" is a bit of a misnomer, it's really more of a "filter" mode).  Then type stuff to modify the current search term, shown at the bottom of the inventory panel.  You can still navigate the filtered inventory list with arrow keys etc. Hit Esc to exit search mode or Enter to pop out the focused item (and also exit search mode).

Closes #126.
2023-05-12 10:02:08 +00:00
Brent Yorgey
305867a58c
add new give tutorial (#1249)
Fixes #1086.
2023-05-12 09:45:35 +00:00
Karl Ostmo
271b485254
Do not show static when no robots exist in creative mode (#1248)
"Stub" maps generated by the World Editor (#873) do not define any robots.

It is convenient to view these via:

    scripts/play.sh --scenario stub.yaml --cheat

However, the static needs to be suppressed in creative mode when robots do not exist.
2023-05-07 23:16:13 +00:00
Karl Ostmo
f4776e25c6
reduce lag in monitor bot for Maypole scenario (#1244) 2023-05-05 12:32:53 -07:00
Karl Ostmo
6555a41274
Fix scenario menu update after quitting to menu (#1243)
Ensures that the "played" status and the "best scores" get updated in the scenario menu after "Quit to menu" is selected from within a scenario.

This bug appeared after merging #974.
2023-05-03 18:42:50 +00:00
Brent Yorgey
f2ae7dd6da
finer texture for static (#1241)
Closes #1145.

Instead of using simple black or white cells to display static, use cells with any one of the 16 possible arrangements of on/off quarter-blocks.  This makes static appear at almost 2x resolution as before (though there is still a bit of blockiness since the quarter-blocks can't occur independently). See https://asciinema.org/a/582095 for a demonstration.
2023-05-03 18:15:41 +00:00
Karl Ostmo
0e63c7afc1
Record best code size (#974)
towards #866

NOTE: #1116 should be merged first so that the schema change of save files is less disruptive.

## Examples

Different criteria can have their own best score:
![image](https://user-images.githubusercontent.com/261693/219904496-fcd23ca0-b208-43e1-afc6-188acfe327cf.png)

All criteria share the same single best score:
![image](https://user-images.githubusercontent.com/261693/219904553-abe3011c-41b0-469c-b34d-95d84b91697a.png)

## Behavior notes

* As currently designed, the code size will only be scored if the the player has specified their code **before** the scenario begins.  Furthermore, any input into the REPL will invalidate code size scoring for the current game.
* Because of this, the only way to score code so far is with a command-line argument of `--run` or `--autoplay`.  However, #1010  shall implement code size scoring when a scenario is launched from the UI.
* In the "best scores" display, if multiple "best score" criteria were all from the same game, they will be consolidated.  If all criteria are for the same game, the criteria labels will be omitted.
* The code size metrics will not be displayed if a "best score" was not obtained via `--run`.

## Caveats

### `run` command

Currently, the code entailed in a `run "somescript.sw"` command is not transitively included, so using `run` make the code size score meaningless.

## Testing

### Unit tests

Run the subset of unit tests:

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

### Manual integration tests

First, reset the score:

    rm -f ~/.local/share/swarm/saves/Tutorials_grab.yaml

Saving the following to `grab-soln.sw`:
```
move;
move; grab; turn back; move; turn back; move;
move; grab; turn back; move; turn back; move;
move; grab; turn back; move; turn back; move;
move; grab; turn back; move; turn back; move;
move; grab; turn back; move; turn back; move;
move; grab;
```
Run as follows:

    scripts/play.sh --scenario Tutorials/grab.yaml --run grab-soln.sw

This should establish a record for code size.

Then, play the Grab tutorial and immediately paste and run this in the REPL:

    move; move; grab; move; grab; move; grab; move; grab; move; grab; move; grab;

This solution is faster in terms of time, but should not displace the code-length record, since no code length should be recorded from a REPL solution.
2023-05-02 07:06:01 +00:00
Karl Ostmo
8d2b523658
place hanoi files in subdirectory (#1236) 2023-04-29 14:49:47 -07:00
Karl Ostmo
da7190a0b4
Implement push command (#1235)
Closes #1234

A `"dozer blade"` is defined locally to provide `push` capability.  To decouple this PR from bikeshedding, will defer the definition of a global entity that offers `push` capability to another PR.
2023-04-29 20:57:55 +00:00
Karl Ostmo
2dddea136e
unrestricted variant of atomic (#1231)
Closes #1227.

Demo:

    scripts/play.sh --scenario data/scenarios/Challenges/word-search.yaml --autoplay
2023-04-29 16:23:51 +00:00
Karl Ostmo
5fd852461e
Immersive multimedia experience (#1233)
Emits a terminal beep upon winning or losing a scenario.
2023-04-29 15:52:36 +00:00
Brent Yorgey
e2b4eaa1da
mention unit and void types in ADT calculator description (#1226)
Closes #753.
2023-04-27 07:05:24 -05:00
Karl Ostmo
eb90bfbe9b
web api to parse, render, and run code (#1142)
Closes #625.

# Demo

## Running code

Start a creative game:

    scripts/play.sh --scenario data/scenarios/creative.yaml

In another terminal window:

    curl -H "Content-Type: text/plain; charset=utf-8" -i http://localhost:5357/code/run --data "move;"

Observe that the robot has moved, and that the command sent via `curl` has been added to the REPL history.

## Parsing and rendering code

Start swarm:

    scripts/play.sh

Run curl:

    curl --silent -H "Content-Type: text/plain; charset=utf-8"  -i http://localhost:5357/code/render --data @data/test/language-snippets/warnings/unused-vars/multiple-lambda-first-unused.sw

See output:
```
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Date: Mon, 06 Mar 2023 10:16:12 GMT
Server: Warp/3.3.23
Content-Type: text/plain;charset=utf-8

def put = \x. \y. place y end
|
`- \x. \y. place y
   |
   `- \y. place y
      |
      `- place y
         |
         +- place
         |
         `- y
```
2023-04-25 21:15:40 +00:00
Karl Ostmo
ffafb8bfa6
implement stride command (#1219)
Closes #1218

    scripts/play.sh --scenario data/scenarios/Testing/1218-stride-command.yaml --autoplay

I decided to implement this command with the same exception behaviors as the `move` command.
If any obstructions exist on the path, then an exception is thrown and no movement is made.

The implications of this are:
1. With `try`, one can learn in `O(1)` time whether any obstructions exist along a line of up to 64 units long
2. One can learn in `O(log N)` time the exact distance of the nearest obstruction along that line.
2023-04-25 17:35:33 +00:00
Brent Yorgey
599225f4d6
Key input handler (#1214)
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.
2023-04-25 16:39:59 +00:00
Karl Ostmo
11165df7c0
More efficient gopher solution using 'watch' (#1216)
Tested using:

    scripts/run-tests.sh --test-arguments '--pattern "gopher"'

| Before | After |
| --- | --- |
| 6.28s | 1.33s |
2023-04-25 16:15:57 +00:00
Brent Yorgey
7df691688f
Immediately flag world for redraw when scanning/uploading (#1225)
Executing `scan` or `upload` may change the way the world is drawn, so we need to flag a redraw.  Fixes #758.

I also included a few updated/improved comments I made while trying to understand various bits of code (though they were ultimately unrelated to this issue).
2023-04-25 15:25:41 +00:00