#  `false` Is Not an Answer.



Our player earns gold now. After eight stages, they have ninety-six of it and nothing to spend it on.

That is not a reward. That is a high score.

So today we are going to sell them something: more damage per click, at a price that goes up every time they buy it.

The shopping is the easy part.

The interesting part is what happens when we try to tell the rest of the program whether the purchase worked.

Because the obvious answer, `true` or `false`, turns out to be not quite an answer at all.

```text
Bought click upgrade 1! Click damage: 7  Gold left: 2
```

---

## Our starting point

If you are only reading along, you can skip past this code to "The free upgrade nobody earned."

If you are building along, you need three structs, three `impl` blocks, nine small functions, and thirteen tests:

```rust
struct Player {
    bonus_damage: u32,
    gold: u32,
    xp: u32,
    level: u32,
}

struct Enemy {
    name: String,
    health: u32,
    gold_reward: u32,
    xp_reward: u32,
}

struct Game {
    stage: u32,
    player: Player,
    enemy: Enemy,
}

impl Player {
    fn new() -> Player {
        Player {
            bonus_damage: 2,
            gold: 0,
            xp: 0,
            level: 1,
        }
    }

    fn add_gold(&mut self, amount: u32) {
        self.gold = self.gold.saturating_add(amount);
    }

    fn add_xp(&mut self, amount: u32) {
        self.xp = self.xp.saturating_add(amount);

        while self.xp >= xp_to_next_level(self.level) {
            self.xp -= xp_to_next_level(self.level);
            self.level = self.level.saturating_add(1);
        }
    }

    fn base_damage(&self) -> u32 {
        base_damage_for_level(self.level)
    }
}

impl Enemy {
    fn for_stage(stage: u32) -> Enemy {
        Enemy {
            name: String::from("Training Slime"),
            health: enemy_health_for_stage(stage),
            gold_reward: gold_reward_for_stage(stage),
            xp_reward: xp_reward_for_stage(stage),
        }
    }
}

impl Game {
    fn new() -> Game {
        Game {
            stage: 1,
            player: Player::new(),
            enemy: Enemy::for_stage(1),
        }
    }

    fn click(&mut self) {
        attack(&self.player, &mut self.enemy);

        if is_defeated(self.enemy.health) {
            self.defeat_enemy();
        }
    }

    fn defeat_enemy(&mut self) {
        self.player.add_gold(self.enemy.gold_reward);
        self.player.add_xp(self.enemy.xp_reward);

        self.stage = self.stage.saturating_add(1);
        self.enemy = Enemy::for_stage(self.stage);
    }
}

fn click_damage(base: u32, bonus: u32) -> u32 {
    base.saturating_add(bonus)
}

fn health_after_hit(health: u32, damage: u32) -> u32 {
    health.saturating_sub(damage)
}

fn is_defeated(health: u32) -> bool {
    health == 0
}

fn enemy_health_for_stage(stage: u32) -> u32 {
    stage.saturating_mul(10).saturating_add(10)
}

fn gold_reward_for_stage(stage: u32) -> u32 {
    stage.saturating_mul(2).saturating_add(3)
}

fn xp_reward_for_stage(stage: u32) -> u32 {
    stage.saturating_mul(2).saturating_add(4)
}

fn xp_to_next_level(level: u32) -> u32 {
    level
        .saturating_sub(1)
        .saturating_mul(15)
        .saturating_add(20)
}

fn base_damage_for_level(level: u32) -> u32 {
    level.saturating_add(4)
}

fn attack(player: &Player, enemy: &mut Enemy) {
    let damage = click_damage(player.base_damage(), player.bonus_damage);

    enemy.health = health_after_hit(enemy.health, damage);
}

fn main() {
    let mut game = Game::new();

    while game.stage <= 8 {
        let stage = game.stage;
        let level = game.player.level;

        game.click();

        if game.stage > stage {
            println!(
                "Stage {stage} cleared!  Gold: {}  XP: {}/{}",
                game.player.gold,
                game.player.xp,
                xp_to_next_level(game.player.level)
            );

            if game.player.level > level {
                println!(
                    "Level up! Now level {}. Click damage: {}",
                    game.player.level,
                    click_damage(game.player.base_damage(), game.player.bonus_damage)
                );
            }

            println!(
                "  Next up: {} with {} health.",
                game.enemy.name, game.enemy.health
            );
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn click_damage_adds_base_and_bonus() {
        assert_eq!(click_damage(3, 4), 7);
    }

    #[test]
    fn health_never_drops_below_zero() {
        assert_eq!(health_after_hit(3, 10), 0);
    }

    #[test]
    fn zero_health_is_defeated() {
        assert!(is_defeated(0));
        assert!(!is_defeated(1));
    }

    #[test]
    fn attack_reduces_enemy_health() {
        let player = Player::new();

        let mut enemy = Enemy::for_stage(1);
        enemy.health = 10;

        attack(&player, &mut enemy);

        assert_eq!(enemy.health, 3);
    }

    #[test]
    fn later_stages_have_tougher_enemies() {
        assert_eq!(enemy_health_for_stage(1), 20);
        assert_eq!(enemy_health_for_stage(2), 30);
    }

    #[test]
    fn later_stages_pay_better() {
        assert_eq!(gold_reward_for_stage(1), 5);
        assert_eq!(gold_reward_for_stage(2), 7);
        assert_eq!(xp_reward_for_stage(1), 6);
        assert_eq!(xp_reward_for_stage(2), 8);
    }

    #[test]
    fn levels_need_more_xp_as_they_go() {
        assert_eq!(xp_to_next_level(1), 20);
        assert_eq!(xp_to_next_level(2), 35);
    }

    #[test]
    fn leveling_up_keeps_leftover_xp() {
        let mut player = Player::new();

        player.add_xp(25);

        assert_eq!(player.level, 2);
        assert_eq!(player.xp, 5);
    }

    #[test]
    fn a_big_reward_can_skip_levels() {
        let mut player = Player::new();

        player.add_xp(60);

        assert_eq!(player.level, 3);
        assert_eq!(player.xp, 5);
    }

    #[test]
    fn levels_make_the_player_hit_harder() {
        assert_eq!(base_damage_for_level(1), 5);
        assert_eq!(base_damage_for_level(2), 6);
    }

    #[test]
    fn a_new_game_starts_at_stage_one() {
        let game = Game::new();

        assert_eq!(game.stage, 1);
        assert_eq!(game.enemy.health, 20);
        assert_eq!(game.player.gold, 0);
    }

    #[test]
    fn clicking_damages_the_enemy() {
        let mut game = Game::new();

        game.click();

        assert_eq!(game.enemy.health, 13);
    }

    #[test]
    fn defeating_an_enemy_pays_the_player() {
        let mut game = Game::new();
        game.enemy.health = 1;

        game.click();

        assert_eq!(game.stage, 2);
        assert_eq!(game.enemy.health, 30);
        assert_eq!(game.player.gold, 5);
        assert_eq!(game.player.xp, 6);
    }
}
```

