# Winning Is Not Progress.

Our game can fight its way through stage after stage now.

Click. The slime falls. A tougher one oozes in. Click.

That is a loop.

It is also a treadmill.

Every slime gets stronger. Our player never does.

In this article, winning starts to pay: gold and experience for every kill, and levels that make the player hit harder.

By the end, clearing a stage prints this:

```text
Stage 3 cleared!  Gold: 21  XP: 4/35
Level up! Now level 2. Click damage: 8
  Next up: Training Slime with 50 health.
```

* * *

## Our starting point

If you are only reading along, you can skip past this code to "Where does the gold go?"

If you are building along, you need three `struct`s, two `impl` blocks, five small functions, and eight tests:

```rust
struct Player {
    base_damage: u32,
    bonus_damage: u32,
}

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

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

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

impl Game {
    fn new() -> Game {
        Game {
            stage: 1,
            player: Player {
                base_damage: 5,
                bonus_damage: 2,
            },
            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.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 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 <= 3 {
        let stage = game.stage;

        game.click();

        if game.stage > stage {
            println!("Stage {stage} cleared!");
        } else {
            println!("{} 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 {
            base_damage: 5,
            bonus_damage: 2,
        };

        let mut enemy = Enemy {
            name: String::from("Training Slime"),
            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 a_new_game_starts_at_stage_one() {
        let game = Game::new();

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

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

        game.click();

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

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

        game.click();

        assert_eq!(game.stage, 2);
        assert_eq!(game.enemy.health, 30);
    }
}
```

`game.click()` attacks the enemy. If that attack kills it, `defeat_enemy()` advances the stage and sends in the next one.

The tests hold those rules in place. Three of them are about to change, so it helps to see them as they are now.

Everything today builds on that.

* * *

## Where does the gold go?

Defeating an enemy should pay.

Before we can pay the player, the player needs somewhere to put the money.

Right now, our `Player` has two fields, and neither of them is a wallet.

Add one:

```rust
struct Player {
    base_damage: u32,
    bonus_damage: u32,
    gold: u32,
}
```

Rust immediately complains.

Every place we build a `Player` now has to say how much gold it starts with.

There are two of those: `Game::new()` and the `attack_reduces_enemy_health` test. (`cargo run` only finds the first one, because it does not compile the tests. `cargo test` finds both.)

We are about to change `Player` a few more times today, and we do not want to chase every field through every place a player gets built.

`Enemy::for_stage()` already solves this exact problem for enemies.

Give `Player` the same treatment:

```rust
impl Player {
    fn new() -> Player {
        Player {
            base_damage: 5,
            bonus_damage: 2,
            gold: 0,
        }
    }
}
```

Put it right after the structs, next to `impl Enemy`.

Same convention as `Game::new()`: `new` is not a Rust keyword, just the name Rust programmers use for "make me a fresh one of these."

Then use it in `Game::new()`:

```rust
            player: Player::new(),
```

and in the test:

```rust
        let player = Player::new();
```

Now there is exactly one place that knows what a new player looks like.

When `Player` changes again, and it will, that is the only place we touch.

While you are in the tests, `a_new_game_starts_at_stage_one` can check that the wallet starts empty:

```rust
    assert_eq!(game.player.gold, 0);
```

Run `cargo test`.

All eight should still pass.

If you `cargo run` instead, Rust warns that `gold` is never read. A warning, not an error. The game is about to read it.

* * *

## How much is a slime worth?

How much gold should a defeated enemy drop?

That is a game rule.

Game rules get their own small function, like `enemy_health_for_stage()`.

Give this one the same treatment, right next to it:

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

```text
stage 1 → 5 gold
stage 2 → 7 gold
stage 3 → 9 gold
```

Later enemies are tougher.

They should also pay better.

Now, where does that reward live?

It belongs to the enemy.

A slime knows how much health it has.

It should also know what it is worth:

```rust
struct Enemy {
    name: String,
    health: u32,
    gold_reward: u32,
}
```

And `Enemy::for_stage()` fills it in:

```rust
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),
        }
    }
}
```

Because every enemy is built through `for_stage()`, this is the only place that needed to change.

Now run `cargo test` again.

`cargo run` would build, with one more never-read warning, because the game only builds enemies through `for_stage()`.

The tests do not build. `attack_reduces_enemy_health` builds an `Enemy` by hand, with a name and 10 health, and Rust wants a `gold_reward` there too.

That is the compiler finding the last place for us.

Instead of making that test learn about rewards, let it start from a real enemy and adjust the one thing it cares about:

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

The test still checks what it always checked: an attack takes health off the enemy.

* * *

## Paying out

The player has a wallet. The enemy has a price on its head.

Handing one to the other is the player's job.

Add a method to `impl Player`, under `new()`:

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

`&mut self` is the same `&mut self` from `click()`.

The method borrows the player, changes it, and gives it back.

Which brings us to `defeat_enemy()`, the one place in the game that already knows an enemy just died.

