From 9329c9f77ad5e29b2c9f5ae8a3b30cf65bc91efd Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Mon, 2 Dec 2024 15:51:01 +0100 Subject: [PATCH] y2015d22 finally, alot of small things to watch out for --- justfile | 2 +- y2015/src/days/d22.rs | 220 ++++++++++++++++++++++++++++++++---------- 2 files changed, 168 insertions(+), 54 deletions(-) diff --git a/justfile b/justfile index 8ae16d2..78696dc 100644 --- a/justfile +++ b/justfile @@ -5,4 +5,4 @@ run year day: cargo run --package y{{year}} --bin d{{day}} test year day *part='1': - cargo test --package y{{year}} days::d{{day}}::tests::part{{part}} -- --nocapture + cargo test --package y{{year}} --lib days::d{{day}}::tests::part{{part}} -- --nocapture diff --git a/y2015/src/days/d22.rs b/y2015/src/days/d22.rs index e81b0f5..930ddc1 100644 --- a/y2015/src/days/d22.rs +++ b/y2015/src/days/d22.rs @@ -24,20 +24,20 @@ pub fn process_part1(input: &str) -> u32 { }; let mut smallest = u32::MAX; let mut leafs = vec![start]; - let mut test = 0; println!("start"); + // Playing, Win, Loss + let mut stats = (0, 0, 0); loop { - println!("iter: {test},"); let current_leafs = leafs.clone(); - println!("leaf count: {}", current_leafs.len()); leafs.clear(); // create potential rounds for leaf in current_leafs { if leaf.state == State::Playing { - //println!("try for leaf"); for spell in Spell::get_all() { match spell.name { - SpellID::MagicMissile | SpellID::Drain => {} + SpellID::MagicMissile | SpellID::Drain => { + leafs.push(leaf.use_spell(spell, false)); + } SpellID::Shield => { if !leaf .player @@ -45,68 +45,151 @@ pub fn process_part1(input: &str) -> u32 { .iter() .any(|effect| effect.name == SpellID::Shield && effect.duration > 1) { - continue; + leafs.push(leaf.use_spell(spell, false)); } } SpellID::Poison => { if !leaf - .player + .boss .status_effects .iter() .any(|effect| effect.name == SpellID::Poison && effect.duration > 1) { - continue; + leafs.push(leaf.use_spell(spell, false)); } } SpellID::Recharge => { if !leaf.player.status_effects.iter().any(|effect| { effect.name == SpellID::Recharge && effect.duration > 1 }) { - continue; + leafs.push(leaf.use_spell(spell, false)); } } } - leafs.push(leaf.use_spell(spell)); } } + //leafs.iter().for_each(|leaf| println!("{:?}", leaf)); } + println!("leaf count: {}", leafs.len()); + stats.0 = 0; for leaf in &leafs { if leaf.state == State::Win && leaf.spent_mana < smallest { smallest = leaf.spent_mana; } + match leaf.state { + State::Playing => stats.0 += 1, + State::Win => stats.1 += 1, + State::Loss => stats.2 += 1, + }; //println!( // "player hp: {}, boss hp: {}, spent mana so far: {}", // leaf.player.hp, leaf.boss.hp, leaf.spent_mana //); } - leafs = leafs - .clone() - .iter() - .filter(|&leaf| leaf.state == State::Playing || leaf.spent_mana <= smallest) - .map(|leaf| leaf.to_owned()) - .collect(); + println!("Playing, win, loss: {stats:?}"); + leafs.retain(|leaf| leaf.state == State::Playing && leaf.spent_mana < smallest); if leafs.is_empty() { break; } - // already know it's higher than this - if smallest <= 2000 { - let mut count_losses = 0; - for leaf in leafs { - if leaf.state == State::Loss { - count_losses += 1; - } - } - println!("Losses: {count_losses}"); - break; - } - test += 1; println!(" smallest: {smallest}"); } smallest } pub fn process_part2(input: &str) -> u32 { - 0 + let mut boss = Character::default(); + input.lines().for_each(|line| { + let (attribute, value) = line.split_once(": ").unwrap(); + if attribute == "Hit Points" { + boss.hp = value.parse::().unwrap(); + } else if attribute == "Damage" { + boss.damage = value.parse::().unwrap(); + } + }); + let player = Character { + hp: 50, + mana: 500, + ..Default::default() + }; + // Initial state, nothing happened yet + let start = RoundNode { + player, + boss, + spent_mana: 0, + state: State::Playing, + }; + let mut smallest = u32::MAX; + let mut leafs = vec![start]; + println!("start"); + // Playing, Win, Loss + let mut stats = (0, 0, 0); + loop { + let current_leafs = leafs.clone(); + leafs.clear(); + // create potential rounds + for leaf in current_leafs { + if leaf.state == State::Playing { + for spell in Spell::get_all() { + match spell.name { + SpellID::MagicMissile | SpellID::Drain => { + leafs.push(leaf.use_spell(spell, true)); + } + SpellID::Shield => { + if !leaf + .player + .status_effects + .iter() + .any(|effect| effect.name == SpellID::Shield && effect.duration > 1) + { + leafs.push(leaf.use_spell(spell, true)); + } + } + SpellID::Poison => { + if !leaf + .boss + .status_effects + .iter() + .any(|effect| effect.name == SpellID::Poison && effect.duration > 1) + { + leafs.push(leaf.use_spell(spell, true)); + } + } + SpellID::Recharge => { + if !leaf.player.status_effects.iter().any(|effect| { + effect.name == SpellID::Recharge && effect.duration > 1 + }) { + leafs.push(leaf.use_spell(spell, true)); + } + } + } + } + } + //leafs.iter().for_each(|leaf| println!("{:?}", leaf)); + } + println!("leaf count: {}", leafs.len()); + stats.0 = 0; + for leaf in &leafs { + if leaf.state == State::Win && leaf.spent_mana < smallest { + smallest = leaf.spent_mana; + } + match leaf.state { + State::Playing => stats.0 += 1, + State::Win => stats.1 += 1, + State::Loss => stats.2 += 1, + }; + //println!( + // "player hp: {}, boss hp: {}, spent mana so far: {}", + // leaf.player.hp, leaf.boss.hp, leaf.spent_mana + //); + } + println!("Playing, win, loss: {stats:?}"); + leafs.retain(|leaf| leaf.state == State::Playing && leaf.spent_mana < smallest); + if leafs.is_empty() { + break; + } + println!(" smallest: {smallest}"); + } + smallest } #[derive(Debug, Clone, PartialEq, Eq)] @@ -125,7 +208,7 @@ struct RoundNode { } impl RoundNode { - fn use_spell(&self, spell: Spell) -> RoundNode { + fn use_spell(&self, spell: Spell, hard_mode: bool) -> RoundNode { if self.state != State::Playing { eprintln!("State: {:#?}", self.state); eprintln!("spell: {spell:#?}"); @@ -133,10 +216,25 @@ impl RoundNode { } let mut player = self.player.clone(); let mut boss = self.boss.clone(); + let mut recharge_actif = false; + if hard_mode { + player.hp -= 1; + } + if player.hp == 0 { + return RoundNode { + player, + boss, + spent_mana: spell.cost + self.spent_mana, + state: State::Loss, + }; + } // Player turn for effect in player.status_effects.iter_mut() { if effect.name == SpellID::Recharge { player.mana += effect.mana; + if effect.duration > 1 { + recharge_actif = true; + } } effect.duration -= 1; if effect.duration > 0 && effect.name == SpellID::Shield { @@ -167,8 +265,15 @@ impl RoundNode { .into_iter() .filter(|&effect| effect.duration > 0) .collect::>(); - let used_mana = spell.cost; let mut dmg = 0; + if player.mana < spell.cost { + return RoundNode { + player, + boss, + spent_mana: spell.cost + self.spent_mana, + state: State::Loss, + }; + } match spell.name { SpellID::MagicMissile => { player.mana = player.mana.saturating_sub(spell.cost); @@ -198,15 +303,15 @@ impl RoundNode { return RoundNode { player, boss, - spent_mana: used_mana + self.spent_mana, + spent_mana: spell.cost + self.spent_mana, state: State::Win, }; } - if player.mana == 0 { + if player.mana == 0 && !recharge_actif { return RoundNode { player, boss, - spent_mana: used_mana + self.spent_mana, + spent_mana: spell.cost + self.spent_mana, state: State::Loss, }; } @@ -236,7 +341,7 @@ impl RoundNode { return RoundNode { player, boss, - spent_mana: used_mana + self.spent_mana, + spent_mana: spell.cost + self.spent_mana, state: State::Win, }; } @@ -252,14 +357,14 @@ impl RoundNode { return RoundNode { player, boss, - spent_mana: used_mana + self.spent_mana, + spent_mana: spell.cost + self.spent_mana, state: State::Loss, }; } RoundNode { player, boss, - spent_mana: used_mana + self.spent_mana, + spent_mana: spell.cost + self.spent_mana, state: State::Playing, } } @@ -401,17 +506,15 @@ mod tests { }; let mut smallest = u32::MAX; let mut leafs = vec![start]; - let mut test = 0; println!("start"); loop { - println!("iter: {test},"); let current_leafs = leafs.clone(); leafs.clear(); // create potential rounds for leaf in current_leafs { if leaf.state == State::Playing { for spell in Spell::get_all() { - leafs.push(leaf.use_spell(spell)); + leafs.push(leaf.use_spell(spell, false)); } } } @@ -423,15 +526,14 @@ mod tests { if leaf.state == State::Playing { all_done = false; } - println!( - "player hp: {}, boss hp: {}, spent mana so far: {}", - leaf.player.hp, leaf.boss.hp, leaf.spent_mana - ); + //println!( + // "player hp: {}, boss hp: {}, spent mana so far: {}", + // leaf.player.hp, leaf.boss.hp, leaf.spent_mana + //); } if all_done { break; } - test += 1; println!(" smallest: {smallest}"); } assert_eq!(smallest, 226); @@ -458,17 +560,15 @@ mod tests { }; let mut smallest = u32::MAX; let mut leafs = vec![start]; - let mut test = 0; println!("start"); loop { - println!("iter: {test},"); let current_leafs = leafs.clone(); leafs.clear(); // create potential rounds for leaf in current_leafs { if leaf.state == State::Playing { for spell in Spell::get_all() { - leafs.push(leaf.use_spell(spell)); + leafs.push(leaf.use_spell(spell, false)); } } } @@ -480,20 +580,34 @@ mod tests { if leaf.state == State::Playing { all_done = false; } - println!( - "player hp: {}, boss hp: {}, spent mana so far: {}", - leaf.player.hp, leaf.boss.hp, leaf.spent_mana - ); + //println!( + // "player hp: {}, boss hp: {}, spent mana so far: {}", + // leaf.player.hp, leaf.boss.hp, leaf.spent_mana + //); } if all_done { break; } - test += 1; println!(" smallest: {smallest}"); } assert_eq!(smallest, 641); } + #[allow(dead_code)] + fn init_characters() -> (Character, Character) { + let player = Character { + hp: 50, + mana: 500, + ..Default::default() + }; + let boss = Character { + hp: 14, + damage: 8, + ..Default::default() + }; + (player, boss) + } + #[test] fn part2() { let result = process_part2("");