|
|
|
@@ -1,17 +1,32 @@
|
|
|
|
use std::cmp::Reverse;
|
|
|
|
use std::cmp::Reverse;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use itertools::Itertools;
|
|
|
|
|
|
|
|
|
|
|
|
use crate::utils;
|
|
|
|
use crate::utils;
|
|
|
|
|
|
|
|
|
|
|
|
pub fn task1() {
|
|
|
|
pub fn task1() {
|
|
|
|
let input = utils::read_file("input/day24.txt");
|
|
|
|
let input = utils::read_file("input/day24.txt");
|
|
|
|
|
|
|
|
|
|
|
|
let remaining_units = play(&input);
|
|
|
|
let result = play(&input, 0);
|
|
|
|
println!("Standing units after the game: {}", remaining_units);
|
|
|
|
println!("Standing units after the game: {}", result.unwrap().1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn play(input: &str) -> i64 {
|
|
|
|
pub fn task2() {
|
|
|
|
let mut game = Game::create(&input);
|
|
|
|
let input = utils::read_file("input/day24.txt");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let immune_final_count = (0..)
|
|
|
|
|
|
|
|
.map(|boost| play(&input, boost))
|
|
|
|
|
|
|
|
.find(|result| result.map(|(team, _)| team) == Some(Team::ImmuneSystem))
|
|
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
|
|
.1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
println!("Immune systems final count: {immune_final_count}");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn play(input: &str, boost: i64) -> Option<(Team, i64)> {
|
|
|
|
|
|
|
|
let mut game = Game::create(&input, boost);
|
|
|
|
|
|
|
|
|
|
|
|
println!(
|
|
|
|
println!(
|
|
|
|
"Immune start units: {}",
|
|
|
|
"Immune start units: {}",
|
|
|
|
@@ -30,18 +45,20 @@ fn play(input: &str) -> i64 {
|
|
|
|
.sum::<i64>()
|
|
|
|
.sum::<i64>()
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
let mut rounds_played = 0;
|
|
|
|
|
|
|
|
while !game.is_over() {
|
|
|
|
while !game.is_over() {
|
|
|
|
game.round();
|
|
|
|
game.round();
|
|
|
|
rounds_played += 1;
|
|
|
|
if game.draw {
|
|
|
|
|
|
|
|
return None;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
game.remaining_units()
|
|
|
|
Some((game.winning_team(), game.remaining_units()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct Game {
|
|
|
|
struct Game {
|
|
|
|
groups: HashMap<usize, Group>,
|
|
|
|
groups: HashMap<usize, Group>,
|
|
|
|
|
|
|
|
draw: bool,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Game {
|
|
|
|
impl Game {
|
|
|
|
@@ -49,7 +66,20 @@ impl Game {
|
|
|
|
self.groups.values().map(|it| it.units).sum::<i64>()
|
|
|
|
self.groups.values().map(|it| it.units).sum::<i64>()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn create(input: &str) -> Self {
|
|
|
|
fn winning_team(&self) -> Team {
|
|
|
|
|
|
|
|
let teams: Vec<Team> = self
|
|
|
|
|
|
|
|
.groups
|
|
|
|
|
|
|
|
.values()
|
|
|
|
|
|
|
|
.map(|group| group.team)
|
|
|
|
|
|
|
|
.unique()
|
|
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
if teams.len() != 1 {
|
|
|
|
|
|
|
|
panic!("No winning team. Remaining teams: {teams:?}");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
teams[0]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn create(input: &str, boost: i64) -> Self {
|
|
|
|
let mut groups = HashMap::new();
|
|
|
|
let mut groups = HashMap::new();
|
|
|
|
let mut team = Team::ImmuneSystem;
|
|
|
|
let mut team = Team::ImmuneSystem;
|
|
|
|
for (id, line) in input.lines().enumerate() {
|
|
|
|
for (id, line) in input.lines().enumerate() {
|
|
|
|
@@ -62,7 +92,10 @@ impl Game {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"" => (),
|
|
|
|
"" => (),
|
|
|
|
group => {
|
|
|
|
group => {
|
|
|
|
if let Some(group) = Group::from_str(group, team, id) {
|
|
|
|
if let Some(mut group) = Group::from_str(group, team, id) {
|
|
|
|
|
|
|
|
if group.team == Team::ImmuneSystem {
|
|
|
|
|
|
|
|
group.attack_damage += boost;
|
|
|
|
|
|
|
|
}
|
|
|
|
groups.insert(id, group);
|
|
|
|
groups.insert(id, group);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
panic!("bad group: {group}");
|
|
|
|
panic!("bad group: {group}");
|
|
|
|
@@ -70,7 +103,10 @@ impl Game {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Game { groups }
|
|
|
|
Game {
|
|
|
|
|
|
|
|
groups,
|
|
|
|
|
|
|
|
draw: false,
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn round(&mut self) {
|
|
|
|
fn round(&mut self) {
|
|
|
|
@@ -125,6 +161,7 @@ impl Game {
|
|
|
|
let mut all_ids_by_initiative: Vec<usize> = self.groups.values().map(|it| it.id).collect();
|
|
|
|
let mut all_ids_by_initiative: Vec<usize> = self.groups.values().map(|it| it.id).collect();
|
|
|
|
all_ids_by_initiative.sort_unstable_by_key(|id| Reverse(self.groups[id].initiative));
|
|
|
|
all_ids_by_initiative.sort_unstable_by_key(|id| Reverse(self.groups[id].initiative));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.draw = true;
|
|
|
|
for active_id in all_ids_by_initiative {
|
|
|
|
for active_id in all_ids_by_initiative {
|
|
|
|
if !self.groups[&active_id].alive() {
|
|
|
|
if !self.groups[&active_id].alive() {
|
|
|
|
// was killed in this round
|
|
|
|
// was killed in this round
|
|
|
|
@@ -137,6 +174,9 @@ impl Game {
|
|
|
|
let dying_units = damage / enemy.hp_each;
|
|
|
|
let dying_units = damage / enemy.hp_each;
|
|
|
|
self.groups.get_mut(enemy_id).unwrap().units -= dying_units;
|
|
|
|
self.groups.get_mut(enemy_id).unwrap().units -= dying_units;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if dying_units > 0 {
|
|
|
|
|
|
|
|
self.draw = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
// println!(
|
|
|
|
// println!(
|
|
|
|
// "{} dealt {} ({} units) damage to {}",
|
|
|
|
// "{} dealt {} ({} units) damage to {}",
|
|
|
|
// active_id, damage, dying_units, enemy_id
|
|
|
|
// active_id, damage, dying_units, enemy_id
|
|
|
|
@@ -158,7 +198,7 @@ impl Game {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
|
|
enum Team {
|
|
|
|
enum Team {
|
|
|
|
Infection,
|
|
|
|
Infection,
|
|
|
|
ImmuneSystem,
|
|
|
|
ImmuneSystem,
|
|
|
|
@@ -247,15 +287,21 @@ impl Group {
|
|
|
|
mod test {
|
|
|
|
mod test {
|
|
|
|
use crate::tasks::day24::play;
|
|
|
|
use crate::tasks::day24::play;
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
const INPUT: &str = "Immune System:
|
|
|
|
fn example1() {
|
|
|
|
|
|
|
|
let input = "Immune System:
|
|
|
|
|
|
|
|
17 units each with 5390 hit points (weak to radiation, bludgeoning) with an attack that does 4507 fire damage at initiative 2
|
|
|
|
17 units each with 5390 hit points (weak to radiation, bludgeoning) with an attack that does 4507 fire damage at initiative 2
|
|
|
|
989 units each with 1274 hit points (immune to fire; weak to bludgeoning, slashing) with an attack that does 25 slashing damage at initiative 3
|
|
|
|
989 units each with 1274 hit points (immune to fire; weak to bludgeoning, slashing) with an attack that does 25 slashing damage at initiative 3
|
|
|
|
|
|
|
|
|
|
|
|
Infection:
|
|
|
|
Infection:
|
|
|
|
801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1
|
|
|
|
801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1
|
|
|
|
4485 units each with 2961 hit points (immune to radiation; weak to fire, cold) with an attack that does 12 slashing damage at initiative 4";
|
|
|
|
4485 units each with 2961 hit points (immune to radiation; weak to fire, cold) with an attack that does 12 slashing damage at initiative 4";
|
|
|
|
assert_eq!(5216, play(input));
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
|
|
fn example1() {
|
|
|
|
|
|
|
|
assert_eq!(5216, play(INPUT, 0).unwrap().1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
|
|
fn example2() {
|
|
|
|
|
|
|
|
assert_eq!(51, play(INPUT, 1570).unwrap().1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|