# One Fight Is Not a Game.

# One Fight Is Not a Game.

Every fight in our game ends the same way.

The slime dies.

Then the program dies with it.

So far, winning has been how our game ends.

In an idle game, winning is how the game keeps going.

One enemy falls, and the next one walks in.

In this article, we are going to give our game a next stage, a tougher enemy waiting there, and one place to keep all of it.

By the end, a whole turn of our game will fit in one line:

```rust
game.click();
```

That one call will attack the enemy, notice when it dies, advance the stage, and send in the next one.

---

## Our starting point

So far, we have a player, a slime, and a way for one to hit the other.

You need two structs and four small functions to follow along:

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

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

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 attack(
    player: &Player,
    enemy: &mut Enemy,
) {
    let damage = click_damage(
        player.base_damage,
        player.bonus_damage,
    );

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

`attack()` borrows the player to read it and borrows the enemy to change it.

`&Player` is read-only access.

`&mut Enemy` is permission to change the enemy without taking it away from its owner.

We will lean on both of those again today.

Our combat loop looks like this:

```rust
fn main() {
    let player = Player {
        base_damage: 5,
        bonus_damage: 2,
    };

    let mut enemy = Enemy {
        name: String::from("Training Slime"),
        health: 20,
    };

    while !is_defeated(enemy.health) {
        attack(&player, &mut enemy);

        println!(
            "{} health: {}",
            enemy.name,
            enemy.health
        );
    }

    println!("{} defeated!", enemy.name);
}
```

At the bottom of `main.rs`, a `tests` module holds four tests: one for each small rule, and one for `attack()`. You will see all of them in the complete program at the end.

Run it, and the Training Slime goes down in three hits.

Then the program is over.

---

## The next enemy needs to be tougher

Before we worry about where the next enemy comes from, we need to decide what it looks like.

If every enemy had 20 health, stage 50 would feel exactly like stage 1.

The next enemy should be tougher.

That is a game rule, and we know what to do with game rules by now.

Give it a small function, next to our other small rules:

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

Every stage adds ten more health.

```text
stage 1 → 20 health
stage 2 → 30 health
stage 3 → 40 health
```

Stage 1 gives us 20 health, which happens to be the same Training Slime we have been fighting all along.

`saturating_mul()` is the multiplication version of the `saturating_add()` and `saturating_sub()` we already use.

It is the same game-design choice we made for damage and health: a number that goes wrong should stop at a sensible edge, not wrap around into nonsense.

If the stage ever gets absurdly high, the health stops at the largest `u32` instead of overflowing.

The exact balance does not matter yet. We can tune it later.

What matters is that the rule has a name and a test.

Add it to the `tests` module:

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

If you build now, Rust warns that `enemy_health_for_stage` is never used.

That is a warning, not an error. Rust is pointing out code that nothing calls yet.

We are about to call it.

---

## Try it in `main()` first

We have a rule for how tough the next enemy is.

Now we need the game to actually use it.

The quickest way is to put it straight into `main()`.

We need a stage counter, and when an enemy is defeated, the stage goes up and a new enemy replaces the old one:

```rust
fn main() {
    let player = Player {
        base_damage: 5,
        bonus_damage: 2,
    };

    let mut stage = 1;

    let mut enemy = Enemy {
        name: String::from("Training Slime"),
        health: enemy_health_for_stage(stage),
    };

    while stage <= 3 {
        attack(&player, &mut enemy);

        if is_defeated(enemy.health) {
            println!("Stage {stage} cleared!");

            stage = stage.saturating_add(1);

            enemy = Enemy {
                name: String::from("Training Slime"),
                health: enemy_health_for_stage(stage),
            };
        } else {
            println!(
                "{} health: {}",
                enemy.name,
                enemy.health
            );
        }
    }
}
```

We stop after stage 3 so the terminal does not scroll forever.

Instead of announcing that an enemy was defeated, we now announce that the stage was cleared.

Run it:

```bash
cargo run
```

It works.

The slime falls, a tougher one takes its place, and the stages keep coming.

It is also a mess.

Look at what `main()` is doing now:

```text
owns the player
owns the enemy
owns the stage
decides when a stage is cleared
decides what happens next
builds the next enemy
prints everything
```

Only that last line is really the job of `main()`.

Everything else is the game.

And this is the small version.

Soon, defeating an enemy will also need to hand out rewards.

Every one of those rules would land right here, tangled up with the code that prints to the terminal.

There is a bigger problem coming, too.

Later in this series, `main()` goes away completely.

A browser takes over.

If the rules of our game live inside `main()`, they leave with it.

We need somewhere else for them to live.

---

## Give the game a shape

We have done this before.

Three loose numbers were not a player, so we built a `Player`.

A stage, a player, and an enemy sitting loose in `main()` are not a game.

So we build a `Game`:

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

Put it under `Enemy`, with the other structs.

This struct is a little different from our first two.

Its fields are not just numbers and text.

Two of them are entire structs:

```text
Game
├── stage: u32
├── player: Player
└── enemy: Enemy
```

A struct can hold any type, including the types we made ourselves.

When a `Game` holds a `Player`, it does not point at a player that lives somewhere else.

The game owns it.

Same for the enemy.

That is the whole point.

There is now one value we can point at and say:

> This is the current state of the game.

---

## Where does a new enemy come from?

We could build a game the same way we built players and enemies:

```rust
let mut game = Game {
    stage: 1,
    player: Player {
        base_damage: 5,
        bonus_damage: 2,
    },
    enemy: Enemy {
        name: String::from("Training Slime"),
        health: enemy_health_for_stage(1),
    },
};
```

That works, but look at the enemy.

We wrote this exact enemy in our messy `main()` already, once at the start and once after every defeat:

```rust
Enemy {
    name: String::from("Training Slime"),
    health: enemy_health_for_stage(stage),
}
```

Now it shows up a third time.

Every time we need an enemy, we spell out how to build one from scratch.

What we want to say is much shorter:

> Give me the enemy for this stage.

That sounds like a function.

But this function is not a combat rule like `attack()`.

It answers a question about enemies: how is one made?

So it should live with `Enemy`.

---

## Attach functions to a type with `impl`

Rust lets us attach functions to a type with an `impl` block:

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

`impl Enemy` means:

> here are some functions that belong to `Enemy`

Put it right after the structs. `impl` blocks usually sit close to the types they belong to.

Inside, `for_stage()` looks like every function we have written so far.

It takes a stage.

It returns an `Enemy`.

The only difference is where it lives, and that changes how we call it:

```rust
Enemy::for_stage(1)
```

The `::` reaches into `Enemy` and finds the function that belongs to it.

We have actually been using this syntax since we first gave our slime a name:

```rust
String::from("Training Slime")
```

`from()` is a function that belongs to `String`.

`for_stage()` is a function that belongs to `Enemy`.

A function attached to a type like this is called an **associated function**.

Notice that `for_stage()` did not replace our small rule.

It calls it:

```rust
health: enemy_health_for_stage(stage),
```

`enemy_health_for_stage()` still decides how tough an enemy is.

`Enemy::for_stage()` decides how to put an enemy together.

Two small jobs, two small functions.

---

## A new game

`Game` gets its own `impl` block, and its first associated function creates a fresh game:

```rust
impl Game {
    fn new() -> Game {
        Game {
            stage: 1,
            player: Player {
                base_damage: 5,
                bonus_damage: 2,
            },
            enemy: Enemy::for_stage(1),
        }
    }
}
```

There is nothing magical about the name `new`.

Rust has no special constructor keyword.

`new` is just a regular associated function, and naming it `new` is a convention Rust programmers use for "make me a fresh one of these."

Starting a game is now one line:

```rust
let mut game = Game::new();
```

Until `main()` actually uses `Game`, Rust will warn that parts of it are never used.

Those warnings disappear once we rewrite `main()`.

Game setup has a name.

It also has one home, and it is not `main()`.

---

## One click

Now the part we have been building toward.

What happens when a player clicks?

Add this inside the same `impl Game` block, right under `new()`:

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

There is a new word in that signature:

```rust
&mut self
```

`self` means:

> the game this method was called on

When we write:

```rust
game.click();
```

inside `click()`, `self` refers to `game`.

That makes `click()` a **method**: a function attached to a type that works on a specific value of that type.

`Enemy::for_stage()` and `Game::new()` do not take `self`. They make new values, so there is no existing value to work on yet.

`click()` does take `self`. It needs a game that already exists.

Now look at the `&mut` in front of `self`.

We already know what that means.

It is the same `&mut` we used to let `attack()` change an enemy.

`click()` borrows the game mutably for as long as the call lasts.

It can change the game.

It does not own the game.

When `click()` finishes, the borrow ends, and `main()` still owns the game.

A method that only needs to read the game would take `&self` instead, the same way `attack()` takes `&Player`.

We do not need one of those yet.

At the call site, we do not have to write `&mut game` ourselves.

The dot in `game.click()` takes care of the borrow.

The rule from last time still applies, though.

Rust will not mutably borrow something whose owner never agreed it could change.

Leave the `mut` off:

```rust
let game = Game::new();

game.click();
```

and Rust refuses:

```text
error[E0596]: cannot borrow `game`
as mutable, as it is not declared
as mutable
```

`click()` changes the game, so the game has to be mutable:

```rust
let mut game = Game::new();
```

---

## Two borrows, one game

Look inside `click()` again:

```rust
attack(&self.player, &mut self.enemy);
```

That line looks like it breaks the rule we learned last time.

It borrows part of the game to read it.

At the same time, it borrows another part of the game to change it.

Last time, our borrowing rule was:

> **For the same data at the same time: many readers, or one writer, but not both.**

So why is Rust fine with this?

The key words are *the same data*.

`self.player` and `self.enemy` are different fields.

They are both inside the game, but they do not overlap.

Rust can see that reading the player cannot interfere with changing the enemy, so it lets `attack()` have both.

Our `attack()` from last time did not need to change at all.

The borrowing did not go away.

It moved inside the game, where it belongs.

---

## What happens when the enemy dies?

Right now, `click()` only attacks.

When the enemy's health hits zero, the next click attacks a corpse.

That is grim, and it is not much of a game.

After every attack, `click()` should check whether the enemy is defeated:

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

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

Rust will not compile that yet.

`defeat_enemy()` does not exist.

Add it inside `impl Game` too, right under `click()`:

```rust
    fn defeat_enemy(&mut self) {
        self.stage = self.stage.saturating_add(1);
        self.enemy = Enemy::for_stage(self.stage);
    }
```

The stage goes up.

The defeated enemy is replaced by the enemy for the new stage.

The game owns its enemy, so when the old one is replaced, Rust cleans it up for us.

Notice how `click()` calls `defeat_enemy()`:

```rust
self.defeat_enemy();
```

A method can call another method on the same game through `self`.

We could have put those two lines straight into `click()`.

It would work.

But defeating an enemy is its own event, and that event is about to grow.

Soon, it will do more than swap in a new enemy.

Giving it a name now means `click()` stays readable no matter how much happens after a win.

Read `click()` out loud:

> Attack the enemy. If it went down, handle the defeat.

Read `defeat_enemy()`:

> Go to the next stage. Bring in the enemy for that stage.

No arithmetic to decode.

Each one coordinates smaller rules that already know how to do their own jobs.

---

## `main()` gets boring

Here is our new `main()`:

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

`main()` still reads the game so it can print something.

Before each click, it remembers the stage.

After the click, if the stage went up, it knows the enemy was defeated.

That is not a game rule.

That is `main()` deciding what to show.

It does not build enemies.

It does not decide when a stage is cleared.

It does not know how tough the next enemy is.

It creates a game, clicks, and prints.

Boring is exactly what we want here.

Run it:

```bash
cargo run
```

You should see:

```text
Training Slime health: 13
Training Slime health: 6
Stage 1 cleared!
Training Slime health: 23
Training Slime health: 16
Training Slime health: 9
Training Slime health: 2
Stage 2 cleared!
Training Slime health: 33
Training Slime health: 26
Training Slime health: 19
Training Slime health: 12
Training Slime health: 5
Stage 3 cleared!
```

Three stages.

Each slime tougher than the last.

And the game, not `main()`, decided all of that.

---

## Test what a player experiences

Our first five tests each check one rule.

`Game` lets us test something bigger: what actually happens when a player plays.

A new game should start at stage 1 with a stage 1 enemy:

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

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

A click should hurt the enemy:

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

    game.click();

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

And the important one: defeating an enemy should move the game forward.

We do not want to click several times just to set up the test, so we put the enemy one hit away from defeat:

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

This test does not care how damage is calculated.

It does not care how health is subtracted.

Those rules already have their own tests.

This test asks a bigger question:

> When a click defeats an enemy, does the game move on?

Run them:

```bash
cargo test
```

All eight should pass.

---

## The complete program

If you are only reading along, you can 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 {
    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);
    }
}
```

Run both:

```bash
cargo run
cargo test
```

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

---

## What we learned

Along the way, we encountered:

- structs that own other structs
- `impl` blocks
- associated functions like `Enemy::for_stage()`
- `new()` as a naming convention, not a keyword
- methods
- `self` and `&mut self`
- borrowing two different fields at the same time
- `saturating_mul()`
- tests that check what a player experiences

You do not need to memorize all of that yet.

The important change is where the game lives.

We started with a `main()` that owned everything and decided everything.

We ended with:

```rust
let mut game = Game::new();

game.click();
```

One click is now one full turn:

```text
game.click()
    ↓
attack(&self.player, &mut self.enemy)
    ↓
click_damage()
    ↓
health_after_hit()
    ↓
is_defeated()?
    ↓
defeat_enemy()
    ↓
next stage + Enemy::for_stage()
```

Look at the bottom of that chain.

`click_damage()`, `health_after_hit()`, and `is_defeated()` are the same three functions we started this series with.

They have not changed.

They are just a few layers deeper now, inside something that finally looks like a game.

---

## Winning does not pay yet

Our game has a real loop now.

Click. The slime falls. The next one walks in tougher than the last.

But look at what the player gets for all that clicking:

```text
gold:       nothing
experience: nothing
levels:     nothing
```

We just cleared three stages.

Our player is exactly as strong as when the game started.

Every slime gets tougher.

Our player never does.

Keep going long enough, and every new slime takes more clicks than the last one.

Forever.

That is not progression.

That is a treadmill.

An idle game needs a reason to keep clicking: rewards for winning, levels to reach, and something to spend those rewards on.

And this time, we already know where those rewards will be handed out:

```rust
fn defeat_enemy(&mut self)
```

Next, we will make winning worth something.

**Small function. Bigger game.**
