y2015d22 tests work, solution incorrect and slow
This commit is contained in:
parent
5015474745
commit
33e6f1241c
8
justfile
Normal file
8
justfile
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
gen year day:
|
||||||
|
cargo run -- {{year}} {{day}}
|
||||||
|
|
||||||
|
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
|
2
y2015/resources/22_input.txt
Normal file
2
y2015/resources/22_input.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Hit Points: 71
|
||||||
|
Damage: 10
|
20
y2015/src/bin/d22.rs
Normal file
20
y2015/src/bin/d22.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use std::fs;
|
||||||
|
|
||||||
|
use y2015::days::d22;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
part1();
|
||||||
|
part2();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part1() {
|
||||||
|
let root = env!("CARGO_MANIFEST_DIR");
|
||||||
|
let content = fs::read_to_string(format!("{root}/resources/22_input.txt")).unwrap();
|
||||||
|
println!("{}", d22::process_part1(&content));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2() {
|
||||||
|
let root = env!("CARGO_MANIFEST_DIR");
|
||||||
|
let content = fs::read_to_string(format!("{root}/resources/22_input.txt")).unwrap();
|
||||||
|
println!("{}", d22::process_part2(&content));
|
||||||
|
}
|
@ -267,24 +267,3 @@ impl Character {
|
|||||||
self.items_value += item.cost;
|
self.items_value += item.cost;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
const INPUT: &str = "Hit Points: 12
|
|
||||||
Damage: 7
|
|
||||||
Armor: 2";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn part1() {
|
|
||||||
let result = process_part1(INPUT);
|
|
||||||
assert_eq!(result, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn part2() {
|
|
||||||
let result = process_part2(INPUT);
|
|
||||||
assert_eq!(result, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
319
y2015/src/days/d22.rs
Normal file
319
y2015/src/days/d22.rs
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
pub fn process_part1(input: &str) -> u32 {
|
||||||
|
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()
|
||||||
|
};
|
||||||
|
let mut used_mana = Vec::new();
|
||||||
|
for spell in Spell::get_all() {
|
||||||
|
used_mana.push(round(player.clone(), boss.clone(), spell, 0));
|
||||||
|
}
|
||||||
|
let mut smallest = u32::MAX;
|
||||||
|
for state in used_mana {
|
||||||
|
if state.get_value() < smallest {
|
||||||
|
smallest = state.get_value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
smallest
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_part2(input: &str) -> u32 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn round(mut player: Character, mut boss: Character, spell: Spell, spent_so_far: u32) -> State {
|
||||||
|
// 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 {
|
||||||
|
Playing(u32),
|
||||||
|
Win(u32),
|
||||||
|
Loss(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn get_value(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
State::Playing(value) => *value,
|
||||||
|
State::Win(value) => *value,
|
||||||
|
State::Loss(value) => *value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum SpellID {
|
||||||
|
MagicMissile,
|
||||||
|
Drain,
|
||||||
|
Shield,
|
||||||
|
Poison,
|
||||||
|
Recharge,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct Spell {
|
||||||
|
name: SpellID,
|
||||||
|
hp: u32,
|
||||||
|
damage: u32,
|
||||||
|
mana: u32,
|
||||||
|
armor: u32,
|
||||||
|
duration: u32,
|
||||||
|
cost: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Spell {
|
||||||
|
fn get_all() -> Vec<Spell> {
|
||||||
|
vec![
|
||||||
|
Spell {
|
||||||
|
name: SpellID::MagicMissile,
|
||||||
|
hp: 0,
|
||||||
|
damage: 4,
|
||||||
|
mana: 0,
|
||||||
|
armor: 0,
|
||||||
|
duration: 0,
|
||||||
|
cost: 53,
|
||||||
|
},
|
||||||
|
Spell {
|
||||||
|
name: SpellID::Drain,
|
||||||
|
hp: 2,
|
||||||
|
damage: 2,
|
||||||
|
mana: 0,
|
||||||
|
armor: 0,
|
||||||
|
duration: 0,
|
||||||
|
cost: 73,
|
||||||
|
},
|
||||||
|
Spell {
|
||||||
|
name: SpellID::Shield,
|
||||||
|
hp: 0,
|
||||||
|
damage: 0,
|
||||||
|
mana: 0,
|
||||||
|
armor: 7,
|
||||||
|
duration: 6,
|
||||||
|
cost: 113,
|
||||||
|
},
|
||||||
|
Spell {
|
||||||
|
name: SpellID::Poison,
|
||||||
|
hp: 0,
|
||||||
|
damage: 3,
|
||||||
|
mana: 0,
|
||||||
|
armor: 0,
|
||||||
|
duration: 6,
|
||||||
|
cost: 173,
|
||||||
|
},
|
||||||
|
Spell {
|
||||||
|
name: SpellID::Recharge,
|
||||||
|
hp: 0,
|
||||||
|
damage: 0,
|
||||||
|
mana: 101,
|
||||||
|
armor: 0,
|
||||||
|
duration: 5,
|
||||||
|
cost: 229,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
struct Character {
|
||||||
|
hp: u32,
|
||||||
|
damage: u32,
|
||||||
|
armor: u32,
|
||||||
|
mana: u32,
|
||||||
|
status_effects: Vec<Spell>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Character {
|
||||||
|
fn hit_damage(&self, other: &Self) -> u32 {
|
||||||
|
let damage = self.damage.saturating_sub(other.armor);
|
||||||
|
if damage > 0 {
|
||||||
|
damage
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_1() {
|
||||||
|
let boss = Character {
|
||||||
|
hp: 13,
|
||||||
|
damage: 8,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let player = Character {
|
||||||
|
hp: 10,
|
||||||
|
mana: 250,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut used_mana = Vec::new();
|
||||||
|
for spell in Spell::get_all() {
|
||||||
|
used_mana.push(round(player.clone(), boss.clone(), spell, 0));
|
||||||
|
}
|
||||||
|
let mut result = u32::MAX;
|
||||||
|
for state in used_mana {
|
||||||
|
if state.get_value() < result {
|
||||||
|
result = state.get_value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(result, 226);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_2() {
|
||||||
|
let boss = Character {
|
||||||
|
hp: 14,
|
||||||
|
damage: 8,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let player = Character {
|
||||||
|
hp: 10,
|
||||||
|
mana: 250,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut used_mana = Vec::new();
|
||||||
|
for spell in Spell::get_all() {
|
||||||
|
used_mana.push(round(player.clone(), boss.clone(), spell, 0));
|
||||||
|
}
|
||||||
|
let mut result = u32::MAX;
|
||||||
|
for state in used_mana {
|
||||||
|
if state.get_value() < result {
|
||||||
|
result = state.get_value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(result, 641);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2() {
|
||||||
|
let result = process_part2("");
|
||||||
|
assert_eq!(result, 0);
|
||||||
|
}
|
||||||
|
}
|
@ -22,3 +22,5 @@ pub mod d19;
|
|||||||
pub mod d20;
|
pub mod d20;
|
||||||
|
|
||||||
pub mod d21;
|
pub mod d21;
|
||||||
|
|
||||||
|
pub mod d22;
|
||||||
|
Loading…
Reference in New Issue
Block a user