diff --git a/README.md b/README.md
index e1161de7..e9a564fc 100644
--- a/README.md
+++ b/README.md
@@ -10,31 +10,31 @@ robots to explore the world and collect resources, which in turn
allows you to build upgraded robots that can run more interesting and
complex programs.
-![](images/log.png)
+![World 0 after scanning a tree and making a log.](images/tutorial/log.png)
-The implementation is still in an early stage, but planned features
-include:
+The implementation is still in an early stage, but these are some of the (planned) features:
* Practically infinite 2D procedurally generated worlds
* Simple yet powerful programming language based on the polymorphic
lambda calculus + recursion, with a command monad for describing
first-class imperative actions
-* In-game tutorial
+* Editor support with LSP and highlighting
+* (**TBD**) In-game tutorial
* Multiple game modes:
- In Classic mode, you start with the ability to produce only very
basic, limited robots; collecting resources allows you to
bootstrap your way into programming more sophisticated robots
that can explore more of the world, collect more resources, etc.
- - Hardcore mode is like Classic mode, but you start with only a
- limited number of robots. If they get stuck or you run out of
- resources, it's game over!
- - Sandbox mode places no restrictions: program robots to your
+ - Creatrive mode places no restrictions: program robots to your
heart's content using whatever language features you want,
without worrying about collecting resources.
- - In Challenge mode, you attempt to program robots in order to
- solve pre-designed puzzles or challenges.
- - Future versions might also have multiplayer modes, with co-op or
- PvP play over a network...?
+ - (**TBD**) Hardcore mode like Classic mode, but you start
+ with only a limited number of robots. If they get stuck or
+ you run out of resources, it's game over!
+ - (**TBD**) In Challenge mode, where you attempt to program robots
+ in order to solve pre-designed puzzles or challenges.
+ - (**TBD**) Future versions might also have multiplayer modes,
+ with co-op or PvP play over a network...?
Installing and Playing
======================
diff --git a/TUTORIAL.md b/TUTORIAL.md
index 57682f86..05463bae 100644
--- a/TUTORIAL.md
+++ b/TUTORIAL.md
@@ -44,28 +44,32 @@ Getting started
When you first start up Swarm, you should be greeted by a screen that
looks something like this:
-![](images/initial.png)
+![World with seed 0](images/tutorial/world0.png)
In the world view, you see the default [*World 0*](./TUTORIAL.md#world-generation)
-and the little white `Ω` in the middle represents your base. Start by
-using the Tab key to cycle through the four panels (the REPL, the
-info panel, the inventory panel, and the world panel), and read about the various devices
-installed on your base. There is a lot to take in at first so feel
-free to just skim; this tutorial will cover the use of your devices in
-more detail.
+and the little white `Ω` in the middle represents your base. You will
+need to [explore](./TUTORIAL.md#exploring) to find out what all the `?` are.
+
+Start by using the Tab key to cycle through the four panels
+(the REPL, the info panel, the inventory panel, and the world panel),
+and read about the various devices installed on your base.
+
+There is a lot to take in at first so feel free to just skim; this tutorial
+will cover the use of your devices in more detail.
Building your first robot
-------------------------
Pretty much the only thing you can do is build robots. Let's build
-one! Tab back to the REPL (or hit the `Meta-r` shortcut) and type
+one! Tab back to the REPL (or hit the Meta+R
+shortcut) and type
```
-build "hello" {move; move; move; move}
+build "hello" {turn north; move}
```
then hit Enter. You should see a robot appear and travel to the
-east four steps before stopping. It should look something like this:
+north one step before stopping. It should look something like this:
-![](images/firstrobot.png)
+![Hello robot!](images/tutorial/hello.png)
You can also see that on the next line after your input, the REPL printed out
```
@@ -77,7 +81,7 @@ it may be different than the name you specified if there is already
another robot with that name.
You can see that a semicolon is used to chain together commands, that
-is, if `c1` and `c2` are both commands, then `c1 ; c2` is the command
+is, if `c1` and `c2` are both commands, then `c1; c2` is the command
which executes first `c1` and then `c2`. The curly braces around the
second argument to `build` are required; curly braces create an
expression whose evaluation is *delayed* until later. In this case,
@@ -96,7 +100,7 @@ expression currently typed at the prompt parses and type checks, the
REPL will show you the type of the expression in the upper right, like
this:
-![](images/build-type.png)
+![Build type](images/tutorial/build.png)
It will tell you that the type of `build` is
```
@@ -121,9 +125,9 @@ parse error or a type error). If you want to see what the error is,
just hit `Enter`: a dialog box will pop up with a (somewhat) more
informative error message.
-![](images/type-error.png)
+![Hi error](images/tutorial/hi.png)
-To get rid of the error dialog, just hit the `Esc` key.
+To get rid of the error dialog, just hit the Esc key.
Something you can't do yet
--------------------------
@@ -148,39 +152,40 @@ another robot. You'll have to find a way to make more.)
Creating definitions
--------------------
-We can already tell it's going to be tedious typing
-`move;move;move;move;...`. Since your base has a `dictionary`
-installed, let's create some definitions to make
+We can already tell it's going to be tedious getting the robots
+anywhere with `move;move;move;move;...`. Since your base has
+a `dictionary` installed, let's create some definitions to make
our life a bit easier. To start, type the following:
```
-def m2 : cmd () = move ; move end
+def m : cmd () = move end
```
-The `: cmd ()` annotation on `m2` is optional; in this situation the
-game could have easily figured out the type of `m2` if we had just
-written `def m2 = ...` (though there are some situations where a type
+The `: cmd ()` annotation on `m` is optional; in this situation the
+game could have easily figured out the type of `m` if we had just
+written `def m = ...` (though there are some situations where a type
signature may be required). The `end` is required, and is needed to
disambiguate where the end of the definition is.
Now try this:
```
-def m4 = m2;m2 end; def m8 = m4;m4 end
+def m2 = m; m end; def m4 = m2; m2 end; def m8 = m4; m4 end
```
-(The semicolon between consecutive `def` commands is actually
-optional, so you can put many `def` commands in a file without having
-to worry about putting a semicolon after every `end`.) Great, now we
-have commands that will execute `move` four and eight times,
-respectively. Finally, let's use them:
-```
-build "mover" {m8; m8; m2}
-```
-This should build a robot that moves eighteen steps to the east.
+The semicolon between `def` commands is optional, so you can put
+`def` commands in a file and not write a semicolon after every `end`.
+
-(You might wonder at this point if it is possible to create a function
+Great, now we have commands that will execute `move` multiple times.
+Now let's use them:
+```
+build "runner" { turn west; m4; m }
+```
+This should build a robot that moves to the green mass to the west.
+
+You might wonder at this point if it is possible to create a function
that takes a number as input and moves that many steps forward. It
most certainly is, but right now your robots would not be capable
-of executing it. You'll have to figure out how to upgrade them!)
+of executing it. You'll have to figure out how to upgrade them!
Getting the result of a command
-------------------------------
@@ -190,58 +195,60 @@ arrow, like so:
```
var <- command; ... more commands that can refer to var ...
```
-(Yes, this is just like Haskell's `do`-notation; and yes, `cmd` is a
-monad, similar to the `IO` monad in Haskell. But if that doesn't mean
-anything to you, don't worry about it!) Let's build one more
-robot called `"mover"`. It will get renamed to something else to avoid
-a name conflict, but we can capture its name in a variable using the
-above syntax. Then we can use the `view` command to focus on it
-instead of the base. Like so:
+Yes, it's like Haskell's `do`-notation; and yes, `cmd` is a
+monad, similar to the `IO` in Haskell. But if that doesn't mean
+anything to you, don't worry about it!
+
+Let's build one more robot called `"runner"`. It will get renamed
+to something else to avoid a name conflict, but we can capture its
+name in a variable using the above syntax.
+Then we can use the `view` command to focus on it instead of the base:
```
-name <- build "mover" {m8; m8; m4}; view name
+r <- build "runner" { turn west; m4; m }; view r
```
-Note that `base` executes the `view name` command as soon as it
+Note that `base` executes the `view r` command as soon as it
finishes executing the `build` command, which is about the same time
as the newly built robot *starts* executing its program. So we get to
watch the new robot as it goes about its business. Afterwards, the
view should look something like this:
-![](images/mover1.png)
+![View a robot](images/tutorial/viewr.png)
-The view is now centered on `mover1` instead of on our `base`, and the
-top-left panel shows `mover1`'s inventory and installed devices
+The view is now centered on `runner1` instead of on our `base`, and the
+top-left panel shows `runner1`'s inventory and installed devices
instead of `base`'s. (However, commands entered at the REPL will
still be executed by `base`.) To return to viewing `base` and its
inventory, you can type `view "base"` at the prompt, or focus the
-world panel (either using `Tab` or `Meta-w`) and hit `c`.
+world panel (either using Tab or Meta+W)
+and hit C.
Exploring
---------
So what is all this stuff everywhere? Let's find out! When you
`build` a robot, by default it starts out with a `scanner` device,
-which you may have noticed in `mover1`'s inventory. You can `scan`
+which you may have noticed in `runner1`'s inventory. You can `scan`
items in the world to learn about them, and later `upload` what you
have learned to the base.
-Let's build a robot to learn about those green `T` things to the west:
+Let's build a robot to learn about those green `?` things to the west:
```
build "s" {turn west; m4; move; scan west; turn back; m4; upload "base"}
```
-The `turn` command causes a robot to turn, of course. It takes a
-direction as an argument, which can be either an absolute direction
+The `turn` command we used to turn the robot takes a direction as an
+argument, which can be either an absolute direction
(`north`, `south`, `east`, or `west`) or a relative direction
(`forward`, `back`, `left`, `right`, or `down`).
-Notice that the robot did not actually need to walk on top of a `T` to
+Notice that the robot did not actually need to walk on top of a `?` to
learn about it, since it could `scan west` to scan the cell one unit
to the west (you can also `scan down` to scan an item directly underneath the
robot). Also, it was able to `upload` at a distance of one cell away from
the base.
-After this robot finishes, the UI should look like this:
+After this robot finishes, you should have a new entry in your inventory:
-![](images/scantree.png)
+![Scan a tree](images/tutorial/scantree.png)
Apparently those things are trees! Although you do not actually have
any trees yet, you can tab over to your inventory to read about them.
@@ -249,14 +256,12 @@ In the bottom left corner you will see a description of trees along
with some *recipes* involving trees. There is only one recipe,
showing that we can use a tree to construct two branches and a log.
-![](images/viewtree.png)
-
Getting some resources
----------------------
So those tree things look like they might be useful. Let's get one!
```
-build "fetch" {turn west; m8; thing <- grab; turn back; m8; give "base" thing}
+build "fetch" {turn west; m8; thing <- grab; turn back; m8; give "base" thing }
```
You can see that the
`grab` command returns the name of the thing it grabbed, which is
@@ -286,7 +291,7 @@ Note that since the `make` command takes a `string` as an argument,
You should now have two branches and a log in your inventory. Take a
look at them and see what recipes they enable!
-![](images/log.png)
+![Make a log](images/tutorial/log.png)
By this time you may also notice that the tree has grown back (whether
it has finished growing back depends on how long you took to read the
@@ -327,7 +332,7 @@ The world should now look something like the below. Notice that the
on `crasher`. Notice also that `crasher` only moved one unit south,
even though we told it to move two steps! What went wrong?
-![](images/crasher1.png)
+![Let's crash a robot!](images/tutorial/crasher.png)
One thing we could do at this point is to `view "crasher"`. However,
it will probably become a bit more difficult to use the `view` command in
@@ -336,7 +341,7 @@ or remember the name of the robot that crashed? Fortunately, there is
something else we can do: send out another robot to `salvage` the
crashed robot.
-The `salvage` command can be executed by any robot with a `grabber`,
+The `salvage` command can be executed by any robot with a `plasma cutter`,
which is one of the devices installed on new robots by default. It
takes no arguments, and simply looks for any idle robot in the same
cell; if it finds one, it disassembles the idle robot, transferring
@@ -354,14 +359,14 @@ the `upload` command, which we have seen before. In addition to
uploading knowledge about entities, it turns out that it also uploads
the log from a `logger`.
```
-build "fetch" {turn west; m8; thing <- grab; turn back; m8; give "base" thing}
+build "fetch" {turn west; m8; m; thing <- grab; turn back; m8; m; give "base" thing}
make "log"
make "logger"
build "salvager" {turn south; move; log "salvaging..."; salvage; turn back; move; upload "base"}
```
The world should now look something like this:
-![](images/salvaged.png)
+![Salvaging a crashed robot.](images/tutorial/salvaged.png)
As you can see, the base's log now contains some entries from
`crasher`! They were copied over to `salvager`'s log when it salvaged
@@ -406,7 +411,7 @@ lakes or in the middle of a plain. Either way, you have established
your base in the shade of what you assume is a tree and now can send
out robots to explore!
-![World generated with seed 16](./images/world16.png)
+![World generated with seed 16](images/tutorial/world16.png)
Creative Mode
-------------
@@ -416,7 +421,9 @@ Creative mode. In Classic mode, the kinds of actions your robots can
do, and the kinds of programs they can interpret, is restricted by
what devices they have installed. In Creative mode you can do
anything you like, including fabricate arbitrary items out of thin air
-using the `create` command. To switch, highlight the world view
-panel, then hit the `m` key.
+using the `create` command. Also it will not hide unknown entities,
+as you can see in the World 16 above.
+
+To switch, highlight the world view panel, then hit the M key.
Now go forth and build your swarm!
diff --git a/images/build-type.png b/images/build-type.png
deleted file mode 100644
index c901797c..00000000
Binary files a/images/build-type.png and /dev/null differ
diff --git a/images/crasher1.png b/images/crasher1.png
deleted file mode 100644
index fefe9a85..00000000
Binary files a/images/crasher1.png and /dev/null differ
diff --git a/images/firstrobot.png b/images/firstrobot.png
deleted file mode 100644
index 732ef6c7..00000000
Binary files a/images/firstrobot.png and /dev/null differ
diff --git a/images/initial.png b/images/initial.png
index ebcbe656..0f04a310 100644
Binary files a/images/initial.png and b/images/initial.png differ
diff --git a/images/log.png b/images/log.png
deleted file mode 100644
index 04b12ca7..00000000
Binary files a/images/log.png and /dev/null differ
diff --git a/images/mover1.png b/images/mover1.png
deleted file mode 100644
index 32e08823..00000000
Binary files a/images/mover1.png and /dev/null differ
diff --git a/images/salvaged.png b/images/salvaged.png
deleted file mode 100644
index 85a460ab..00000000
Binary files a/images/salvaged.png and /dev/null differ
diff --git a/images/scantree.png b/images/scantree.png
deleted file mode 100644
index b189f311..00000000
Binary files a/images/scantree.png and /dev/null differ
diff --git a/images/tree.png b/images/tree.png
deleted file mode 100644
index 180bd21c..00000000
Binary files a/images/tree.png and /dev/null differ
diff --git a/images/tutorial/build.png b/images/tutorial/build.png
new file mode 100644
index 00000000..a366cc37
Binary files /dev/null and b/images/tutorial/build.png differ
diff --git a/images/tutorial/crasher.png b/images/tutorial/crasher.png
new file mode 100644
index 00000000..c7649d4b
Binary files /dev/null and b/images/tutorial/crasher.png differ
diff --git a/images/tutorial/hello.png b/images/tutorial/hello.png
new file mode 100644
index 00000000..ac45bb1e
Binary files /dev/null and b/images/tutorial/hello.png differ
diff --git a/images/tutorial/hi.png b/images/tutorial/hi.png
new file mode 100644
index 00000000..4490c376
Binary files /dev/null and b/images/tutorial/hi.png differ
diff --git a/images/tutorial/log.png b/images/tutorial/log.png
new file mode 100644
index 00000000..5ee980cd
Binary files /dev/null and b/images/tutorial/log.png differ
diff --git a/images/tutorial/salvaged.png b/images/tutorial/salvaged.png
new file mode 100644
index 00000000..8a508ebe
Binary files /dev/null and b/images/tutorial/salvaged.png differ
diff --git a/images/tutorial/scantree.png b/images/tutorial/scantree.png
new file mode 100644
index 00000000..697e82be
Binary files /dev/null and b/images/tutorial/scantree.png differ
diff --git a/images/tutorial/viewr.png b/images/tutorial/viewr.png
new file mode 100644
index 00000000..6c5c5f76
Binary files /dev/null and b/images/tutorial/viewr.png differ
diff --git a/images/tutorial/world0.png b/images/tutorial/world0.png
new file mode 100644
index 00000000..1f21fcbe
Binary files /dev/null and b/images/tutorial/world0.png differ
diff --git a/images/tutorial/world16.png b/images/tutorial/world16.png
new file mode 100644
index 00000000..e077f455
Binary files /dev/null and b/images/tutorial/world16.png differ
diff --git a/images/type-error.png b/images/type-error.png
deleted file mode 100644
index f816dc32..00000000
Binary files a/images/type-error.png and /dev/null differ
diff --git a/images/viewtree.png b/images/viewtree.png
deleted file mode 100644
index 12d5f9af..00000000
Binary files a/images/viewtree.png and /dev/null differ
diff --git a/images/world16.png b/images/world16.png
deleted file mode 100644
index ba768ada..00000000
Binary files a/images/world16.png and /dev/null differ
diff --git a/src/Swarm/Game/Entity.hs b/src/Swarm/Game/Entity.hs
index 84d75543..08fc5607 100644
--- a/src/Swarm/Game/Entity.hs
+++ b/src/Swarm/Game/Entity.hs
@@ -70,6 +70,7 @@ module Swarm.Game.Entity (
lookupByName,
countByName,
contains,
+ contains0plus,
elems,
-- ** Modification
@@ -509,6 +510,10 @@ insertCount cnt e (Inventory cs byN) =
contains :: Inventory -> Entity -> Bool
contains inv e = lookup e inv > 0
+-- | Check whether an inventory has an entry for entity (used by robots).
+contains0plus :: Entity -> Inventory -> Bool
+contains0plus e = isJust . IM.lookup (e ^. entityHash) . counts
+
-- | Delete a single copy of a certain entity from an inventory.
delete :: Entity -> Inventory -> Inventory
delete = deleteCount 1
diff --git a/src/Swarm/Game/Robot.hs b/src/Swarm/Game/Robot.hs
index 7534b153..22ea6b42 100644
--- a/src/Swarm/Game/Robot.hs
+++ b/src/Swarm/Game/Robot.hs
@@ -51,6 +51,7 @@ module Swarm.Game.Robot (
baseRobot,
-- ** Query
+ robotKnows,
isActive,
waitingUntil,
getResult,
@@ -227,6 +228,10 @@ inventoryHash = to (\r -> 17 `hashWithSalt` (r ^. (robotEntity . entityHash)) `h
inventoryCapabilities :: Inventory -> Set Capability
inventoryCapabilities = setOf (to elems . traverse . _2 . entityCapabilities . traverse)
+-- | Does the robot know of the entity's existence.
+robotKnows :: Robot -> Entity -> Bool
+robotKnows r e = contains0plus e (r ^. robotInventory) || contains0plus e (r ^. installedDevices)
+
-- | Get the set of capabilities this robot possesses. This is only a
-- getter, not a lens, because it is automatically generated from
-- the 'installedDevices'. The only way to change a robot's
diff --git a/src/Swarm/TUI/View.hs b/src/Swarm/TUI/View.hs
index f6d447c8..9aea1dce 100644
--- a/src/Swarm/TUI/View.hs
+++ b/src/Swarm/TUI/View.hs
@@ -314,7 +314,11 @@ drawWorld g =
drawLoc :: W.Coords -> Widget Name
drawLoc coords =
- let (ePrio, eWidget) = drawCell coords (g ^. world)
+ let (ePrio, eWidget) = drawCell hiding (g ^. world) coords
+ hiding =
+ if g ^. gameMode == Creative
+ then HideNoEntity
+ else maybe HideAllEntities HideEntityUnknownTo $ focusedRobot g
in case M.lookup (W.coordsToLoc coords) robotsByLoc of
Just r
| ePrio > (r ^. robotDisplay . displayPriority) -> eWidget
@@ -323,11 +327,23 @@ drawWorld g =
str [lookupDisplay ((r ^. robotOrientation) >>= toDirection) (r ^. robotDisplay)]
Nothing -> eWidget
--- | Draw a single cell of the world.
-drawCell :: W.Coords -> W.World Int Entity -> (Int, Widget Name)
-drawCell i w = case W.lookupEntity i w of
- Just e -> (e ^. entityDisplay . displayPriority, displayEntity e)
+data HideEntity = HideAllEntities | HideNoEntity | HideEntityUnknownTo Robot
+
+-- | Draw a single cell of the world, either hiding entities that current robot does not know,
+-- or hiding all/none depending on Left value (True/False).
+drawCell :: HideEntity -> W.World Int Entity -> W.Coords -> (Int, Widget Name)
+drawCell edr w i = case W.lookupEntity i w of
Nothing -> (0, displayTerrain (toEnum (W.lookupTerrain i w)))
+ Just e ->
+ ( e ^. entityDisplay . displayPriority
+ , displayEntity (hide e)
+ )
+ where
+ known e = case edr of
+ HideAllEntities -> False
+ HideNoEntity -> True
+ HideEntityUnknownTo ro -> ro `robotKnows` e
+ hide e = (if known e then id else entityDisplay . defaultChar %~ const '?') e
------------------------------------------------------------
-- Robot inventory panel