I'm trying to solve Day 22 of Advent of Code 2015, and I get the right answer to Part 1 but I'm not sure why my answer to Part 2 is incorrect.

This is the puzzle with the input:


<document>
# Advent of Code 2015 - Day 22: Wizard Simulator 20XX

Little Henry Case decides that defeating bosses with swords and stuff is boring. Now he's playing the game with a wizard. Of course, he gets stuck on another boss and needs your help again.

In this version, combat still proceeds with the player and the boss taking alternating turns. The player still goes first. Now, however, you don't get any equipment; instead, you must choose one of your spells to cast. The first character at or below 0 hit points loses.

Since you're a wizard, you don't get to wear armor, and you can't attack normally. However, since you do magic damage, your opponent's armor is ignored, and so the boss effectively has zero armor as well. As before, if armor (from a spell, in this case) would reduce damage below 1, it becomes 1 instead - that is, the boss' attacks always deal at least 1 damage.

On each of your turns, you must select one of your spells to cast. If you cannot afford to cast any spell, you lose. Spells cost mana; you start with 500 mana, but have no maximum limit. You must have enough mana to cast a spell, and its cost is immediately deducted when you cast it. Your spells are Magic Missile, Drain, Shield, Poison, and Recharge.

    Magic Missile costs 53 mana. It instantly does 4 damage.
    Drain costs 73 mana. It instantly does 2 damage and heals you for 2 hit points.
    Shield costs 113 mana. It starts an effect that lasts for 6 turns. While it is active, your armor is increased by 7.
    Poison costs 173 mana. It starts an effect that lasts for 6 turns. At the start of each turn while it is active, it deals the boss 3 damage.
    Recharge costs 229 mana. It starts an effect that lasts for 5 turns. At the start of each turn while it is active, it gives you 101 new mana.

Effects all work the same way. Effects apply at the start of both the player's turns and the boss' turns. Effects are created with a timer (the number of turns they last); at the start of each turn, after they apply any effect they have, their timer is decreased by one. If this decreases the timer to zero, the effect ends. You cannot cast a spell that would start an effect which is already active. However, effects can be started on the same turn they end.

For example, suppose the player has 10 hit points and 250 mana, and that the boss has 13 hit points and 8 damage:

-- Player turn --
- Player has 10 hit points, 0 armor, 250 mana
- Boss has 13 hit points
Player casts Poison.

-- Boss turn --
- Player has 10 hit points, 0 armor, 77 mana
- Boss has 13 hit points
Poison deals 3 damage; its timer is now 5.
Boss attacks for 8 damage.

-- Player turn --
- Player has 2 hit points, 0 armor, 77 mana
- Boss has 10 hit points
Poison deals 3 damage; its timer is now 4.
Player casts Magic Missile, dealing 4 damage.

-- Boss turn --
- Player has 2 hit points, 0 armor, 24 mana
- Boss has 3 hit points
Poison deals 3 damage. This kills the boss, and the player wins.

Now, suppose the same initial conditions, except that the boss has 14 hit points instead:

-- Player turn --
- Player has 10 hit points, 0 armor, 250 mana
- Boss has 14 hit points
Player casts Recharge.

-- Boss turn --
- Player has 10 hit points, 0 armor, 21 mana
- Boss has 14 hit points
Recharge provides 101 mana; its timer is now 4.
Boss attacks for 8 damage!

-- Player turn --
- Player has 2 hit points, 0 armor, 122 mana
- Boss has 14 hit points
Recharge provides 101 mana; its timer is now 3.
Player casts Shield, increasing armor by 7.

-- Boss turn --
- Player has 2 hit points, 7 armor, 110 mana
- Boss has 14 hit points
Shield's timer is now 5.
Recharge provides 101 mana; its timer is now 2.
Boss attacks for 8 - 7 = 1 damage!

-- Player turn --
- Player has 1 hit point, 7 armor, 211 mana
- Boss has 14 hit points
Shield's timer is now 4.
Recharge provides 101 mana; its timer is now 1.
Player casts Drain, dealing 2 damage, and healing 2 hit points.

-- Boss turn --
- Player has 3 hit points, 7 armor, 239 mana
- Boss has 12 hit points
Shield's timer is now 3.
Recharge provides 101 mana; its timer is now 0.
Recharge wears off.
Boss attacks for 8 - 7 = 1 damage!

-- Player turn --
- Player has 2 hit points, 7 armor, 340 mana
- Boss has 12 hit points
Shield's timer is now 2.
Player casts Poison.

-- Boss turn --
- Player has 2 hit points, 7 armor, 167 mana
- Boss has 12 hit points
Shield's timer is now 1.
Poison deals 3 damage; its timer is now 5.
Boss attacks for 8 - 7 = 1 damage!

-- Player turn --
- Player has 1 hit point, 7 armor, 167 mana
- Boss has 9 hit points
Shield's timer is now 0.
Shield wears off, decreasing armor by 7.
Poison deals 3 damage; its timer is now 4.
Player casts Magic Missile, dealing 4 damage.

