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;
|
||||
}
|
||||
}
|
||||
|
||||
#[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 d21;
|
||||
|
||||
pub mod d22;
|
||||
|
Loading…
Reference in New Issue
Block a user