This commit is contained in:
Fabian Schmidt 2024-11-05 15:44:23 +01:00
parent aa28df24b3
commit 1d9c848525
4 changed files with 315 additions and 0 deletions

View File

@ -0,0 +1,3 @@
Hit Points: 103
Damage: 9
Armor: 2

20
y2015/src/bin/d21.rs Normal file
View 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
View 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);
}
}

View File

@ -20,3 +20,5 @@ pub mod d9;
pub mod d19;
pub mod d20;
pub mod d21;