y2015d21
This commit is contained in:
parent
aa28df24b3
commit
1d9c848525
3
y2015/resources/21_input.txt
Normal file
3
y2015/resources/21_input.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Hit Points: 103
|
||||||
|
Damage: 9
|
||||||
|
Armor: 2
|
20
y2015/src/bin/d21.rs
Normal file
20
y2015/src/bin/d21.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use std::fs;
|
||||||
|
|
||||||
|
use y2015::days::d21;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
part1();
|
||||||
|
part2();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part1() {
|
||||||
|
let root = env!("CARGO_MANIFEST_DIR");
|
||||||
|
let content = fs::read_to_string(format!("{root}/resources/21_input.txt")).unwrap();
|
||||||
|
println!("{}", d21::process_part1(&content));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2() {
|
||||||
|
let root = env!("CARGO_MANIFEST_DIR");
|
||||||
|
let content = fs::read_to_string(format!("{root}/resources/21_input.txt")).unwrap();
|
||||||
|
println!("{}", d21::process_part2(&content));
|
||||||
|
}
|
290
y2015/src/days/d21.rs
Normal file
290
y2015/src/days/d21.rs
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
use core::panic;
|
||||||
|
|
||||||
|
// 1 Weapon
|
||||||
|
// 0-1 Armor
|
||||||
|
// 0-2 Rings
|
||||||
|
const SHOP: &str = "Weapons: Cost Damage Armor
|
||||||
|
Dagger 8 4 0
|
||||||
|
Shortsword 10 5 0
|
||||||
|
Warhammer 25 6 0
|
||||||
|
Longsword 40 7 0
|
||||||
|
Greataxe 74 8 0
|
||||||
|
|
||||||
|
Armor: Cost Damage Armor
|
||||||
|
Leather 13 0 1
|
||||||
|
Chainmail 31 0 2
|
||||||
|
Splintmail 53 0 3
|
||||||
|
Bandedmail 75 0 4
|
||||||
|
Platemail 102 0 5
|
||||||
|
|
||||||
|
Rings: Cost Damage Armor
|
||||||
|
Damage +1 25 1 0
|
||||||
|
Damage +2 50 2 0
|
||||||
|
Damage +3 100 3 0
|
||||||
|
Defense +1 20 0 1
|
||||||
|
Defense +2 40 0 2
|
||||||
|
Defense +3 80 0 3";
|
||||||
|
|
||||||
|
pub fn process_part1(input: &str) -> u32 {
|
||||||
|
let player = Character {
|
||||||
|
hp: 100,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut boss = Character::default();
|
||||||
|
input.lines().for_each(|line| {
|
||||||
|
let (attribute, value) = line.split_once(": ").unwrap();
|
||||||
|
match attribute {
|
||||||
|
"Hit Points" => {
|
||||||
|
boss.hp = value.parse::<u32>().unwrap();
|
||||||
|
}
|
||||||
|
"Damage" => {
|
||||||
|
boss.damage = value.parse::<u32>().unwrap();
|
||||||
|
}
|
||||||
|
"Armor" => {
|
||||||
|
boss.armor = value.parse::<u32>().unwrap();
|
||||||
|
}
|
||||||
|
_ => panic!("Should not happen"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut weapons = Vec::new();
|
||||||
|
let mut armors = Vec::new();
|
||||||
|
let mut rings = Vec::new();
|
||||||
|
|
||||||
|
SHOP.split("\n\n").for_each(|category| {
|
||||||
|
let mut items = category.lines();
|
||||||
|
let (category_name, _) = items.next().unwrap().split_once(":").unwrap();
|
||||||
|
let item_vec = items
|
||||||
|
.map(|item| {
|
||||||
|
let item_vec = item.split_whitespace().collect::<Vec<&str>>();
|
||||||
|
let cols = item_vec.len();
|
||||||
|
let mut name = item_vec[0].to_string();
|
||||||
|
if item_vec[1].contains("+") {
|
||||||
|
name.push_str(item_vec[1]);
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
name,
|
||||||
|
cost: item_vec[cols - 3].parse::<u32>().unwrap(),
|
||||||
|
damage: item_vec[cols - 2].parse::<u32>().unwrap(),
|
||||||
|
armor: item_vec[cols - 1].parse::<u32>().unwrap(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<Item>>();
|
||||||
|
match category_name {
|
||||||
|
"Weapons" => weapons = item_vec,
|
||||||
|
"Armor" => armors = item_vec,
|
||||||
|
"Rings" => rings = item_vec,
|
||||||
|
_ => panic!("Should not happen"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//println!("{weapons:#?}");
|
||||||
|
//println!("{armors:#?}");
|
||||||
|
//println!("{rings:#?}");
|
||||||
|
|
||||||
|
rings.sort_by(|ring_a, ring_b| ring_a.cost.cmp(&ring_b.cost));
|
||||||
|
|
||||||
|
let choices = create_item_choices(weapons, armors, rings);
|
||||||
|
let mut lowest_cost = u32::MAX;
|
||||||
|
for (weapon, armor, rings) in choices {
|
||||||
|
let mut current_player = player;
|
||||||
|
current_player.equip(weapon);
|
||||||
|
if let Some(armor) = armor {
|
||||||
|
current_player.equip(armor);
|
||||||
|
}
|
||||||
|
if !rings.is_empty() {
|
||||||
|
for ring in rings {
|
||||||
|
current_player.equip(ring);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if current_player.wins_against(&boss) && current_player.items_value < lowest_cost {
|
||||||
|
lowest_cost = current_player.items_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lowest_cost
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_part2(input: &str) -> u32 {
|
||||||
|
let player = Character {
|
||||||
|
hp: 100,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut boss = Character::default();
|
||||||
|
input.lines().for_each(|line| {
|
||||||
|
let (attribute, value) = line.split_once(": ").unwrap();
|
||||||
|
match attribute {
|
||||||
|
"Hit Points" => {
|
||||||
|
boss.hp = value.parse::<u32>().unwrap();
|
||||||
|
}
|
||||||
|
"Damage" => {
|
||||||
|
boss.damage = value.parse::<u32>().unwrap();
|
||||||
|
}
|
||||||
|
"Armor" => {
|
||||||
|
boss.armor = value.parse::<u32>().unwrap();
|
||||||
|
}
|
||||||
|
_ => panic!("Should not happen"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut weapons = Vec::new();
|
||||||
|
let mut armors = Vec::new();
|
||||||
|
let mut rings = Vec::new();
|
||||||
|
|
||||||
|
SHOP.split("\n\n").for_each(|category| {
|
||||||
|
let mut items = category.lines();
|
||||||
|
let (category_name, _) = items.next().unwrap().split_once(":").unwrap();
|
||||||
|
let item_vec = items
|
||||||
|
.map(|item| {
|
||||||
|
let item_vec = item.split_whitespace().collect::<Vec<&str>>();
|
||||||
|
let cols = item_vec.len();
|
||||||
|
let mut name = item_vec[0].to_string();
|
||||||
|
if item_vec[1].contains("+") {
|
||||||
|
name.push_str(item_vec[1]);
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
name,
|
||||||
|
cost: item_vec[cols - 3].parse::<u32>().unwrap(),
|
||||||
|
damage: item_vec[cols - 2].parse::<u32>().unwrap(),
|
||||||
|
armor: item_vec[cols - 1].parse::<u32>().unwrap(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<Item>>();
|
||||||
|
match category_name {
|
||||||
|
"Weapons" => weapons = item_vec,
|
||||||
|
"Armor" => armors = item_vec,
|
||||||
|
"Rings" => rings = item_vec,
|
||||||
|
_ => panic!("Should not happen"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//println!("{weapons:#?}");
|
||||||
|
//println!("{armors:#?}");
|
||||||
|
//println!("{rings:#?}");
|
||||||
|
|
||||||
|
rings.sort_by(|ring_a, ring_b| ring_a.cost.cmp(&ring_b.cost));
|
||||||
|
|
||||||
|
let choices = create_item_choices(weapons, armors, rings);
|
||||||
|
let mut highest_cost = 0;
|
||||||
|
for (weapon, armor, rings) in choices {
|
||||||
|
let mut current_player = player;
|
||||||
|
current_player.equip(weapon);
|
||||||
|
if let Some(armor) = armor {
|
||||||
|
current_player.equip(armor);
|
||||||
|
}
|
||||||
|
if !rings.is_empty() {
|
||||||
|
for ring in rings {
|
||||||
|
current_player.equip(ring);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !current_player.wins_against(&boss) && current_player.items_value > highest_cost {
|
||||||
|
highest_cost = current_player.items_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
highest_cost
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_item_choices(
|
||||||
|
weapons: Vec<Item>,
|
||||||
|
armors: Vec<Item>,
|
||||||
|
rings: Vec<Item>,
|
||||||
|
) -> Vec<(Item, Option<Item>, Vec<Item>)> {
|
||||||
|
let mut choices = Vec::new();
|
||||||
|
|
||||||
|
for weapon in &weapons {
|
||||||
|
choices.push((weapon.clone(), None, vec![]));
|
||||||
|
for armor in &armors {
|
||||||
|
choices.push((weapon.clone(), Some(armor.clone()), vec![]));
|
||||||
|
for ring1 in &rings {
|
||||||
|
choices.push((weapon.clone(), Some(armor.clone()), vec![ring1.clone()]));
|
||||||
|
for ring2 in &rings {
|
||||||
|
if ring2.name == ring1.name {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
choices.push((
|
||||||
|
weapon.clone(),
|
||||||
|
Some(armor.clone()),
|
||||||
|
vec![ring1.clone(), ring2.clone()],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ring1 in &rings {
|
||||||
|
choices.push((weapon.clone(), None, vec![ring1.clone()]));
|
||||||
|
for ring2 in &rings {
|
||||||
|
if ring2.name == ring1.name {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
choices.push((weapon.clone(), None, vec![ring1.clone(), ring2.clone()]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
choices
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Item {
|
||||||
|
name: String,
|
||||||
|
cost: u32,
|
||||||
|
damage: u32,
|
||||||
|
armor: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
|
struct Character {
|
||||||
|
hp: u32,
|
||||||
|
damage: u32,
|
||||||
|
armor: u32,
|
||||||
|
items_value: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Character {
|
||||||
|
fn hit_damage(&self, other: &Self) -> u32 {
|
||||||
|
let damage = self.damage.saturating_sub(other.armor);
|
||||||
|
if damage > 0 {
|
||||||
|
damage
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wins_against(&self, other: &Self) -> bool {
|
||||||
|
let damage_to_other = self.hit_damage(other);
|
||||||
|
let mut turns_to_win = other.hp / damage_to_other;
|
||||||
|
if other.hp % damage_to_other != 0 {
|
||||||
|
turns_to_win += 1;
|
||||||
|
}
|
||||||
|
let damage_to_self = other.hit_damage(self);
|
||||||
|
let mut turns_to_lose = self.hp / damage_to_self;
|
||||||
|
if self.hp % damage_to_self != 0 {
|
||||||
|
turns_to_lose += 1;
|
||||||
|
}
|
||||||
|
turns_to_win <= turns_to_lose
|
||||||
|
}
|
||||||
|
|
||||||
|
fn equip(&mut self, item: Item) {
|
||||||
|
self.damage += item.damage;
|
||||||
|
self.armor += item.armor;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -20,3 +20,5 @@ pub mod d9;
|
|||||||
pub mod d19;
|
pub mod d19;
|
||||||
|
|
||||||
pub mod d20;
|
pub mod d20;
|
||||||
|
|
||||||
|
pub mod d21;
|
||||||
|
Loading…
Reference in New Issue
Block a user