Compare commits

..

3 Commits

Author SHA1 Message Date
Johannes
c162fcb6d9 day24 p1 wrong (example ok) 2018-12-29 16:43:39 +01:00
Johannes
097cfac516 day24 part1 input parsing 2018-12-26 23:30:39 +01:00
Johannes
2b51e67fc6 day23 hmmm 2018-12-26 22:27:36 +01:00
7 changed files with 389 additions and 2 deletions

7
Cargo.lock generated
View File

@@ -11,6 +11,7 @@ name = "aoc_2018"
version = "0.1.0"
dependencies = [
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"gcd 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -35,6 +36,11 @@ name = "either"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "gcd"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itertools"
version = "0.7.11"
@@ -158,6 +164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
"checksum gcd 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0896cb73353671dbe2b17312c97b55b7032831d28c809c703bece4a392b1df3a"
"checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d"
"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
"checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311"

View File

@@ -8,3 +8,4 @@ edition = "2018"
regex = "1.1.0"
chrono = "0.4.6"
itertools = "0.7.11"
gcd = "1.1.0"

23
input/day24.txt Normal file
View File

@@ -0,0 +1,23 @@
Immune System:
3400 units each with 1430 hit points (immune to fire, radiation, slashing) with an attack that does 4 radiation damage at initiative 4
138 units each with 8650 hit points (weak to bludgeoning; immune to slashing, cold, radiation) with an attack that does 576 slashing damage at initiative 16
255 units each with 9469 hit points (weak to radiation, fire) with an attack that does 351 bludgeoning damage at initiative 8
4145 units each with 2591 hit points (immune to cold; weak to slashing) with an attack that does 6 fire damage at initiative 12
3605 units each with 10989 hit points with an attack that does 26 fire damage at initiative 19
865 units each with 11201 hit points with an attack that does 102 slashing damage at initiative 10
633 units each with 10092 hit points (weak to slashing, radiation) with an attack that does 150 slashing damage at initiative 11
2347 units each with 3322 hit points with an attack that does 12 cold damage at initiative 2
7045 units each with 3877 hit points (weak to radiation) with an attack that does 5 bludgeoning damage at initiative 5
1086 units each with 8626 hit points (weak to radiation) with an attack that does 69 slashing damage at initiative 13
Infection:
2152 units each with 12657 hit points (weak to fire, cold) with an attack that does 11 fire damage at initiative 18
40 units each with 39458 hit points (immune to radiation, fire, slashing; weak to bludgeoning) with an attack that does 1519 slashing damage at initiative 7
59 units each with 35138 hit points (immune to radiation; weak to fire) with an attack that does 1105 fire damage at initiative 15
1569 units each with 51364 hit points (weak to radiation) with an attack that does 55 radiation damage at initiative 17
929 units each with 23887 hit points (weak to bludgeoning) with an attack that does 48 cold damage at initiative 14
5264 units each with 14842 hit points (immune to cold, fire; weak to slashing, bludgeoning) with an attack that does 4 bludgeoning damage at initiative 9
1570 units each with 30419 hit points (weak to radiation, cold; immune to fire) with an attack that does 35 slashing damage at initiative 1
1428 units each with 21393 hit points (weak to radiation) with an attack that does 29 cold damage at initiative 6
1014 units each with 25717 hit points (weak to fire) with an attack that does 47 fire damage at initiative 3
7933 units each with 29900 hit points (immune to bludgeoning, radiation, slashing) with an attack that does 5 slashing damage at initiative 20

View File

@@ -1,4 +1,4 @@
fn main() {
aoc_2018::tasks::day23::task1();
// aoc_2018::tasks::day15::task2();
aoc_2018::tasks::day24::task1();
// aoc_2018::tasks::day23::task2();
}

View File

@@ -30,6 +30,70 @@ pub fn task1() {
println!("There are {} bots in range of the big bot.", bots_in_range);
}
pub fn task2() {
let input = utils::read_file("input/day23.txt");
let regex =
Regex::new(r"^pos=<(?P<x>-?\d+),(?P<y>-?\d+),(?P<z>-?\d+)>, r=(?P<range>\d+)$").unwrap();
let bots: Vec<Bot> = input
.lines()
.map(|line| {
let m = regex.captures(line).unwrap();
let x = m["x"].parse::<isize>().unwrap();
let y = m["y"].parse::<isize>().unwrap();
let z = m["z"].parse::<isize>().unwrap();
let range = m["range"].parse::<usize>().unwrap();
Bot { x, y, z, range }
})
.collect();
// let r_min = bots.iter().min_by_key(|it| it.range).unwrap().range;
// let r_max = bots.iter().max_by_key(|it| it.range).unwrap().range;
// println!("Radius min max: {}/{}", r_min, r_max);
// let x_min = bots.iter().min_by_key(|it| it.x).unwrap().x;
// let x_max = bots.iter().max_by_key(|it| it.x).unwrap().x;
// println!("X range: {}", x_max - x_min);
// let y_min = bots.iter().min_by_key(|it| it.y).unwrap().y;
// let y_max = bots.iter().max_by_key(|it| it.y).unwrap().y;
// println!("Y range: {}", y_max - y_min);
// let z_min = bots.iter().min_by_key(|it| it.z).unwrap().z;
// let z_max = bots.iter().max_by_key(|it| it.z).unwrap().z;
// println!("Z range: {}", z_max - z_min);
let neighbor_counts: Vec<(Bot, usize)> = bots
.iter()
.flat_map(|bot| bot.corners())
.map(|corner| {
let count = bots
.iter()
.filter(|bot| bot.distance(&corner) <= bot.range)
.count();
(corner, count)
})
.collect();
let max = neighbor_counts.iter().max_by_key(|it| it.1).unwrap().1;
let start = Bot {
x: 0,
y: 0,
z: 0,
range: 0,
};
let candidates = neighbor_counts.iter().filter(|it| it.1 == max).count();
println!("{} points in range of {} bots", candidates, max);
let candidate = neighbor_counts
.iter()
.filter(|it| it.1 == max)
.min_by_key(|it| it.0.distance(&start));
println!(
"Corner with most bots in range: {:?}",
candidate.unwrap().0.distance(&start)
);
// wrong: 37446460,43177892,57318660; 137943012; 102224079;
}
#[derive(Debug)]
struct Bot {
x: isize,
@@ -42,4 +106,45 @@ impl Bot {
fn distance(&self, other: &Self) -> usize {
((other.x - self.x).abs() + (other.y - self.y).abs() + (other.z - self.z).abs()) as usize
}
fn corners(&self) -> Vec<Self> {
vec![
Bot {
x: self.x + self.range as isize,
y: self.y,
z: self.z,
range: 0,
},
Bot {
x: self.x - self.range as isize,
y: self.y,
z: self.z,
range: 0,
},
Bot {
x: self.x,
y: self.y + self.range as isize,
z: self.z,
range: 0,
},
Bot {
x: self.x,
y: self.y - self.range as isize,
z: self.z,
range: 0,
},
Bot {
x: self.x,
y: self.y,
z: self.z + self.range as isize,
range: 0,
},
Bot {
x: self.x,
y: self.y,
z: self.z - self.range as isize,
range: 0,
},
]
}
}

250
src/tasks/day24.rs Normal file
View File

@@ -0,0 +1,250 @@
use crate::utils;
use std::cmp::Reverse;
use std::collections::HashMap;
pub fn task1() {
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
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:
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";
let input = utils::read_file("input/day24.txt");
let mut game = Game::create(&input);
println!(
"Immune start units: {}",
game.groups
.iter()
.filter(|(_, it)| it.team == Team::ImmuneSystem)
.map(|(_, it)| it.units)
.sum::<i64>()
);
println!(
"Infection start units: {}",
game.groups
.iter()
.filter(|(_, it)| it.team == Team::Infection)
.map(|(_, it)| it.units)
.sum::<i64>()
);
let mut rounds_played = 0;
while !game.is_over() {
game.round();
rounds_played += 1;
}
println!("{:#?}", game);
println!("Played {} rounds", rounds_played);
println!(
"Standing units after the game: {}",
game.groups.iter().map(|(_, it)| it.units).sum::<i64>()
);
// 21107 too high
// 21004 too high
}
#[derive(Debug)]
struct Game {
groups: HashMap<usize, Group>,
}
impl Game {
fn create(input: &str) -> Self {
let mut groups = HashMap::new();
let mut team = Team::ImmuneSystem;
for (id, line) in input.lines().enumerate() {
match line {
"Immune System:" => {
team = Team::ImmuneSystem;
}
"Infection:" => {
team = Team::Infection;
}
"" => (),
group => {
if let Some(group) = Group::from_str(group, team, id) {
groups.insert(id, group);
}
}
}
}
Game { groups }
}
fn round(&mut self) {
let mut target: HashMap<usize, usize> = HashMap::new();
// lock targets ordered by effective power
let mut all_by_power: Vec<&Group> = self.groups.iter().map(|(_, it)| it).collect();
all_by_power.sort_unstable_by_key(|a| Reverse((a.effective_power(), a.initiative)));
// for group in all_by_power.iter() {
// println!(
// "{}: {} ({})",
// group.id,
// group.effective_power(),
// group.initiative
// );
// }
// println!("{:?}", all_by_power);
for group in all_by_power.iter() {
if let Some(t) = self
.groups
.iter()
.map(|(_, it)| it)
.filter(|it| it.team != group.team)
.filter(|it| !target.values().any(|t| *t == it.id))
// .filter(|it| group.compute_attack_damage_to(&it) >= it.hp_each)
// .inspect(|it| {
// println!(
// "{} would deal {} damage to {}",
// group.id,
// group.compute_attack_damage_to(it),
// it.id
// )
// })
.max_by_key(|it| {
(
group.compute_attack_damage_to(&it),
it.effective_power(),
it.initiative,
)
})
{
if group.compute_attack_damage_to(&t) <= 0 {
println!(
"Didn't find a target where {:?} can deal positive damage.",
group
);
continue;
} else {
target.insert(group.id, t.id);
}
}
}
// attack ordered by initiative
let mut all_ids_by_initiative: Vec<usize> =
self.groups.iter().map(|(_, it)| it.id).collect();
all_ids_by_initiative.sort_unstable_by_key(|id| Reverse(self.groups[id].initiative));
for active_id in all_ids_by_initiative {
if !self.groups[&active_id].alive() {
// was killed in this round
println!("Group {} already dead", active_id);
continue;
}
if let Some(enemy_id) = target.get(&active_id) {
let enemy = &self.groups[enemy_id];
let damage: i64 = self.groups[&active_id].compute_attack_damage_to(&enemy);
let dying_units = damage / enemy.hp_each;
if let Some(enemy) = self.groups.get_mut(enemy_id) {
enemy.units -= dying_units;
}
// println!(
// "{} dealt {} ({} units) damage to {}",
// active_id, damage, dying_units, enemy_id
// );
}
}
// clean up dead groups
self.groups.retain(|_, it| it.alive());
// println!("{:?}", self.groups);
}
fn is_over(&self) -> bool {
self.groups.len() == 0
|| self.groups.iter().all(|(_, it)| it.team == Team::Infection)
|| self
.groups
.iter()
.all(|(_, it)| it.team == Team::ImmuneSystem)
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
enum Team {
Infection,
ImmuneSystem,
}
#[derive(Debug, Clone)]
struct Group {
id: usize,
team: Team,
units: i64,
hp_each: i64,
weaknesses: Vec<String>,
immunities: Vec<String>,
attack_damage: i64,
attack_type: String,
initiative: u64,
}
impl Group {
fn from_str(input: &str, team: Team, id: usize) -> Option<Self> {
// 801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1
use regex::Regex;
let regex = Regex::new(r"(\d+) units each with (\d+) hit points \((.+)\) with an attack that does (\d+) (\w+) damage at initiative (\d+)").unwrap();
if let Some(m) = regex.captures(input) {
let units: i64 = m[1].parse().unwrap();
let hp_each: i64 = m[2].parse().unwrap();
let attack_damage: i64 = m[4].parse().unwrap();
let attack_type = m[5].to_string();
let initiative: u64 = m[6].parse().unwrap();
let mut weaknesses: Vec<String> = Vec::new();
let mut immunities: Vec<String> = Vec::new();
for part in m[3].split("; ") {
if part.starts_with("weak to ") {
weaknesses = part[8..].split(", ").map(|it| it.to_string()).collect();
} else if part.starts_with("immune to ") {
immunities = part[10..].split(", ").map(|it| it.to_string()).collect();
}
}
let group = Group {
id,
team,
units,
hp_each,
weaknesses,
immunities,
attack_damage,
attack_type,
initiative,
};
Some(group)
} else {
None
}
}
fn alive(&self) -> bool {
self.units > 0
}
fn effective_power(&self) -> i64 {
if !self.alive() {
panic!("I have no power, im dead!")
}
self.units * self.attack_damage
}
fn compute_attack_damage_to(&self, other: &Self) -> i64 {
if self.alive() {
if other.weaknesses.contains(&self.attack_type) {
self.effective_power() * 2
} else if other.immunities.contains(&self.attack_type) {
0
} else {
self.effective_power()
}
} else {
panic!("I'm not alive, I cannot attack anyone")
}
}
}

View File

@@ -16,3 +16,4 @@ pub mod day15;
pub mod day20;
pub mod day22;
pub mod day23;
pub mod day24;