-- Boss turn --
- Player has 1 hit point, 0 armor, 114 mana
- Boss has 2 hit points
Poison deals 3 damage. This kills the boss, and the player wins.

You start with 50 hit points and 500 mana points. The boss's actual stats are in your puzzle input. What is the least amount of mana you can spend and still win the fight? (Do not include mana recharge effects as "spending" negative mana.)

Puzzle Input:
Hit Points: 51
Damage: 9
</document>

My solution is:

```rust
use core::cmp::Ordering;
use std::collections::BinaryHeap;

const MAGIC_MISSILE_MANA_COST: u32 = 53;
const MAGIC_MISSILE_DAMAGE: u32 = 4;

const DRAIN_MANA_COST: u32 = 73;
const DRAIN_AMOUNT: u32 = 2;

const SHIELD_MANA_COST: u32 = 113;
const SHIELD_DURATION: u32 = 6;
const SHIELD_BONUS_ARMOR: u32 = 7;

const POISON_MANA_COST: u32 = 173;
const POISON_DURATION: u32 = 6;
const POISON_DAMAGE: u32 = 3;

const RECHARGE_MANA_COST: u32 = 229;
const RECHARGE_DURATION: u32 = 5;
const RECHARGE_AMOUNT: u32 = 101;

#[derive(Debug, PartialEq, Clone)]
enum EffectType {
    Shield,
    Poison,
    Recharge,
}

#[derive(Debug, Clone)]
struct Effect {
    r#type: EffectType,
    timer: u32,
}

#[derive(Debug, Default, Clone)]
struct Character {
    hp: u32,
    mana: u32,
    damage: u32,
    armor: u32,
    bonus_armor: u32,
    effects: Vec<Effect>,
}

impl Character {
    fn apply_effects(&mut self) {
        self.bonus_armor = 0;

        let mut i = 0;
        while i < self.effects.len() {
            if self.effects[i].timer <= 0 {
                self.effects.swap_remove(i);
                continue;
            }

            match self.effects[i].r#type {
                EffectType::Shield => {
                    self.apply_shield();
                }

                EffectType::Poison => {
                    self.apply_poison();
                }
                EffectType::Recharge => {
                    self.apply_mana_recharge();
                }
            }

            self.effects[i].timer = self.effects[i].timer.saturating_sub(1);
            i += 1;
        }
    }

    fn apply_shield(&mut self) {
        self.bonus_armor = self.bonus_armor.saturating_add(SHIELD_BONUS_ARMOR);
    }

    fn apply_poison(&mut self) {
        self.hp = self.hp.saturating_sub(POISON_DAMAGE);
    }

    fn apply_mana_recharge(&mut self) {
        self.mana = self.mana.saturating_add(RECHARGE_AMOUNT);
    }

    fn cast_magic_missile(&mut self, other: &mut Character) -> bool {
        if self.mana >= MAGIC_MISSILE_MANA_COST {
            self.mana -= MAGIC_MISSILE_MANA_COST;
            other.hp = other.hp.saturating_sub(MAGIC_MISSILE_DAMAGE);
            return true;
        }
        return false;
    }

    fn cast_drain(&mut self, other: &mut Character) -> bool {
        if self.mana >= DRAIN_MANA_COST {
            self.mana -= DRAIN_MANA_COST;
            other.hp = other.hp.saturating_sub(DRAIN_AMOUNT);
            self.hp = self.hp.saturating_add(DRAIN_AMOUNT);
            return true;
        }
        return false;
    }

    fn has_effect(&self, effect_type: EffectType) -> bool {
        for effect in self.effects.iter() {
            if effect.r#type == effect_type {
                return true;
            }
        }
        return false;
    }

    fn cast_shield(&mut self) -> bool {
        if self.mana >= SHIELD_MANA_COST {
            if !self.has_effect(EffectType::Shield) {
                self.mana -= SHIELD_MANA_COST;
                self.effects.push(Effect {
                    r#type: EffectType::Shield,
                    timer: SHIELD_DURATION,
                });
                return true;
            }
        }
        return false;
    }

    fn cast_poison(&mut self, other: &mut Character) -> bool {
        if self.mana >= POISON_MANA_COST {
            if !other.has_effect(EffectType::Poison) {
                self.mana -= POISON_MANA_COST;
                other.effects.push(Effect {
                    r#type: EffectType::Poison,
                    timer: POISON_DURATION,
                });
                return true;
            }
        }
        return false;
    }

    fn cast_recharge(&mut self) -> bool {
        if self.mana >= RECHARGE_MANA_COST {
            if !self.has_effect(EffectType::Recharge) {
                self.mana -= RECHARGE_MANA_COST;
                self.effects.push(Effect {
                    r#type: EffectType::Recharge,
                    timer: RECHARGE_DURATION,
                });
                return true;
            }
        }
        return false;
    }

    fn attack(&self, other: &mut Character) {
        let total_armor = other.armor + other.bonus_armor;
        let total_damage = std::cmp::max(1, self.damage.saturating_sub(total_armor));
        other.hp = other.hp.saturating_sub(total_damage);
    }
}

#[derive(Debug, Clone)]
enum Spell {
    MagicMissile,
    Drain,
    Shield,
    Poison,
    Recharge,
}

const SPELLS: [Spell; 5] = [
    Spell::MagicMissile,
    Spell::Drain,
    Spell::Shield,
    Spell::Poison,
    Spell::Recharge,
];

#[derive(Debug, Clone, Default)]
enum GameStatus {
    #[default]
    Ongoing,
    PlayerWon(bool),
}

#[derive(Debug, Default)]
struct GameState {
    player: Character,
    opp: Character,
    status: GameStatus,
    spell_sequence: Vec<Spell>,
    mana_spent: u32,
}

impl PartialEq for GameState {
    fn eq(&self, other: &Self) -> bool {
        self.mana_spent == other.mana_spent
    }
}

impl Eq for GameState {}

impl Ord for GameState {
    fn cmp(&self, other: &Self) -> Ordering {
        other.mana_spent.cmp(&self.mana_spent) // Reversed
    }
}

impl PartialOrd for GameState {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

fn next_states(state: &GameState) -> Vec<GameState> {
    let mut player = state.player.clone();
    let mut opp = state.opp.clone();

    player.apply_effects();
    opp.apply_effects();

    if opp.hp < 1 {
        return vec![GameState {
            player: player,
            opp: opp,
            status: GameStatus::PlayerWon(true),
            mana_spent: state.mana_spent,
            spell_sequence: state.spell_sequence.clone(),
        }];
    }

    let mut all_results = vec![];
    for spell in SPELLS.iter() {
        let mut player = player.clone();
        let mut opp = opp.clone();
        let cast_successful = match spell {
            Spell::MagicMissile => player.cast_magic_missile(&mut opp),
            Spell::Drain => player.cast_drain(&mut opp),
            Spell::Shield => player.cast_shield(),
            Spell::Poison => player.cast_poison(&mut opp),
            Spell::Recharge => player.cast_recharge(),
        };
        if cast_successful {
            let mut spell_sequence = state.spell_sequence.clone();
            spell_sequence.push(spell.clone());
            let mana_cost = match spell {
                Spell::MagicMissile => MAGIC_MISSILE_MANA_COST,
                Spell::Drain => DRAIN_MANA_COST,
                Spell::Shield => SHIELD_MANA_COST,
                Spell::Poison => POISON_MANA_COST,
                Spell::Recharge => RECHARGE_MANA_COST,
            };
            let mana_spent = state.mana_spent.saturating_add(mana_cost);

            player.apply_effects();
            opp.apply_effects();

            if opp.hp < 1 {
                all_results.push(GameState {
                    player: player,
                    opp: opp,
                    status: GameStatus::PlayerWon(true),
                    mana_spent: mana_spent,
                    spell_sequence: spell_sequence,
                });
                continue;
            }

            opp.attack(&mut player);

            if player.hp < 1 {
                all_results.push(GameState {
                    player: player,
                    opp: opp,
                    status: GameStatus::PlayerWon(false),
                    mana_spent: mana_spent,
                    spell_sequence: spell_sequence.clone(),
                });
                continue;
            }

            all_results.push(GameState {
                player: player,
                opp: opp,
                status: GameStatus::Ongoing,
                spell_sequence: spell_sequence,
                mana_spent: mana_spent,
            });
        }
    }

    all_results
}

fn get_terminal_state(state: GameState) -> Option<GameState> {
    let mut queue = BinaryHeap::new();
    queue.push(state);

    while let Some(state) = queue.pop() {
        if let GameStatus::PlayerWon(won) = state.status {
            if won {
                return Some(state);
            }
            continue;
        }
        let states = next_states(&state);
        for state in states {
            queue.push(state);
        }
    }
    None
}

fn main() {
    let initial_game_state = GameState {
        player: Character {
            hp: 50,
            mana: 500,
            ..Default::default()
        },
        opp: Character {
            hp: 51,
            damage: 9,
            ..Default::default()
        },
        ..Default::default()
    };

    dbg!(get_terminal_state(initial_game_state));
}
```

Which gives me a result of 900. This is apparently correct.

Now for Part 2, this is the question:

<document>
— Part Two —

On the next run through the game, you increase the difficulty to hard.

At the start of each player turn (before any other effects apply), you lose 1 hit point. If this brings you to or below 0 hit points, you lose.

With the same starting stats for you and the boss, what is the least amount of mana you can spend and still win the fight?
</document>

My idea was to simply change the beginning of the next_state() function to be:

```rust
fn next_states(state: &GameState) -> Vec<GameState> {
    let mut player = state.player.clone();
    let mut opp = state.opp.clone();

    player.hp = player.hp.saturating_sub(1);

    if player.hp < 1 {
        return vec![GameState {
            player: player,
            opp: opp,
            status: GameStatus::PlayerWon(false),
            mana_spent: state.mana_spent,
            spell_sequence: state.spell_sequence.clone(),
        }];
    }

    player.apply_effects();
    opp.apply_effects();

    … (the same as before)

```

Which makes sense to me, this give a result of 1242. But apparently the answer is wrong and it’s too high. What am I doing wrong? And are there any mistakes that you can spot?