`Player` stores what the player has earned: gold, XP, and a level. Defeating an enemy pays gold and XP, and enough XP levels the player up.

Click damage comes from two places. `base_damage()` works it out from the player's level. `bonus_damage` is just a number stored in the struct.

Run `cargo test` and all thirteen should pass.

---

## The free upgrade nobody earned

Look at that `Player` again.

```rust
    bonus_damage: u32,
```

Every new player starts with 2 of it. For free. Since the first time we gave the player a second damage number.

`base_damage` is already a method, not a field. It is worked out from the player's level, because it is a consequence of that level, not a fact to store.

`bonus_damage` has the same problem, with a different cause.

Bonus damage should be a consequence of how many upgrades the player has bought.

So the player does not need to store the bonus. The player needs to store the shopping.

Two rules, under `base_damage_for_level()`:

```rust
fn click_upgrade_bonus(upgrades: u32) -> u32 {
    upgrades.saturating_mul(2)
}

fn click_upgrade_cost(upgrades: u32) -> u32 {
    upgrades.saturating_add(1).saturating_mul(10)
}
```

Each upgrade adds 2 damage:

```text
0 upgrades → 0 bonus
1 upgrade  → 2 bonus
2 upgrades → 4 bonus
```

And each one costs more than the last:

```text
first upgrade  → 10 gold
second upgrade → 20 gold
third upgrade  → 30 gold
```

