solution: day21
This commit is contained in:
		@@ -62,11 +62,11 @@ mod tests {
 | 
				
			|||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn test_part_one() {
 | 
					    fn test_part_one() {
 | 
				
			||||||
        let input = aoc::read_file("test_inputs", 19);
 | 
					        let input = aoc::read_file("test_inputs", 19);
 | 
				
			||||||
        assert_eq!(part_one(&input.trim()), Some(4));
 | 
					        assert_eq!(part_one(input.trim()), Some(4));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn test_part_two() {
 | 
					    fn test_part_two() {
 | 
				
			||||||
        let input = aoc::read_file("test_inputs", 19);
 | 
					        let input = aoc::read_file("test_inputs", 19);
 | 
				
			||||||
        assert_eq!(part_two(&input.trim()), Some(3));
 | 
					        assert_eq!(part_two(input.trim()), Some(3));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										259
									
								
								src/bin/21.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								src/bin/21.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,259 @@
 | 
				
			|||||||
 | 
					use itertools::Itertools;
 | 
				
			||||||
 | 
					use ItemType::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static STORE: [Item; 16] = [
 | 
				
			||||||
 | 
					    // weapons
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Weapon,
 | 
				
			||||||
 | 
					        cost: 8,
 | 
				
			||||||
 | 
					        damage: 4,
 | 
				
			||||||
 | 
					        armor: 0,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Weapon,
 | 
				
			||||||
 | 
					        cost: 10,
 | 
				
			||||||
 | 
					        damage: 5,
 | 
				
			||||||
 | 
					        armor: 0,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Weapon,
 | 
				
			||||||
 | 
					        cost: 25,
 | 
				
			||||||
 | 
					        damage: 6,
 | 
				
			||||||
 | 
					        armor: 0,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Weapon,
 | 
				
			||||||
 | 
					        cost: 40,
 | 
				
			||||||
 | 
					        damage: 7,
 | 
				
			||||||
 | 
					        armor: 0,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Weapon,
 | 
				
			||||||
 | 
					        cost: 74,
 | 
				
			||||||
 | 
					        damage: 8,
 | 
				
			||||||
 | 
					        armor: 0,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    // armor
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Armor,
 | 
				
			||||||
 | 
					        cost: 13,
 | 
				
			||||||
 | 
					        damage: 0,
 | 
				
			||||||
 | 
					        armor: 1,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Armor,
 | 
				
			||||||
 | 
					        cost: 31,
 | 
				
			||||||
 | 
					        damage: 0,
 | 
				
			||||||
 | 
					        armor: 2,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Armor,
 | 
				
			||||||
 | 
					        cost: 53,
 | 
				
			||||||
 | 
					        damage: 0,
 | 
				
			||||||
 | 
					        armor: 3,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Armor,
 | 
				
			||||||
 | 
					        cost: 75,
 | 
				
			||||||
 | 
					        damage: 0,
 | 
				
			||||||
 | 
					        armor: 4,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Armor,
 | 
				
			||||||
 | 
					        cost: 102,
 | 
				
			||||||
 | 
					        damage: 0,
 | 
				
			||||||
 | 
					        armor: 5,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    // rings
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Ring,
 | 
				
			||||||
 | 
					        cost: 25,
 | 
				
			||||||
 | 
					        damage: 1,
 | 
				
			||||||
 | 
					        armor: 0,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Ring,
 | 
				
			||||||
 | 
					        cost: 50,
 | 
				
			||||||
 | 
					        damage: 2,
 | 
				
			||||||
 | 
					        armor: 0,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Ring,
 | 
				
			||||||
 | 
					        cost: 100,
 | 
				
			||||||
 | 
					        damage: 3,
 | 
				
			||||||
 | 
					        armor: 0,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Ring,
 | 
				
			||||||
 | 
					        cost: 20,
 | 
				
			||||||
 | 
					        damage: 0,
 | 
				
			||||||
 | 
					        armor: 1,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Ring,
 | 
				
			||||||
 | 
					        cost: 40,
 | 
				
			||||||
 | 
					        damage: 0,
 | 
				
			||||||
 | 
					        armor: 2,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Item {
 | 
				
			||||||
 | 
					        item_type: Ring,
 | 
				
			||||||
 | 
					        cost: 80,
 | 
				
			||||||
 | 
					        damage: 0,
 | 
				
			||||||
 | 
					        armor: 3,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Copy)]
 | 
				
			||||||
 | 
					struct Player {
 | 
				
			||||||
 | 
					    hp: i32,
 | 
				
			||||||
 | 
					    damage: i32,
 | 
				
			||||||
 | 
					    armor: i32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Player {
 | 
				
			||||||
 | 
					    fn alive(&self) -> bool {
 | 
				
			||||||
 | 
					        self.hp > 0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					enum ItemType {
 | 
				
			||||||
 | 
					    Weapon,
 | 
				
			||||||
 | 
					    Armor,
 | 
				
			||||||
 | 
					    Ring,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct Item {
 | 
				
			||||||
 | 
					    item_type: ItemType,
 | 
				
			||||||
 | 
					    cost: i32,
 | 
				
			||||||
 | 
					    damage: i32,
 | 
				
			||||||
 | 
					    armor: i32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Item {
 | 
				
			||||||
 | 
					    fn valid_selection(selection: &[&Item]) -> bool {
 | 
				
			||||||
 | 
					        if selection
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .filter(|x| matches!(x.item_type, Weapon))
 | 
				
			||||||
 | 
					            .count()
 | 
				
			||||||
 | 
					            != 1
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if selection
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .filter(|x| matches!(x.item_type, Armor))
 | 
				
			||||||
 | 
					            .count()
 | 
				
			||||||
 | 
					            > 1
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if selection
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .filter(|x| matches!(x.item_type, Ring))
 | 
				
			||||||
 | 
					            .count()
 | 
				
			||||||
 | 
					            > 2
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn simulate(me: Player, items: &[&Item], mut boss: Player) -> bool {
 | 
				
			||||||
 | 
					    let mut equiped_me = Player {
 | 
				
			||||||
 | 
					        hp: me.hp,
 | 
				
			||||||
 | 
					        damage: me.damage + items.iter().map(|x| x.damage).sum::<i32>(),
 | 
				
			||||||
 | 
					        armor: me.armor + items.iter().map(|x| x.armor).sum::<i32>(),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while equiped_me.alive() && boss.alive() {
 | 
				
			||||||
 | 
					        let mut my_damage = equiped_me.damage - boss.armor;
 | 
				
			||||||
 | 
					        if my_damage < 1 {
 | 
				
			||||||
 | 
					            my_damage = 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut boss_damage = boss.damage - equiped_me.armor;
 | 
				
			||||||
 | 
					        if boss_damage < 1 {
 | 
				
			||||||
 | 
					            boss_damage = 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        equiped_me.hp -= boss_damage;
 | 
				
			||||||
 | 
					        boss.hp -= my_damage;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    !boss.alive()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					pub fn part_one(input: &str) -> Option<i32> {
 | 
				
			||||||
 | 
					    let (hp, damage, armor) = input
 | 
				
			||||||
 | 
					        .split('\n')
 | 
				
			||||||
 | 
					        .map(|x| x.split(": ").last().unwrap().parse::<i32>().unwrap())
 | 
				
			||||||
 | 
					        .next_tuple()
 | 
				
			||||||
 | 
					        .unwrap();
 | 
				
			||||||
 | 
					    let boss = Player { hp, damage, armor };
 | 
				
			||||||
 | 
					    let me = Player {
 | 
				
			||||||
 | 
					        hp: 100,
 | 
				
			||||||
 | 
					        damage: 0,
 | 
				
			||||||
 | 
					        armor: 0,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (0..STORE.len())
 | 
				
			||||||
 | 
					        .map(|x| {
 | 
				
			||||||
 | 
					            STORE
 | 
				
			||||||
 | 
					                .iter()
 | 
				
			||||||
 | 
					                .combinations(x)
 | 
				
			||||||
 | 
					                .filter(|y| Item::valid_selection(y))
 | 
				
			||||||
 | 
					                .map(|y| {
 | 
				
			||||||
 | 
					                    (
 | 
				
			||||||
 | 
					                        y.iter().map(|z| z.cost).sum::<i32>(),
 | 
				
			||||||
 | 
					                        simulate(me, &y, boss),
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .filter(|(_, y)| *y)
 | 
				
			||||||
 | 
					                .map(|(y, _)| y)
 | 
				
			||||||
 | 
					                .collect_vec()
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .filter_map(|x| x.iter().min().copied())
 | 
				
			||||||
 | 
					        .min()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					pub fn part_two(input: &str) -> Option<i32> {
 | 
				
			||||||
 | 
					    let (hp, damage, armor) = input
 | 
				
			||||||
 | 
					        .split('\n')
 | 
				
			||||||
 | 
					        .map(|x| x.split(": ").last().unwrap().parse::<i32>().unwrap())
 | 
				
			||||||
 | 
					        .next_tuple()
 | 
				
			||||||
 | 
					        .unwrap();
 | 
				
			||||||
 | 
					    let boss = Player { hp, damage, armor };
 | 
				
			||||||
 | 
					    let me = Player {
 | 
				
			||||||
 | 
					        hp: 100,
 | 
				
			||||||
 | 
					        damage: 0,
 | 
				
			||||||
 | 
					        armor: 0,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (0..STORE.len())
 | 
				
			||||||
 | 
					        .map(|x| {
 | 
				
			||||||
 | 
					            STORE
 | 
				
			||||||
 | 
					                .iter()
 | 
				
			||||||
 | 
					                .combinations(x)
 | 
				
			||||||
 | 
					                .filter(|y| Item::valid_selection(y))
 | 
				
			||||||
 | 
					                .map(|y| {
 | 
				
			||||||
 | 
					                    (
 | 
				
			||||||
 | 
					                        y.iter().map(|z| z.cost).sum::<i32>(),
 | 
				
			||||||
 | 
					                        !simulate(me, &y, boss),
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .filter(|(_, y)| *y)
 | 
				
			||||||
 | 
					                .map(|(y, _)| y)
 | 
				
			||||||
 | 
					                .collect_vec()
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .filter_map(|x| x.iter().max().copied())
 | 
				
			||||||
 | 
					        .max()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    let input = &aoc::read_file("inputs", 21);
 | 
				
			||||||
 | 
					    aoc::solve!(1, part_one, input);
 | 
				
			||||||
 | 
					    aoc::solve!(2, part_two, input);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user