y2015d22 new approach, no recursion, still broken
This commit is contained in:
parent
46deb678df
commit
00692c18df
@ -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]
|
||||||
|
Loading…
Reference in New Issue
Block a user