Both rules take the number of upgrades already bought. Neither one knows a `Player` exists.

Now replace the field with a count of purchases:

```rust
struct Player {
    gold: u32,
    xp: u32,
    level: u32,
    click_upgrade_level: u32,
}
```

A new player has bought nothing:

```rust
            click_upgrade_level: 0,
```

And just like base damage, bonus damage becomes a method that asks the rule. Put it under `base_damage()`:

```rust
    fn bonus_damage(&self) -> u32 {
        click_upgrade_bonus(self.click_upgrade_level)
    }
```

`attack()` gets its second pair of parentheses:

```rust
    let damage = click_damage(player.base_damage(), player.bonus_damage());
```

Notice what `Player` is made of now.

```text
Player
├── gold
├── xp
├── level
└── click_upgrade_level
```

Not a single damage number.

Every field is a record of **progress**: what the player has earned, and what they have done with it.

The damage numbers are worked out from that progress, by rules, on demand.

That is going to matter a great deal in a few articles, when we save the game.

We will only have to save what the player earned. Everything else can be recalculated.

### The first fight changes

One honest side effect.

A new player used to hit for 7: 5 base plus 2 free bonus.

Now they hit for 5, because the bonus has to be bought.

The stage 1 slime still has 20 health, so it takes four clicks instead of three.

Two of our tests notice.

`clicking_damages_the_enemy` expected 13 health after one click. It is 15 now.

`attack_reduces_enemy_health` expected 3 health left from 10. It is 5.

Update both numbers and run `cargo test`.

Those tests were not broken. The game changed, and the tests were telling the truth about it.

---

## Buying the upgrade

Here is the first version of the purchase. Add it to `impl Player`, under `bonus_damage()`:

```rust
    fn buy_click_upgrade(&mut self) -> bool {
        let cost = click_upgrade_cost(self.click_upgrade_level);

        if self.gold < cost {
            return false;
        }

        self.gold -= cost;
        self.click_upgrade_level = self.click_upgrade_level.saturating_add(1);

        true
    }
```

Work out the price.

If the player cannot afford it, stop, and say so.

Otherwise, take the money, add the upgrade, and say so.

Two things to notice.

`return false;` leaves the method early.

Everything after it only runs if the player could afford the upgrade.

That lets the rest of the method read without an `else` wrapped around it.

And `self.gold -= cost;` is a plain minus sign, like the one in `add_xp()`.

The check right above it guarantees `self.gold` is at least `cost`, so the subtraction cannot go below zero.

Then `Game` passes the request along, inside `impl Game`:

```rust
    fn buy_click_upgrade(&mut self) -> bool {
        self.player.buy_click_upgrade()
    }
```

The game does not know the price of an upgrade.

It does not know what happens when you buy one.

It knows the player handles that.

Nothing calls either method yet, so Rust will warn about both. `main()` will fix that shortly.

---

## `true` is not an answer

This works.

Now imagine using it:

```rust
if game.buy_click_upgrade() {
    println!("Bought it!");
} else {
    println!("???");
}
```

What goes in the `else`?

`false` means the purchase did not happen.

It does not say *why*.

Today there is only one reason: not enough gold.

But look at this from the other side.

The code calling `buy_click_upgrade()` has to *remember* that `false` means "not enough gold."

Nothing in the code says so.

Add a second reason to fail someday, say a maximum upgrade level, and `false` starts meaning two different things.

Every place that checked it is now quietly wrong, and Rust will not say a word.

We want an answer that says what happened in plain words, and that Rust can check for us.

---

## Give the outcome a name

Rust has a type for "one of these specific things":

```rust
#[derive(Debug, PartialEq)]
enum PurchaseOutcome {
    Purchased,
    NotEnoughGold,
}
```

Put it with the structs. It is a type, like they are.

`enum` is short for *enumeration*: a type whose values are listed out, one by one.

A `PurchaseOutcome` is either `Purchased` or `NotEnoughGold`.

It cannot be anything else.

It cannot be both.