It grows:

```rust
    fn defeat_enemy(&mut self) {
        self.player.add_gold(self.enemy.gold_reward);

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

The order matters.

We pay the player **before** the enemy is replaced.

On the next line, `self.enemy` becomes a brand new slime with a brand new reward.

If we paid afterward, the player would get paid for an enemy they have not fought yet.

Read `defeat_enemy()` out loud now:

> Pay the player. Go to the next stage. Bring in the next enemy.

No arithmetic to decode.

* * *

## Experience and levels

Gold is something to spend.

Experience, or XP, is something to keep.

Every defeated enemy should also give the player experience, and when they have enough, they level up.

That is two rules, so we need two numbers:

```text
how much XP does an enemy give?
how much XP does the next level take?
```

The first one looks like the gold rule.

Put it right under `gold_reward_for_stage()`:

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

The second one is new.

Each level should take a little more than the last:

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

```text
level 1 → 20 XP to reach level 2
level 2 → 35 XP to reach level 3
level 3 → 50 XP to reach level 4
```

That chain is split across lines so each step is easy to read. A line starting with `.` just continues the line above it.

Level 1 is the starting line. The `saturating_sub(1)` makes it count as zero steps taken, so level 1 needs the plain 20 and every level after that adds 15. Without it, the first level would already cost 35.

The exact numbers do not matter yet.

They have names and they will have tests.

The enemy carries its XP reward the same way it carries gold:

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

Add it to `Enemy::for_stage()`, under `gold_reward`:

```rust
            xp_reward: xp_reward_for_stage(stage),
```

And the player needs two more fields: how much XP they have, and what level they are:

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

In `Player::new()`:

```rust
            xp: 0,
            level: 1,
```

That is now the normal place a fresh player is built, so future changes stay concentrated there.

Run `cargo test`. Still eight, plus a few warnings that the new fields and `xp_to_next_level()` are never used. The next section uses them.

* * *

## Leveling up

Now the interesting part.

Adding XP is not like adding gold.

Gold just piles up.

XP piles up until it crosses a line, and then something happens:

```rust
    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);
        }
    }
```

Add it to `impl Player`, right under `add_gold()`.

Walk through it with a level 1 player who has 0 XP and just earned 25.

The XP becomes 25.

Level 1 needs 20. We have 25. That is enough, so we go into the loop.

Spend 20 of it. XP is now 5. Level is now 2.

Level 2 needs 35. We have 5. The loop stops.

The player is level 2 with 5 XP toward the next one.

Why a `while` and not an `if`?

Because a reward might be big enough to cross two lines at once.

Give that same level 1 player 60 XP.

Spend 20 → level 2, 40 left.

Level 2 needs 35. 40 is enough. Spend 35 → level 3, 5 left.

Level 3 needs 50. The loop stops.

An `if` would have stopped at level 2 and left 40 XP sitting there, more than enough for another level the player never got.

A `while` keeps going until the XP really is below the line.

You might notice that `xp_to_next_level(self.level)` gets called twice in a row. That is fine. It is a tiny calculation, and for the same level it always gives the same answer.

That matters more than it looks right now. Later in this series, a player is going to come back after a few hours away and collect a very large pile of XP all at once.

### A plain minus sign

Look at this line:

```rust
self.xp -= xp_to_next_level(self.level);
```

`-=` is shorthand: `self.xp -= 20` means `self.xp = self.xp - 20`.

That is ordinary subtraction, not `saturating_sub()`.

Everywhere else in this program, subtraction goes through `saturating_sub()`.

Here, it is safe, and the line right above it is the reason.

The `while` only lets us in when `self.xp` is at least as big as the amount we are about to subtract.

The result can never drop below zero.

`saturating_sub()` is for when a number *might* go somewhere it should not.

When the code right next to it proves that it cannot, plain subtraction says exactly what it means.

Now `defeat_enemy()` pays both:

```rust
        self.player.add_gold(self.enemy.gold_reward);
        self.player.add_xp(self.enemy.xp_reward);
