y2015d22 new approach, no recursion, still broken

This commit is contained in:
Fabian Schmidt 2024-11-15 16:01:09 +01:00
parent 46deb678df
commit 00692c18df

View File

@ -1,3 +1,5 @@
use core::panic;
pub fn process_part1(input: &str) -> u32 { pub fn process_part1(input: &str) -> u32 {
let mut boss = Character::default(); let mut boss = Character::default();
input.lines().for_each(|line| { input.lines().for_each(|line| {
@ -13,15 +15,62 @@ pub fn process_part1(input: &str) -> u32 {
mana: 500, mana: 500,
..Default::default() ..Default::default()
}; };
let mut used_mana = Vec::new(); // Initial state, nothing happened yet
for spell in Spell::get_all() { let start = RoundNode {
used_mana.push(round(player.clone(), boss.clone(), spell, 0)); player,
} boss,
spent_mana: 0,
state: State::Playing,
};
let mut smallest = u32::MAX; let mut smallest = u32::MAX;
for state in used_mana { let mut leafs = vec![start];
if state.get_value() < smallest { let mut test = 0;
smallest = state.get_value(); println!("start");
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() {
leafs.push(leaf.use_spell(spell));
}
}
} }
for leaf in &leafs {
if leaf.state == State::Win && leaf.spent_mana < smallest {
smallest = leaf.spent_mana;
}
//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();
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 smallest
} }
@ -30,139 +79,179 @@ pub fn process_part2(input: &str) -> u32 {
0 0
} }
fn round(mut player: Character, mut boss: Character, spell: Spell, spent_so_far: u32) -> State { #[derive(Debug, Clone, PartialEq, Eq)]
// Player turn
for effect in player.status_effects.iter_mut() {
if effect.name == SpellID::Recharge {
player.mana += effect.mana;
}
effect.duration -= 1;
if effect.duration > 0 && effect.name == SpellID::Shield {
player.armor = effect.armor;
} else if effect.name == SpellID::Shield {
player.armor = 0;
}
}
for effect in boss.status_effects.iter_mut() {
boss.hp = boss.hp.saturating_sub(effect.damage);
effect.duration -= 1;
}
boss.status_effects = boss
.status_effects
.into_iter()
.filter(|&effect| effect.duration > 0)
.collect::<Vec<Spell>>();
player.status_effects = player
.status_effects
.into_iter()
.filter(|&effect| effect.duration > 0)
.collect::<Vec<Spell>>();
let used_mana = spell.cost + spent_so_far;
let mut dmg = 0;
match spell.name {
SpellID::MagicMissile => {
player.mana = player.mana.saturating_sub(spell.cost);
dmg = spell.damage;
}
SpellID::Drain => {
player.mana = player.mana.saturating_sub(spell.cost);
dmg = spell.damage;
player.hp += spell.hp;
}
SpellID::Shield => {
player.mana = player.mana.saturating_sub(spell.cost);
player.status_effects.push(spell);
player.armor = spell.armor;
}
SpellID::Poison => {
player.mana = player.mana.saturating_sub(spell.cost);
boss.status_effects.push(spell);
}
SpellID::Recharge => {
player.mana = player.mana.saturating_sub(spell.cost);
player.status_effects.push(spell);
}
}
boss.hp = boss.hp.saturating_sub(dmg);
if boss.hp == 0 {
return State::Win(used_mana);
}
if player.mana == 0 {
return State::Loss(0);
}
// Boss turn
for effect in player.status_effects.iter_mut() {
if effect.name == SpellID::Recharge {
player.mana += effect.mana;
}
effect.duration -= 1;
if effect.duration > 0 && effect.name == SpellID::Shield {
player.armor = effect.armor;
} else if effect.name == SpellID::Shield {
player.armor = 0;
}
}
player.status_effects = player
.status_effects
.into_iter()
.filter(|&effect| effect.duration > 0)
.collect::<Vec<Spell>>();
for effect in boss.status_effects.iter_mut() {
boss.hp = boss.hp.saturating_sub(effect.damage);
effect.duration -= 1;
}
if boss.hp == 0 {
return State::Win(used_mana);
}
boss.status_effects = boss
.status_effects
.into_iter()
.filter(|&effect| effect.duration > 0)
.collect::<Vec<Spell>>();
let dmg = boss.hit_damage(&player);
player.hp = player.hp.saturating_sub(dmg);
if player.hp == 0 {
return State::Loss(0);
}
// next round
let mut smallest_used_mana = State::Playing(u32::MAX);
for spell in Spell::get_all() {
let next_state = round(player.clone(), boss.clone(), spell, used_mana);
match next_state {
State::Playing(mana) => {
if mana < smallest_used_mana.get_value() {
smallest_used_mana = State::Playing(mana);
}
}
State::Win(mana) => {
if mana < smallest_used_mana.get_value() {
smallest_used_mana = State::Win(mana);
}
}
State::Loss(_) => {}
};
}
smallest_used_mana
}
#[derive(Debug)]
enum State { enum State {
Playing(u32), Playing,
Win(u32), Win,
Loss(u32), Loss,
} }
impl State { #[derive(Debug, Clone)]
fn get_value(&self) -> u32 { struct RoundNode {
match self { player: Character,
State::Playing(value) => *value, boss: Character,
State::Win(value) => *value, spent_mana: u32,
State::Loss(value) => *value, state: State,
}
impl RoundNode {
fn use_spell(&self, spell: Spell) -> RoundNode {
if self.state != State::Playing {
eprintln!("State: {:#?}", self.state);
eprintln!("spell: {spell:#?}");
panic!("State should be Playing");
}
let mut player = self.player.clone();
let mut boss = self.boss.clone();
// Player turn
for effect in player.status_effects.iter_mut() {
if effect.name == SpellID::Recharge {
player.mana += effect.mana;
}
effect.duration -= 1;
if effect.duration > 0 && effect.name == SpellID::Shield {
player.armor = effect.armor;
} else if effect.name == SpellID::Shield {
player.armor = 0;
}
}
for effect in boss.status_effects.iter_mut() {
boss.hp = boss.hp.saturating_sub(effect.damage);
effect.duration -= 1;
}
boss.status_effects = boss
.status_effects
.into_iter()
.filter(|&effect| effect.duration > 0)
.collect::<Vec<Spell>>();
if boss.hp == 0 {
return RoundNode {
player,
boss,
spent_mana: self.spent_mana,
state: State::Win,
};
}
player.status_effects = player
.status_effects
.into_iter()
.filter(|&effect| effect.duration > 0)
.collect::<Vec<Spell>>();
let used_mana = spell.cost;
let mut dmg = 0;
match spell.name {
SpellID::MagicMissile => {
player.mana = player.mana.saturating_sub(spell.cost);
dmg = spell.damage;
}
SpellID::Drain => {
player.mana = player.mana.saturating_sub(spell.cost);
dmg = spell.damage;
player.hp += spell.hp;
}
SpellID::Shield => {
player.mana = player.mana.saturating_sub(spell.cost);
player.status_effects.push(spell);
player.armor = spell.armor;
}
SpellID::Poison => {
player.mana = player.mana.saturating_sub(spell.cost);
boss.status_effects.push(spell);
}
SpellID::Recharge => {
player.mana = player.mana.saturating_sub(spell.cost);
player.status_effects.push(spell);
}
}
boss.hp = boss.hp.saturating_sub(dmg);
if boss.hp == 0 {
return RoundNode {
player,
boss,
spent_mana: used_mana + self.spent_mana,
state: State::Win,
};
}
if player.mana == 0 {
return RoundNode {
player,
boss,
spent_mana: used_mana + self.spent_mana,
state: State::Loss,
};
}
// Boss turn
for effect in player.status_effects.iter_mut() {
if effect.name == SpellID::Recharge {
player.mana += effect.mana;
}
effect.duration -= 1;
if effect.duration > 0 && effect.name == SpellID::Shield {
player.armor = effect.armor;
} else if effect.name == SpellID::Shield {
player.armor = 0;
}
}
player.status_effects = player
.status_effects
.into_iter()
.filter(|&effect| effect.duration > 0)
.collect::<Vec<Spell>>();
for effect in boss.status_effects.iter_mut() {
boss.hp = boss.hp.saturating_sub(effect.damage);
effect.duration -= 1;
}
if boss.hp == 0 {
return RoundNode {
player,
boss,
spent_mana: used_mana + self.spent_mana,
state: State::Win,
};
}
boss.status_effects = boss
.status_effects
.into_iter()
.filter(|&effect| effect.duration > 0)
.collect::<Vec<Spell>>();
let dmg = boss.hit_damage(&player);
player.hp = player.hp.saturating_sub(dmg);
if player.hp == 0 {
return RoundNode {
player,
boss,
spent_mana: used_mana + self.spent_mana,
state: State::Loss,
};
}
RoundNode {
player,
boss,
spent_mana: used_mana + self.spent_mana,
state: State::Playing,
} }
} }
//fn get_leafs(&self) -> Vec<RoundNode> {
// let mut leafs = Vec::new();
// match self.state {
// State::Playing => {
// if self.next_rounds.is_empty() {
// return vec![self.clone()];
// }
// let mut round_leafs = Vec::new();
// for round in self.next_rounds.clone() {
// round_leafs = round.get_leafs();
// }
// leafs.append(&mut round_leafs);
// leafs
// }
// State::Win => vec![self.clone()],
// State::Loss => vec![self.clone()],
// }
//}
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -273,17 +362,49 @@ mod tests {
mana: 250, mana: 250,
..Default::default() ..Default::default()
}; };
let mut used_mana = Vec::new(); // Initial state, nothing happened yet
for spell in Spell::get_all() { let start = RoundNode {
used_mana.push(round(player.clone(), boss.clone(), spell, 0)); player,
} boss,
let mut result = u32::MAX; spent_mana: 0,
for state in used_mana { state: State::Playing,
if state.get_value() < result { };
result = state.get_value(); 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));
}
}
} }
let mut all_done = true;
for leaf in &leafs {
if leaf.state == State::Win && leaf.spent_mana < smallest {
smallest = leaf.spent_mana;
}
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
);
}
if all_done {
break;
}
test += 1;
println!(" smallest: {smallest}");
} }
assert_eq!(result, 226); assert_eq!(smallest, 226);
} }
#[test] #[test]
@ -298,17 +419,49 @@ mod tests {
mana: 250, mana: 250,
..Default::default() ..Default::default()
}; };
let mut used_mana = Vec::new(); // Initial state, nothing happened yet
for spell in Spell::get_all() { let start = RoundNode {
used_mana.push(round(player.clone(), boss.clone(), spell, 0)); player,
} boss,
let mut result = u32::MAX; spent_mana: 0,
for state in used_mana { state: State::Playing,
if state.get_value() < result { };
result = state.get_value(); 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));
}
}
} }
let mut all_done = true;
for leaf in &leafs {
if leaf.state == State::Win && leaf.spent_mana < smallest {
smallest = leaf.spent_mana;
}
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
);
}
if all_done {
break;
}
test += 1;
println!(" smallest: {smallest}");
} }
assert_eq!(result, 641); assert_eq!(smallest, 641);
} }
#[test] #[test]