Compare commits
3 Commits
6e438127ec
...
c162fcb6d9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c162fcb6d9 | ||
|
|
097cfac516 | ||
|
|
2b51e67fc6 |
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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
23
input/day24.txt
Normal 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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
250
src/tasks/day24.rs
Normal 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,3 +16,4 @@ pub mod day15;
|
||||
pub mod day20;
|
||||
pub mod day22;
|
||||
pub mod day23;
|
||||
pub mod day24;
|
||||
|
||||
Reference in New Issue
Block a user