```

* * *

## What does a level actually do?

Right now, nothing.

The player levels up, and hits exactly as hard as before.

A level should make the player stronger.

Look at `Player` again:

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

`base_damage` is a number we wrote down once, when the player was created, and never touched again.

But base damage should not be a fact about the player anymore.

It should be a *consequence* of the player's level.

That makes it a rule. Put it under `xp_to_next_level()`:

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

```text
level 1 → 5 damage
level 2 → 6 damage
level 3 → 7 damage
```

Level 1 gives 5, which is exactly the `base_damage` we have had since the beginning.

Nothing about the first fight changes.

Now the field can go.

Remove `base_damage` from the struct and from `Player::new()`.

Rust will complain about one more place that still uses it. We will get to that in a second.

In its place, give `Player` a method that asks the rule, right under `add_xp()`:

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

There is something new in that signature.

`&self`, without the `mut`.

So far, our methods have needed `&mut self` because they changed something.

A method that only needs to **read** takes `&self` instead. This is our first one.

`base_damage()` looks at the player's level and reports a number.

It does not change anything, so it does not ask for permission to.

One thing still uses the old field: `attack()`.

It reads `player.base_damage`, and that field no longer exists.

Rust says so (the line is wrapped here to fit):

```text
error[E0615]: attempted to take value of method
`base_damage` on type `&Player`
```

and even suggests the fix: use parentheses to call the method.

```rust
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);
}
```

The parentheses are the only difference.

`player.base_damage` was a number stored in the player.

`player.base_damage()` is a number the player works out from its level.

`attack()` did not have to learn anything about levels.

It asked for base damage. It got base damage.

Run `cargo test`. All eight still pass, and that is the point: a level 1 player still deals 5 base damage, so nothing a test checks has changed.

* * *

## `main()` shows the progress

Our `main()` has been printing the enemy's health after every click.

That was useful when a fight took three clicks. Now there are rewards to watch, so let us print when something happens instead:

```rust
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
            );
        }
    }
}
```

Before each click, `main()` remembers the stage and the level.

After the click, it compares.

Stage went up? The enemy died, so say so, show the wallet, and introduce the next one.

Level went up? Say that too.

Notice that the level check sits *inside* the stage check. It can, because XP is only ever handed out in `defeat_enemy()`. No enemy died, no XP, no level up.

One small thing about that first `println!`. `{stage}` works because `stage` is a plain variable. `game.player.gold` has dots in it, so it goes in the list after the string and a bare `{}` picks it up.

`main()` still does not know how leveling works.

It does borrow one rule, `xp_to_next_level()`, so it can show how far away the next level is. That is display, not a decision.

It clicks, it asks, and it prints what it was told.

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.
Stage 3 cleared!  Gold: 21  XP: 4/35
Level up! Now level 2. Click damage: 8
  Next up: Training Slime with 50 health.
Stage 4 cleared!  Gold: 32  XP: 16/35
  Next up: Training Slime with 60 health.
Stage 5 cleared!  Gold: 45  XP: 30/35
  Next up: Training Slime with 70 health.
Stage 6 cleared!  Gold: 60  XP: 11/50
Level up! Now level 3. Click damage: 9
  Next up: Training Slime with 80 health.
Stage 7 cleared!  Gold: 77  XP: 29/50
  Next up: Training Slime with 90 health.
Stage 8 cleared!  Gold: 96  XP: 49/50
  Next up: Training Slime with 100 health.
```

Read that top to bottom.

Gold climbs. XP climbs toward a line, crosses it, and resets with the leftover carried over. Click damage starts at 7 and climbs to 8, then 9.

Compare that to where we started this article: 5 base damage plus 2 bonus, on stage 1 and on stage 30 alike.

The slimes still get tougher every stage. The difference is that now, so does the player.

* * *

## Testing the rules

Each new rule gets a test for its numbers. Add these to the `tests` module:

```rust
    #[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 levels_make_the_player_hit_harder() {
        assert_eq!(base_damage_for_level(1), 5);
        assert_eq!(base_damage_for_level(2), 6);
    }
```

Then the behaviors.

The two walkthroughs from `add_xp()` become tests, so the explanation and the code can never drift apart:

```rust
    #[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);
    }
```

And the big one.

The last test in our starting point, `defeating_an_enemy_advances_the_stage`, checked that a win moved the game forward.

Now a win should also pay.

Rename it and add two checks at the end:

```rust
    #[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);
    }
```

This test does not care how damage is calculated, or how XP crosses a threshold.

Those rules have their own tests now.

It asks the bigger question:

> When a click kills something, does the player get paid for it?

Run them:

```bash
cargo test
```

All thirteen 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 {
    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);
    }
}
```

Run both:

```bash
cargo run
cargo test
```

The game should clear eight stages, and all thirteen tests should pass.

* * *

## What we learned

Along the way, we encountered:

*   `Player::new()`, so a player is built in one place
    
*   `&self` methods that read without changing
    
*   a `while` loop that keeps going until a condition stops being true
    
*   `-=`, and plain subtraction where the code above it proves the number cannot go below zero
    
*   rules that calculate a value instead of storing it
    

That is a short list, and the last item is the one worth keeping.

`base_damage` used to be a number sitting in the player.

Now it is a rule applied to the player's level.

The player got smaller, and the game got bigger.

Hold onto that idea. We are going to do it again next time, to the other half of that damage calculation.

* * *

## Gold with nothing to buy

Our player finally gets stronger by winning.

There is just one problem.

Look at the last line of that run again:

```text
Stage 8 cleared!  Gold: 96  XP: 49/50
```

Ninety-six gold.

We call it money, but there is nothing in this game to spend it on.

```text
gold earned: 96
gold spent:  0
```

Right now it is not a reward.

It is a high score.

Next time, we are going to give the player something to buy, and discover that the obvious way to report whether a purchase worked is not good enough.

**Small function. Bigger game.**