The two options are called **variants**, and you name one the same way you reach into a type for a function:

```rust
PurchaseOutcome::NotEnoughGold
```

You can think about `bool` the same way for this purpose: it also stands for one of exactly two possibilities, `true` or `false`.

The difference is that our enum lets us give those possibilities names that belong to our game.

### The line above the enum

```rust
#[derive(Debug, PartialEq)]
```

`#[derive(...)]` asks Rust to generate implementations for the traits we list. Here, that saves us from writing the standard `Debug` and `PartialEq` implementations by hand.

`PartialEq` is what lets `==` compare two outcomes. Without it, Rust has no idea whether `Purchased == Purchased`.

`Debug` is what lets a failing test print the outcome it got, so the failure message can say `NotEnoughGold` instead of nothing at all.

Our tests will want both.

### Use it

Now the purchase can say what happened:

```rust
    fn buy_click_upgrade(&mut self) -> PurchaseOutcome {
        let cost = click_upgrade_cost(self.click_upgrade_level);

        if self.gold < cost {
            return PurchaseOutcome::NotEnoughGold;
        }

        self.gold -= cost;
        self.click_upgrade_level = self.click_upgrade_level.saturating_add(1);

        PurchaseOutcome::Purchased
    }
```

Same shape. Same early return.

Only the answers changed, and now they read like the game talking.

`Game::buy_click_upgrade()` changes its return type to match:

```rust
    fn buy_click_upgrade(&mut self) -> PurchaseOutcome {
        self.player.buy_click_upgrade()
    }
```

---

## `match`: one branch per answer

Rust's `match` expression gives us a way to handle each possible variant explicitly.

Here is how `main()` will react to a purchase:

```rust
match game.buy_click_upgrade() {
    PurchaseOutcome::Purchased => println!("Bought it!"),
    PurchaseOutcome::NotEnoughGold => {}
}
```

`match` looks at the value and runs the branch that fits.

Each line is a pattern, an arrow, and what to do.

The `{}` on the second branch means: do nothing.

That is allowed. What is not allowed is leaving a variant out.

Delete the `NotEnoughGold` line and Rust refuses to compile:

```text
error[E0004]: non-exhaustive patterns:
`PurchaseOutcome::NotEnoughGold` not covered
```

This is the payoff.

With our `bool` version, the caller had to remember what `false` meant.

With an enum and `match`, the possible outcomes are named, and Rust can make us handle every one.

If we add a third variant next month, every `match` in the program will stop compiling until it says what to do about it.

Rust is keeping a list for us.

---

## `main()` goes shopping

Add the purchase to the loop, after the stage-cleared block:

```rust
        match game.buy_click_upgrade() {
            PurchaseOutcome::Purchased => println!(
                "Bought click upgrade {}! Click damage: {}  Gold left: {}",
                game.player.click_upgrade_level,
                click_damage(game.player.base_damage(), game.player.bonus_damage()),
                game.player.gold
            ),
            PurchaseOutcome::NotEnoughGold => {}
        }
```

Every turn, `main()` tries to buy an upgrade, and only says something when it works.

Worth being clear about what that means: in this terminal version, the game buys an upgrade the moment the player can afford one. Nobody is deciding anything.

Later, when this game has a button, the player will choose when to spend. The purchasing rule does not change when that happens. Only who calls it.

Run it:

```bash
cargo run
```

```text
Stage 1 cleared!  Gold: 5  XP: 6/20
  Next up: Training Slime with 30 health.
Stage 2 cleared!  Gold: 12  XP: 14/20
  Next up: Training Slime with 40 health.
Bought click upgrade 1! Click damage: 7  Gold left: 2
Stage 3 cleared!  Gold: 11  XP: 4/35
Level up! Now level 2. Click damage: 8
  Next up: Training Slime with 50 health.
Stage 4 cleared!  Gold: 22  XP: 16/35
  Next up: Training Slime with 60 health.
Bought click upgrade 2! Click damage: 10  Gold left: 2
Stage 5 cleared!  Gold: 15  XP: 30/35
  Next up: Training Slime with 70 health.
Stage 6 cleared!  Gold: 30  XP: 11/50
Level up! Now level 3. Click damage: 11
  Next up: Training Slime with 80 health.
Bought click upgrade 3! Click damage: 13  Gold left: 0
Stage 7 cleared!  Gold: 17  XP: 29/50
  Next up: Training Slime with 90 health.
Stage 8 cleared!  Gold: 36  XP: 49/50
  Next up: Training Slime with 100 health.
```

