# Walking

Source: https://zafnok.github.io/shuyi-engine/learn/tutorial/03-walk/

By the end of this step, the arrow keys walk the player around the room, and walls stop
them.

![The player has walked one tile up from where the level started them.](https://zafnok.github.io/shuyi-engine/tutorial-tries/walked/before.png)

It sounds like a small thing, but this is the step where you learn how a Shuyi game's
rules are built, and everything later rests on it.

| File | What changes |
| ---- | ------------ |
| `crates/game/src/rules.rs` | What the player can ask for, and the rules that answer |
| `crates/game/src/board.rs` | Turns actions into commands |
| `crates/game/files/en.ron` | The help line mentions walking |
| `crates/game/tests/board.rs` | A test of walking |

## The big idea: commands

The obvious way to write a walk is for the screen to do it: when Up is pressed, subtract
one from the player's `y`. Many games start that way, and it works, until you want undo, or a
replay, or a bot, or a test that doesn't need a screen, or online play. Each of those then has
to change the game from outside, or copy what the screen does.

So Shuyi games are built another way.

> **Game idea, and a rule of Shuyi:** the state changes in **only one way**: by a **command**.
> A command is plain data saying what the player is asking for: "take one step right". The rules
> take a command and either change the state, or refuse and change nothing. The same state and
> the same command always give the same result, on every computer.
>
> A screen hears actions, sends commands, and shows the state. It never changes the state itself.

The whole chain looks like this, and it's worth learning by heart. A key press becomes an
**action** (the keymap), an action becomes a **command** (the screen), and a command changes the
**state** (the rules), which the screen then draws.

## A command

In `crates/game/src/rules.rs`, say what the player can ask for, what can happen, and why a
command can be refused. Add this just after `Puzzle`:

```rust
/// What the player asks for.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Command {
    /// Take one step that way.
    Step(Dir),
}

/// What happened.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub enum Event {
    /// The player walked to this tile.
    Walked(Pos),
}

/// Why a command was refused.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Refusal {
    /// A wall is that way.
    Wall,
    /// A box is that way, and nothing can push it yet.
    Box,
}

impl fmt::Display for Refusal {
    // check-text: not player text, what a test or a tool prints
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(match self {
            Refusal::Wall => "a wall is that way",
            Refusal::Box => "a box is that way",
        })
    }
}
```

> **Rust:** `Command::Step(Dir)` is an enum possibility carrying data: a step, and which way.
> `Dir` is the engine's enum of Up, Down, Left and Right. Having an `Event` and a `Refusal` of
> your own means the rules say exactly what happened, and exactly why not, and each of these
> can be checked by a test.
>
> `impl fmt::Display for Refusal` is the standard trait for "how this looks as text", needed
> because the engine may print a refusal in a message.

## The rules

Now the rules themselves. `TurnRules` is the engine's trait for a game that moves in turns.
Add this at the end of the file:

```rust
impl TurnRules for Puzzle {
    type Command = Command;
    type Event = Event;
    type Error = Refusal;
    /// Nothing ends a level yet.
    type Outcome = ();

    fn check(&self, command: &Command) -> Result<(), Refusal> {
        let Command::Step(dir) = *command;
        let to = self.player.step(dir);
        if self.is_wall(to) {
            return Err(Refusal::Wall);
        }
        if self.boxes.contains(&to) {
            return Err(Refusal::Box);
        }
        Ok(())
    }

    fn apply(&mut self, command: &Command) -> Result<Vec<Event>, Refusal> {
        // All or nothing: a refused command changes nothing.
        self.check(command)?;
        let Command::Step(dir) = *command;
        let to = self.player.step(dir);
        self.player = to;
        Ok(vec![Event::Walked(to)])
    }

    fn legal_commands(&self) -> Vec<Command> {
        Dir::ALL
            .into_iter()
            .map(Command::Step)
            .filter(|command| self.check(command).is_ok())
            .collect()
    }

    fn outcome(&self) -> Option<()> {
        None
    }
}
```

It's short, and there's a lot to see in it.

The `type` lines fill in the blanks of the trait: this game's command, event and error are the
ones you just wrote. `Outcome` is how a game ends. For now, nothing ends it, so it's `()`.

**`check`** answers "would this command be allowed?", without changing anything. It finds where
the step leads, and refuses if there's a wall or a box there. `self.player.step(dir)` is the engine's
way of saying "one tile that way from here". `Ok(())` means "allowed, and there's nothing else to
say".

**`apply`** does it. It calls `check` first, and that `?` is doing a job here: if `check` refuses,
`apply` returns the refusal at once, before anything has changed. This is what's meant by **all or
nothing**: a refused command changes nothing, not even part of the state. Rust helps you here, as
`&mut self` says this one may change the puzzle, and `check`'s `&self` says that one may not.

**`legal_commands`** lists everything allowed right now. Bots will use it. It tries all four
directions, and keeps those that `check` accepts.

> **Rust:** `Dir::ALL.into_iter().map(Command::Step).filter(...).collect()` is an **iterator chain**:
> take each direction, turn each into a `Command::Step`, keep those that pass, and gather the result
> into a list. You could write that as a `for` loop too, and it would be just as good. You'll see both.
>
> `let Command::Step(dir) = *command;` opens up the command to get its direction. The `*` means
> "the value this reference points to", as `command` is a `&Command`. This form of `let` only works
> since `Step` is the only possibility so far: when an enum has more, you need a `match`.

`check` needs one small helper, which goes in `impl Puzzle`:

```rust
    /// Whether `pos` is a wall, or off the map.
    fn is_wall(&self, pos: Pos) -> bool {
        self.tiles.get(pos).is_none_or(|&tile| tile == WALL)
    }
```

`self.tiles.get(pos)` gives an `Option`, since `pos` might be off the map, and `is_none_or` says yes
for a `None`, or if the test is true of what's inside. So stepping off the map counts as a wall.

At the top of the file, you'll need these too. The compiler will list anything you missed:

```rust
use core::fmt;
use std::collections::BTreeSet;

use serde::{Deserialize, Serialize};
use shuyi::core::{Dir, Grid, Pos, TerrainId, TurnRules};

use crate::level::{Feature, Level, TARGET, WALL};
```

## What rules are for

To see how the rules decide things, turn one off. This makes the wall check never fire:

In `crates/game/src/rules.rs`:

```diff
- if self.is_wall(to) {
+ if false && self.is_wall(to) {
```

![After pressing Up twice, the player has stopped under the wall.](https://zafnok.github.io/shuyi-engine/tutorial-tries/no-walls/before.png)

![After pressing Up twice, the player stands inside the top wall.](https://zafnok.github.io/shuyi-engine/tutorial-tries/no-walls/after.png)

The player walks right into the wall. Nothing else had to change: no screen, no drawing. One
line of the rules, and the whole game follows it. Now put it back.

## From actions to commands

The board's job is to listen for actions, and send commands. In `crates/game/src/board.rs`, first
add a way to send one, in `impl Board`:

```rust
    /// Sends `command` to the rules. A refused one changes nothing.
    fn send(&mut self, command: Command) {
        let _ = self.puzzle.apply(&command);
    }
```

Why `let _ =`? `apply` returns a `Result`, and Rust warns if you ignore one, since it may hold a
problem. Walking into a wall isn't a problem here, as it just does nothing. `let _ =` says you've
thought about it.

Then tell the engine which actions the board listens to now, replacing the old `actions`:

```rust
    /// The four steps, and Cancel to go back.
    fn actions(&self) -> Option<Vec<UiAction>> {
        Some(vec![
            UiAction::Up,
            UiAction::Down,
            UiAction::Left,
            UiAction::Right,
            UiAction::Cancel,
        ])
    }
```

and turn each action into a command, replacing the old `update`:

```rust
    fn update(&mut self, _: &mut Ctx<Data>, input: &FrameInput<UiAction>) -> Transition<Data> {
        for &action in &input.actions {
            match action {
                UiAction::Up => self.send(Command::Step(Dir::Up)),
                UiAction::Down => self.send(Command::Step(Dir::Down)),
                UiAction::Left => self.send(Command::Step(Dir::Left)),
                UiAction::Right => self.send(Command::Step(Dir::Right)),
                UiAction::Cancel => return Transition::Pop,
                _ => {}
            }
        }
        Transition::None
    }
```

> **Rust:** `for &action in &input.actions` runs once for each action this frame. In a `match`,
> `_ => {}` means "anything else: do nothing". `_` is a catch-all, and `{}` is an empty block.
> Without that arm the compiler would refuse, because `UiAction` has a `Confirm` this `match`
> doesn't mention.

The board doesn't read a key anywhere. It reacts to `UiAction`'s moves, and the keys stay in
`files/keymap.ron`, where a player can change them. In that file, `Up` has two keys, `Up` and `w`,
so `w` already walks too. You got that for free.

Change the mapping, and the game does what you say, not what the key's name says:

In `crates/game/src/board.rs`:

```diff
- UiAction::Up => self.send(Command::Step(Dir::Up)),
+ UiAction::Up => self.send(Command::Step(Dir::Down)),
```

![After pressing Up, the player is one tile higher.](https://zafnok.github.io/shuyi-engine/tutorial-tries/up-is-down/before.png)

![After pressing Up, the player is one tile lower.](https://zafnok.github.io/shuyi-engine/tutorial-tries/up-is-down/after.png)

That's the one place that says what Up means. (Put it back.)

Now say so in the help line, in `files/en.ron`:

```ron
    // The board.
    "board.help": "{Cursor} walk · {Cancel} back",
```

`{Cursor}` is a special name for the four direction keys together, and shows as `arrows`.

Run it, and walk into every wall. You'll find you can't walk through the box, either. That's the
`Refusal::Box` you wrote. Step 4 fixes that.

## A test

Add to `crates/game/tests/board.rs`:

```rust
#[test]
fn the_player_walks_and_a_wall_stops_them() {
    let mut h = on_board();
    assert_eq!(board(&h).puzzle.player(), Pos::new(2, 2));
    h.keys("Up");
    assert_eq!(board(&h).puzzle.player(), Pos::new(2, 1));
    h.keys("Up");
    assert_eq!(board(&h).puzzle.player(), Pos::new(2, 1));
}
```

The player starts at `(2, 2)`. Up takes them to `(2, 1)`. Pressing Up again runs into the wall, so
they don't move. This test presses real keys through the whole game, and reads the state. It needs
`use shuyi::core::Pos;` with the others at the top.

Since the rules are their own thing, they can be tested with no screen too. Have a look at how
[chapter 6](https://zafnok.github.io/shuyi-engine/learn/book/rules-as-commands/index.md#turnrules) does that, if you're curious.

## What you learned

- **Rust:** iterator chains, `*`, `_`, and traits with `type` blanks in them.
- **Game idea:** commands. The state changes one way only, and every rule is all or nothing.
- **Shuyi:** `TurnRules` (`check`, `apply`, `legal_commands`, `outcome`), and the chain from
  key to action to command to state.

[Chapter 3](https://zafnok.github.io/shuyi-engine/learn/book/actions/index.md) explains actions in more detail, and
[chapter 6](https://zafnok.github.io/shuyi-engine/learn/book/rules-as-commands/index.md) the rules. Next, it becomes a game.
