y2015d22 finally, alot of small things to watch out for
This commit is contained in:
parent
67d3a56011
commit
9329c9f77a
2
justfile
2
justfile
@ -5,4 +5,4 @@ run year day:
|
|||||||
cargo run --package y{{year}} --bin d{{day}}
|
cargo run --package y{{year}} --bin d{{day}}
|
||||||
|
|
||||||
test year day *part='1':
|
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
|
||||||
|
@ -24,20 +24,20 @@ pub fn process_part1(input: &str) -> u32 {
|
|||||||
};
|
};
|
||||||
let mut smallest = u32::MAX;
|
let mut smallest = u32::MAX;
|
||||||
let mut leafs = vec![start];
|
let mut leafs = vec![start];
|
||||||
let mut test = 0;
|
|
||||||
println!("start");
|
println!("start");
|
||||||
|
// Playing, Win, Loss
|
||||||
|
let mut stats = (0, 0, 0);
|
||||||
loop {
|
loop {
|
||||||
println!("iter: {test},");
|
|
||||||
let current_leafs = leafs.clone();
|
let current_leafs = leafs.clone();
|
||||||
println!("leaf count: {}", current_leafs.len());
|
|
||||||
leafs.clear();
|
leafs.clear();
|
||||||
// create potential rounds
|
// create potential rounds
|
||||||
for leaf in current_leafs {
|
for leaf in current_leafs {
|
||||||
if leaf.state == State::Playing {
|
if leaf.state == State::Playing {
|
||||||
//println!("try for leaf");
|
|
||||||
for spell in Spell::get_all() {
|
for spell in Spell::get_all() {
|
||||||
match spell.name {
|
match spell.name {
|
||||||
SpellID::MagicMissile | SpellID::Drain => {}
|
SpellID::MagicMissile | SpellID::Drain => {
|
||||||
|
leafs.push(leaf.use_spell(spell, false));
|
||||||
|
}
|
||||||
SpellID::Shield => {
|
SpellID::Shield => {
|
||||||
if !leaf
|
if !leaf
|
||||||
.player
|
.player
|
||||||
@ -45,68 +45,151 @@ pub fn process_part1(input: &str) -> u32 {
|
|||||||
.iter()
|
.iter()
|
||||||
.any(|effect| effect.name == SpellID::Shield && effect.duration > 1)
|
.any(|effect| effect.name == SpellID::Shield && effect.duration > 1)
|
||||||
{
|
{
|
||||||
continue;
|
leafs.push(leaf.use_spell(spell, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SpellID::Poison => {
|
SpellID::Poison => {
|
||||||
if !leaf
|
if !leaf
|
||||||
.player
|
.boss
|
||||||
.status_effects
|
.status_effects
|
||||||
.iter()
|
.iter()
|
||||||
.any(|effect| effect.name == SpellID::Poison && effect.duration > 1)
|
.any(|effect| effect.name == SpellID::Poison && effect.duration > 1)
|
||||||
{
|
{
|
||||||
continue;
|
leafs.push(leaf.use_spell(spell, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SpellID::Recharge => {
|
SpellID::Recharge => {
|
||||||
if !leaf.player.status_effects.iter().any(|effect| {
|
if !leaf.player.status_effects.iter().any(|effect| {
|
||||||
effect.name == SpellID::Recharge && effect.duration > 1
|
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 {
|
for leaf in &leafs {
|
||||||
if leaf.state == State::Win && leaf.spent_mana < smallest {
|
if leaf.state == State::Win && leaf.spent_mana < smallest {
|
||||||
smallest = leaf.spent_mana;
|
smallest = leaf.spent_mana;
|
||||||
}
|
}
|
||||||
|
match leaf.state {
|
||||||
|
State::Playing => stats.0 += 1,
|
||||||
|
State::Win => stats.1 += 1,
|
||||||
|
State::Loss => stats.2 += 1,
|
||||||
|
};
|
||||||
//println!(
|
//println!(
|
||||||
// "player hp: {}, boss hp: {}, spent mana so far: {}",
|
// "player hp: {}, boss hp: {}, spent mana so far: {}",
|
||||||
// leaf.player.hp, leaf.boss.hp, leaf.spent_mana
|
// leaf.player.hp, leaf.boss.hp, leaf.spent_mana
|
||||||
//);
|
//);
|
||||||
}
|
}
|
||||||
leafs = leafs
|
println!("Playing, win, loss: {stats:?}");
|
||||||
.clone()
|
leafs.retain(|leaf| leaf.state == State::Playing && leaf.spent_mana < smallest);
|
||||||
.iter()
|
|
||||||
.filter(|&leaf| leaf.state == State::Playing || leaf.spent_mana <= smallest)
|
|
||||||
.map(|leaf| leaf.to_owned())
|
|
||||||
.collect();
|
|
||||||
if leafs.is_empty() {
|
if leafs.is_empty() {
|
||||||
break;
|
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}");
|
println!(" smallest: {smallest}");
|
||||||
}
|
}
|
||||||
smallest
|
smallest
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_part2(input: &str) -> u32 {
|
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::<u32>().unwrap();
|
||||||
|
} else if attribute == "Damage" {
|
||||||
|
boss.damage = value.parse::<u32>().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)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@ -125,7 +208,7 @@ struct RoundNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
if self.state != State::Playing {
|
||||||
eprintln!("State: {:#?}", self.state);
|
eprintln!("State: {:#?}", self.state);
|
||||||
eprintln!("spell: {spell:#?}");
|
eprintln!("spell: {spell:#?}");
|
||||||
@ -133,10 +216,25 @@ impl RoundNode {
|
|||||||
}
|
}
|
||||||
let mut player = self.player.clone();
|
let mut player = self.player.clone();
|
||||||
let mut boss = self.boss.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
|
// Player turn
|
||||||
for effect in player.status_effects.iter_mut() {
|
for effect in player.status_effects.iter_mut() {
|
||||||
if effect.name == SpellID::Recharge {
|
if effect.name == SpellID::Recharge {
|
||||||
player.mana += effect.mana;
|
player.mana += effect.mana;
|
||||||
|
if effect.duration > 1 {
|
||||||
|
recharge_actif = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
effect.duration -= 1;
|
effect.duration -= 1;
|
||||||
if effect.duration > 0 && effect.name == SpellID::Shield {
|
if effect.duration > 0 && effect.name == SpellID::Shield {
|
||||||
@ -167,8 +265,15 @@ impl RoundNode {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|&effect| effect.duration > 0)
|
.filter(|&effect| effect.duration > 0)
|
||||||
.collect::<Vec<Spell>>();
|
.collect::<Vec<Spell>>();
|
||||||
let used_mana = spell.cost;
|
|
||||||
let mut dmg = 0;
|
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 {
|
match spell.name {
|
||||||
SpellID::MagicMissile => {
|
SpellID::MagicMissile => {
|
||||||
player.mana = player.mana.saturating_sub(spell.cost);
|
player.mana = player.mana.saturating_sub(spell.cost);
|
||||||
@ -198,15 +303,15 @@ impl RoundNode {
|
|||||||
return RoundNode {
|
return RoundNode {
|
||||||
player,
|
player,
|
||||||
boss,
|
boss,
|
||||||
spent_mana: used_mana + self.spent_mana,
|
spent_mana: spell.cost + self.spent_mana,
|
||||||
state: State::Win,
|
state: State::Win,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if player.mana == 0 {
|
if player.mana == 0 && !recharge_actif {
|
||||||
return RoundNode {
|
return RoundNode {
|
||||||
player,
|
player,
|
||||||
boss,
|
boss,
|
||||||
spent_mana: used_mana + self.spent_mana,
|
spent_mana: spell.cost + self.spent_mana,
|
||||||
state: State::Loss,
|
state: State::Loss,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -236,7 +341,7 @@ impl RoundNode {
|
|||||||
return RoundNode {
|
return RoundNode {
|
||||||
player,
|
player,
|
||||||
boss,
|
boss,
|
||||||
spent_mana: used_mana + self.spent_mana,
|
spent_mana: spell.cost + self.spent_mana,
|
||||||
state: State::Win,
|
state: State::Win,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -252,14 +357,14 @@ impl RoundNode {
|
|||||||
return RoundNode {
|
return RoundNode {
|
||||||
player,
|
player,
|
||||||
boss,
|
boss,
|
||||||
spent_mana: used_mana + self.spent_mana,
|
spent_mana: spell.cost + self.spent_mana,
|
||||||
state: State::Loss,
|
state: State::Loss,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
RoundNode {
|
RoundNode {
|
||||||
player,
|
player,
|
||||||
boss,
|
boss,
|
||||||
spent_mana: used_mana + self.spent_mana,
|
spent_mana: spell.cost + self.spent_mana,
|
||||||
state: State::Playing,
|
state: State::Playing,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -401,17 +506,15 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let mut smallest = u32::MAX;
|
let mut smallest = u32::MAX;
|
||||||
let mut leafs = vec![start];
|
let mut leafs = vec![start];
|
||||||
let mut test = 0;
|
|
||||||
println!("start");
|
println!("start");
|
||||||
loop {
|
loop {
|
||||||
println!("iter: {test},");
|
|
||||||
let current_leafs = leafs.clone();
|
let current_leafs = leafs.clone();
|
||||||
leafs.clear();
|
leafs.clear();
|
||||||
// create potential rounds
|
// create potential rounds
|
||||||
for leaf in current_leafs {
|
for leaf in current_leafs {
|
||||||
if leaf.state == State::Playing {
|
if leaf.state == State::Playing {
|
||||||
for spell in Spell::get_all() {
|
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 {
|
if leaf.state == State::Playing {
|
||||||
all_done = false;
|
all_done = false;
|
||||||
}
|
}
|
||||||
println!(
|
//println!(
|
||||||
"player hp: {}, boss hp: {}, spent mana so far: {}",
|
// "player hp: {}, boss hp: {}, spent mana so far: {}",
|
||||||
leaf.player.hp, leaf.boss.hp, leaf.spent_mana
|
// leaf.player.hp, leaf.boss.hp, leaf.spent_mana
|
||||||
);
|
//);
|
||||||
}
|
}
|
||||||
if all_done {
|
if all_done {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
test += 1;
|
|
||||||
println!(" smallest: {smallest}");
|
println!(" smallest: {smallest}");
|
||||||
}
|
}
|
||||||
assert_eq!(smallest, 226);
|
assert_eq!(smallest, 226);
|
||||||
@ -458,17 +560,15 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let mut smallest = u32::MAX;
|
let mut smallest = u32::MAX;
|
||||||
let mut leafs = vec![start];
|
let mut leafs = vec![start];
|
||||||
let mut test = 0;
|
|
||||||
println!("start");
|
println!("start");
|
||||||
loop {
|
loop {
|
||||||
println!("iter: {test},");
|
|
||||||
let current_leafs = leafs.clone();
|
let current_leafs = leafs.clone();
|
||||||
leafs.clear();
|
leafs.clear();
|
||||||
// create potential rounds
|
// create potential rounds
|
||||||
for leaf in current_leafs {
|
for leaf in current_leafs {
|
||||||
if leaf.state == State::Playing {
|
if leaf.state == State::Playing {
|
||||||
for spell in Spell::get_all() {
|
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 {
|
if leaf.state == State::Playing {
|
||||||
all_done = false;
|
all_done = false;
|
||||||
}
|
}
|
||||||
println!(
|
//println!(
|
||||||
"player hp: {}, boss hp: {}, spent mana so far: {}",
|
// "player hp: {}, boss hp: {}, spent mana so far: {}",
|
||||||
leaf.player.hp, leaf.boss.hp, leaf.spent_mana
|
// leaf.player.hp, leaf.boss.hp, leaf.spent_mana
|
||||||
);
|
//);
|
||||||
}
|
}
|
||||||
if all_done {
|
if all_done {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
test += 1;
|
|
||||||
println!(" smallest: {smallest}");
|
println!(" smallest: {smallest}");
|
||||||
}
|
}
|
||||||
assert_eq!(smallest, 641);
|
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]
|
#[test]
|
||||||
fn part2() {
|
fn part2() {
|
||||||
let result = process_part2("");
|
let result = process_part2("");
|
||||||
|
Loading…
Reference in New Issue
Block a user