Now gold goes somewhere.

It climbs, and then it drops to almost nothing, because something got bought.

Click damage goes 5 → 7 → 8 → 10 → 11 → 13, pushed up from two directions at once: levels the player earns, and upgrades the player buys.

Compare the last line to where we started this article. Ninety-six gold and nothing to do with it, versus thirty-six gold and three upgrades already paid for.

---

## Testing the purchase

The pricing rule gets a test for its numbers:

```rust
    #[test]
    fn upgrades_cost_more_each_time() {
        assert_eq!(click_upgrade_cost(0), 10);
        assert_eq!(click_upgrade_cost(1), 20);
    }
```

Then the two things that can happen.

A broke player cannot buy anything, and a failed purchase changes nothing:

```rust
    #[test]
    fn buying_an_upgrade_needs_gold() {
        let mut player = Player::new();

        assert_eq!(player.buy_click_upgrade(), PurchaseOutcome::NotEnoughGold);
        assert_eq!(player.click_upgrade_level, 0);
        assert_eq!(player.bonus_damage(), 0);
    }

    #[test]
    fn buying_an_upgrade_spends_gold() {
        let mut player = Player::new();
        player.gold = 12;

        assert_eq!(player.buy_click_upgrade(), PurchaseOutcome::Purchased);
        assert_eq!(player.gold, 2);
        assert_eq!(player.click_upgrade_level, 1);
        assert_eq!(player.bonus_damage(), 2);
    }
```

Those `assert_eq!` lines comparing outcomes are exactly why we asked for `PartialEq` and `Debug`.

Without `PartialEq`, the comparison would not compile. Without `Debug`, a failure could not tell you which outcome it got instead.

Run them:

```bash
cargo test
```

All sixteen should pass.

---

## The complete program

If you are only reading along, skip to the last two sections.

If you have been following along in your editor, here is everything in one place.

At this point, `src/main.rs` should look like this:

