solution: day22
This commit is contained in:
		
							
								
								
									
										211
									
								
								src/bin/22.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								src/bin/22.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,211 @@
 | 
				
			|||||||
 | 
					use std::cmp::max;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use itertools::Itertools;
 | 
				
			||||||
 | 
					use lazy_static::lazy_static;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Copy)]
 | 
				
			||||||
 | 
					struct Player {
 | 
				
			||||||
 | 
					    hp: i32,
 | 
				
			||||||
 | 
					    damage: i32,
 | 
				
			||||||
 | 
					    mana: i32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
 | 
					struct Spell {
 | 
				
			||||||
 | 
					    name: String,
 | 
				
			||||||
 | 
					    timer: i32,
 | 
				
			||||||
 | 
					    damage: i32,
 | 
				
			||||||
 | 
					    armor: i32,
 | 
				
			||||||
 | 
					    heal: i32,
 | 
				
			||||||
 | 
					    mana: i32,
 | 
				
			||||||
 | 
					    cost: i32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lazy_static! {
 | 
				
			||||||
 | 
					    static ref SPELLS: [Spell; 5] = [
 | 
				
			||||||
 | 
					        Spell {
 | 
				
			||||||
 | 
					            name: "Magic Missile".to_string(),
 | 
				
			||||||
 | 
					            timer: 1,
 | 
				
			||||||
 | 
					            damage: 4,
 | 
				
			||||||
 | 
					            armor: 0,
 | 
				
			||||||
 | 
					            heal: 0,
 | 
				
			||||||
 | 
					            mana: 0,
 | 
				
			||||||
 | 
					            cost: 53,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        Spell {
 | 
				
			||||||
 | 
					            name: "Drain".to_string(),
 | 
				
			||||||
 | 
					            timer: 1,
 | 
				
			||||||
 | 
					            damage: 2,
 | 
				
			||||||
 | 
					            armor: 0,
 | 
				
			||||||
 | 
					            heal: 2,
 | 
				
			||||||
 | 
					            mana: 0,
 | 
				
			||||||
 | 
					            cost: 73,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        Spell {
 | 
				
			||||||
 | 
					            name: "Shield".to_string(),
 | 
				
			||||||
 | 
					            timer: 6,
 | 
				
			||||||
 | 
					            damage: 0,
 | 
				
			||||||
 | 
					            armor: 7,
 | 
				
			||||||
 | 
					            heal: 0,
 | 
				
			||||||
 | 
					            mana: 0,
 | 
				
			||||||
 | 
					            cost: 113,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        Spell {
 | 
				
			||||||
 | 
					            name: "Poison".to_string(),
 | 
				
			||||||
 | 
					            timer: 6,
 | 
				
			||||||
 | 
					            damage: 3,
 | 
				
			||||||
 | 
					            armor: 0,
 | 
				
			||||||
 | 
					            heal: 0,
 | 
				
			||||||
 | 
					            mana: 0,
 | 
				
			||||||
 | 
					            cost: 173,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        Spell {
 | 
				
			||||||
 | 
					            name: "Recharge".to_string(),
 | 
				
			||||||
 | 
					            timer: 5,
 | 
				
			||||||
 | 
					            damage: 0,
 | 
				
			||||||
 | 
					            armor: 0,
 | 
				
			||||||
 | 
					            heal: 0,
 | 
				
			||||||
 | 
					            mana: 101,
 | 
				
			||||||
 | 
					            cost: 229,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn optimize_fight(
 | 
				
			||||||
 | 
					    mut boss: Player,
 | 
				
			||||||
 | 
					    mut player: Player,
 | 
				
			||||||
 | 
					    players_turn: bool,
 | 
				
			||||||
 | 
					    mut active_spells: Vec<Spell>,
 | 
				
			||||||
 | 
					    best_cost: &mut i32,
 | 
				
			||||||
 | 
					    cost: i32,
 | 
				
			||||||
 | 
					    hardmode: bool,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    if cost > *best_cost {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let mut remaining = active_spells
 | 
				
			||||||
 | 
					        .drain(..)
 | 
				
			||||||
 | 
					        .filter(|x| x.timer > 0)
 | 
				
			||||||
 | 
					        .collect_vec();
 | 
				
			||||||
 | 
					    let mut armor = 0;
 | 
				
			||||||
 | 
					    for spell in remaining.iter_mut() {
 | 
				
			||||||
 | 
					        spell.timer -= 1;
 | 
				
			||||||
 | 
					        boss.hp -= spell.damage;
 | 
				
			||||||
 | 
					        armor += spell.armor;
 | 
				
			||||||
 | 
					        player.hp += spell.heal;
 | 
				
			||||||
 | 
					        player.mana += spell.mana;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if hardmode && players_turn {
 | 
				
			||||||
 | 
					        player.hp -= 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if player.hp <= 0 {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if boss.hp <= 0 {
 | 
				
			||||||
 | 
					        if cost < *best_cost {
 | 
				
			||||||
 | 
					            *best_cost = cost;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if players_turn {
 | 
				
			||||||
 | 
					        let current = remaining
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .filter(|x| x.timer > 0)
 | 
				
			||||||
 | 
					            .map(|x| x.name.to_string())
 | 
				
			||||||
 | 
					            .collect_vec();
 | 
				
			||||||
 | 
					        for spell in SPELLS
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .filter(|x| !current.contains(&x.name) && x.cost < player.mana)
 | 
				
			||||||
 | 
					            .collect_vec()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            let mut new_spells = remaining.to_vec();
 | 
				
			||||||
 | 
					            new_spells.push(spell.clone());
 | 
				
			||||||
 | 
					            let mut c = player;
 | 
				
			||||||
 | 
					            c.mana -= spell.cost;
 | 
				
			||||||
 | 
					            optimize_fight(
 | 
				
			||||||
 | 
					                boss,
 | 
				
			||||||
 | 
					                c,
 | 
				
			||||||
 | 
					                !players_turn,
 | 
				
			||||||
 | 
					                new_spells,
 | 
				
			||||||
 | 
					                best_cost,
 | 
				
			||||||
 | 
					                cost + spell.cost,
 | 
				
			||||||
 | 
					                hardmode,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        let damage = max(boss.damage - armor, 1);
 | 
				
			||||||
 | 
					        player.hp -= damage;
 | 
				
			||||||
 | 
					        optimize_fight(
 | 
				
			||||||
 | 
					            boss,
 | 
				
			||||||
 | 
					            player,
 | 
				
			||||||
 | 
					            !players_turn,
 | 
				
			||||||
 | 
					            remaining,
 | 
				
			||||||
 | 
					            best_cost,
 | 
				
			||||||
 | 
					            cost,
 | 
				
			||||||
 | 
					            hardmode,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn part_one(input: &str) -> Option<i32> {
 | 
				
			||||||
 | 
					    let (hp, damage) = input
 | 
				
			||||||
 | 
					        .split('\n')
 | 
				
			||||||
 | 
					        .map(|x| x.split(": ").last().unwrap().parse::<i32>().unwrap())
 | 
				
			||||||
 | 
					        .next_tuple()
 | 
				
			||||||
 | 
					        .unwrap();
 | 
				
			||||||
 | 
					    let boss = Player {
 | 
				
			||||||
 | 
					        hp,
 | 
				
			||||||
 | 
					        damage,
 | 
				
			||||||
 | 
					        mana: 0,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    let me = Player {
 | 
				
			||||||
 | 
					        hp: 50,
 | 
				
			||||||
 | 
					        damage: 0,
 | 
				
			||||||
 | 
					        mana: 500,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    // this takes 7minutes but iiwii
 | 
				
			||||||
 | 
					    let mut best_score = i32::max_value();
 | 
				
			||||||
 | 
					    optimize_fight(boss, me, true, vec![], &mut best_score, 0, false);
 | 
				
			||||||
 | 
					    Some(best_score)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					pub fn part_two(input: &str) -> Option<i32> {
 | 
				
			||||||
 | 
					    let (hp, damage) = input
 | 
				
			||||||
 | 
					        .split('\n')
 | 
				
			||||||
 | 
					        .map(|x| x.split(": ").last().unwrap().parse::<i32>().unwrap())
 | 
				
			||||||
 | 
					        .next_tuple()
 | 
				
			||||||
 | 
					        .unwrap();
 | 
				
			||||||
 | 
					    let boss = Player {
 | 
				
			||||||
 | 
					        hp,
 | 
				
			||||||
 | 
					        damage,
 | 
				
			||||||
 | 
					        mana: 0,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    let me = Player {
 | 
				
			||||||
 | 
					        hp: 50,
 | 
				
			||||||
 | 
					        damage: 0,
 | 
				
			||||||
 | 
					        mana: 500,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    let mut best_score = i32::max_value();
 | 
				
			||||||
 | 
					    optimize_fight(boss, me, true, vec![], &mut best_score, 0, true);
 | 
				
			||||||
 | 
					    Some(best_score)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    let input = &aoc::read_file("inputs", 22);
 | 
				
			||||||
 | 
					    aoc::solve!(1, part_one, input);
 | 
				
			||||||
 | 
					    aoc::solve!(2, part_two, input);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_part_one() {
 | 
				
			||||||
 | 
					        let input = aoc::read_file("test_inputs", 22);
 | 
				
			||||||
 | 
					        assert_eq!(part_one(input.trim()), Some(212));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_part_two() {
 | 
				
			||||||
 | 
					        let input = aoc::read_file("test_inputs", 22);
 | 
				
			||||||
 | 
					        assert_eq!(part_two(input.trim()), Some(212));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										2
									
								
								src/test_inputs/22.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/test_inputs/22.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					Hit Points: 14
 | 
				
			||||||
 | 
					Damage: 8
 | 
				
			||||||
		Reference in New Issue
	
	Block a user