```rust
struct Player {
    gold: u32,
    xp: u32,
    level: u32,
    click_upgrade_level: u32,
}

struct Enemy {
    name: String,
    health: u32,
    gold_reward: u32,
    xp_reward: u32,
}

struct Game {
    stage: u32,
    player: Player,
    enemy: Enemy,
}

#[derive(Debug, PartialEq)]
enum PurchaseOutcome {
    Purchased,
    NotEnoughGold,
}

impl Player {
    fn new() -> Player {
        Player {
            gold: 0,
            xp: 0,
            level: 1,
            click_upgrade_level: 0,
        }
    }

    fn add_gold(&mut self, amount: u32) {
        self.gold = self.gold.saturating_add(amount);
    }

    fn add_xp(&mut self, amount: u32) {
        self.xp = self.xp.saturating_add(amount);

        while self.xp >= xp_to_next_level(self.level) {
            self.xp -= xp_to_next_level(self.level);
            self.level = self.level.saturating_add(1);
        }
    }

    fn base_damage(&self) -> u32 {
        base_damage_for_level(self.level)
    }

    fn bonus_damage(&self) -> u32 {
        click_upgrade_bonus(self.click_upgrade_level)
    }

    fn buy_click_upgrade(&mut self) -> PurchaseOutcome {
        let cost = click_upgrade_cost(self.click_upgrade_level);

        if self.gold < cost {
            return PurchaseOutcome::NotEnoughGold;
        }

        self.gold -= cost;
        self.click_upgrade_level = self.click_upgrade_level.saturating_add(1);

        PurchaseOutcome::Purchased
    }
}

impl Enemy {
    fn for_stage(stage: u32) -> Enemy {
        Enemy {
            name: String::from("Training Slime"),
            health: enemy_health_for_stage(stage),
            gold_reward: gold_reward_for_stage(stage),
            xp_reward: xp_reward_for_stage(stage),
        }
    }
}

impl Game {
    fn new() -> Game {
        Game {
            stage: 1,
            player: Player::new(),
            enemy: Enemy::for_stage(1),
        }
    }

    fn click(&mut self) {
        attack(&self.player, &mut self.enemy);

        if is_defeated(self.enemy.health) {
            self.defeat_enemy();
        }
    }

    fn defeat_enemy(&mut self) {
        self.player.add_gold(self.enemy.gold_reward);
        self.player.add_xp(self.enemy.xp_reward);

        self.stage = self.stage.saturating_add(1);
        self.enemy = Enemy::for_stage(self.stage);
    }

    fn buy_click_upgrade(&mut self) -> PurchaseOutcome {
        self.player.buy_click_upgrade()
    }
}

fn click_damage(base: u32, bonus: u32) -> u32 {
    base.saturating_add(bonus)
}

fn health_after_hit(health: u32, damage: u32) -> u32 {
    health.saturating_sub(damage)
}

fn is_defeated(health: u32) -> bool {
    health == 0
}

fn enemy_health_for_stage(stage: u32) -> u32 {
    stage.saturating_mul(10).saturating_add(10)
}

fn gold_reward_for_stage(stage: u32) -> u32 {
    stage.saturating_mul(2).saturating_add(3)
}

fn xp_reward_for_stage(stage: u32) -> u32 {
    stage.saturating_mul(2).saturating_add(4)
}

fn xp_to_next_level(level: u32) -> u32 {
    level
        .saturating_sub(1)
        .saturating_mul(15)
        .saturating_add(20)
}

fn base_damage_for_level(level: u32) -> u32 {
    level.saturating_add(4)
}

fn click_upgrade_bonus(upgrades: u32) -> u32 {
    upgrades.saturating_mul(2)
}

fn click_upgrade_cost(upgrades: u32) -> u32 {
    upgrades.saturating_add(1).saturating_mul(10)
}

fn attack(player: &Player, enemy: &mut Enemy) {
    let damage = click_damage(player.base_damage(), player.bonus_damage());

    enemy.health = health_after_hit(enemy.health, damage);
}

fn main() {
    let mut game = Game::new();

    while game.stage <= 8 {
        let stage = game.stage;
        let level = game.player.level;

        game.click();

        if game.stage > stage {
            println!(
                "Stage {stage} cleared!  Gold: {}  XP: {}/{}",
                game.player.gold,
                game.player.xp,
                xp_to_next_level(game.player.level)
            );

            if game.player.level > level {
                println!(
                    "Level up! Now level {}. Click damage: {}",
                    game.player.level,
                    click_damage(game.player.base_damage(), game.player.bonus_damage())
                );
            }

            println!(
                "  Next up: {} with {} health.",
                game.enemy.name, game.enemy.health
            );
        }

        match game.buy_click_upgrade() {
            PurchaseOutcome::Purchased => println!(
                "Bought click upgrade {}! Click damage: {}  Gold left: {}",
                game.player.click_upgrade_level,
                click_damage(game.player.base_damage(), game.player.bonus_damage()),
                game.player.gold
            ),
            PurchaseOutcome::NotEnoughGold => {}
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn click_damage_adds_base_and_bonus() {
        assert_eq!(click_damage(3, 4), 7);
    }

    #[test]
    fn health_never_drops_below_zero() {
        assert_eq!(health_after_hit(3, 10), 0);
    }

    #[test]
    fn zero_health_is_defeated() {
        assert!(is_defeated(0));
        assert!(!is_defeated(1));
    }

    #[test]
    fn attack_reduces_enemy_health() {
        let player = Player::new();

        let mut enemy = Enemy::for_stage(1);
        enemy.health = 10;

        attack(&player, &mut enemy);

        assert_eq!(enemy.health, 5);
    }

    #[test]
    fn later_stages_have_tougher_enemies() {
        assert_eq!(enemy_health_for_stage(1), 20);
        assert_eq!(enemy_health_for_stage(2), 30);
    }

    #[test]
    fn later_stages_pay_better() {
        assert_eq!(gold_reward_for_stage(1), 5);
        assert_eq!(gold_reward_for_stage(2), 7);
        assert_eq!(xp_reward_for_stage(1), 6);
        assert_eq!(xp_reward_for_stage(2), 8);
    }

    #[test]
    fn levels_need_more_xp_as_they_go() {
        assert_eq!(xp_to_next_level(1), 20);
        assert_eq!(xp_to_next_level(2), 35);
    }

    #[test]
    fn leveling_up_keeps_leftover_xp() {
        let mut player = Player::new();

        player.add_xp(25);

        assert_eq!(player.level, 2);
        assert_eq!(player.xp, 5);
    }

    #[test]
    fn a_big_reward_can_skip_levels() {
        let mut player = Player::new();

        player.add_xp(60);

        assert_eq!(player.level, 3);
        assert_eq!(player.xp, 5);
    }

    #[test]
    fn levels_make_the_player_hit_harder() {
        assert_eq!(base_damage_for_level(1), 5);
        assert_eq!(base_damage_for_level(2), 6);
    }

    #[test]
    fn upgrades_cost_more_each_time() {
        assert_eq!(click_upgrade_cost(0), 10);
        assert_eq!(click_upgrade_cost(1), 20);
    }

    #[test]
    fn buying_an_upgrade_needs_gold() {
        let mut player = Player::new();

        assert_eq!(player.buy_click_upgrade(), PurchaseOutcome::NotEnoughGold);
        assert_eq!(player.click_upgrade_level, 0);
        assert_eq!(player.bonus_damage(), 0);
    }

    #[test]
    fn buying_an_upgrade_spends_gold() {
        let mut player = Player::new();
        player.gold = 12;

        assert_eq!(player.buy_click_upgrade(), PurchaseOutcome::Purchased);
        assert_eq!(player.gold, 2);
        assert_eq!(player.click_upgrade_level, 1);
        assert_eq!(player.bonus_damage(), 2);
    }

    #[test]
    fn a_new_game_starts_at_stage_one() {
        let game = Game::new();

        assert_eq!(game.stage, 1);
        assert_eq!(game.enemy.health, 20);
        assert_eq!(game.player.gold, 0);
    }

    #[test]
    fn clicking_damages_the_enemy() {
        let mut game = Game::new();

        game.click();

        assert_eq!(game.enemy.health, 15);
    }

    #[test]
    fn defeating_an_enemy_pays_the_player() {
        let mut game = Game::new();
        game.enemy.health = 1;

        game.click();

        assert_eq!(game.stage, 2);
        assert_eq!(game.enemy.health, 30);
        assert_eq!(game.player.gold, 5);
        assert_eq!(game.player.xp, 6);
    }
}
```

Run both:

```bash
cargo run
cargo test
```

The game should clear eight stages, buying three upgrades along the way, and all sixteen tests should pass.

---

## What we learned

Along the way, we encountered:

- a second rule-derived value, replacing a stored one
- early `return`
- `enum` and variants
- `#[derive(Debug, PartialEq)]`
- `match`, and the compiler refusing to let us forget a case

Two ideas are worth keeping.

The first finishes the job `base_damage()` started. `Player` has no damage numbers in it at all now. It stores progress, and damage is a rule applied to that progress.

The second is `PurchaseOutcome`.

When a `bool` would have made the caller remember what `false` meant, we gave the answers names instead, and Rust started checking that every answer gets handled.

That pattern shows up everywhere in Rust. Two common examples you will eventually meet are `Option`, for a value that might not be there, and `Result`, for an operation that might have failed.

This is the first time we needed our own. It will not be the last.

---

## Nothing happens unless you click

Our game finally has a progression loop.

Kill a slime, get paid, level up, buy damage, kill a tougher slime.

There is just one problem with calling it an idle game.

```text
clicks per second while you are playing: a lot
clicks per second while you are not:    zero
```

Nothing happens unless you click.

Walk away, come back an hour later, and the slime is exactly where you left it.

An idle game is supposed to keep working while you do not.

Next time, we are going to give the game time.

Not a timer. Not a clock. Just a number: how many seconds went by.

The game will take that number and work out everything that should have happened.

**Small function. Bigger game.**
