Created
April 26, 2024 03:24
-
-
Save SansPapyrus683/dfd4cad37eac5a52a35d0f8db3d6fa3f to your computer and use it in GitHub Desktop.
incomplete advent of code 2018 day 15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use itertools::Itertools; | |
use std::collections::{HashMap, HashSet, VecDeque}; | |
use std::fs; | |
fn neighbors(r: usize, c: usize) -> Vec<(usize, usize)> { | |
let (ir, ic) = (r as i32, c as i32); | |
vec![(ir + 1, ic), (ir - 1, ic), (ir, ic + 1), (ir, ic - 1)] | |
.iter() | |
.filter(|(r, c)| *r >= 0 && *c >= 0) | |
.map(|(r, c)| (*r as usize, *c as usize)) | |
.collect() | |
} | |
#[derive(Debug, Copy, Clone)] | |
struct Unit { | |
atk: i32, | |
hp: i32, | |
} | |
impl Default for Unit { | |
fn default() -> Self { | |
Self { atk: 3, hp: 200 } | |
} | |
} | |
struct Battle { | |
elves: HashMap<(usize, usize), Unit>, | |
goblins: HashMap<(usize, usize), Unit>, | |
walls: HashSet<(usize, usize)>, | |
} | |
struct BattleResults { | |
elves_won: bool, | |
flawless: bool, | |
rem_hp: u32, | |
rounds_taken: u32, | |
} | |
impl Battle { | |
fn sim(mut self) -> BattleResults { | |
let elf_size = self.elves.len(); | |
let goblin_size = self.goblins.len(); | |
let mut round = 0; | |
while !self.elves.is_empty() && !self.goblins.is_empty() { | |
// this is the worst design decision in the history of decision decisions, maybe ever | |
let tmp_elves = self.elves.clone(); | |
let tmp_goblins = self.goblins.clone(); | |
let mut everything: HashMap<_, _> = | |
tmp_elves.iter().map(|(p, u)| (*p, (*u, true))).collect(); | |
everything.extend( | |
tmp_goblins | |
.iter() | |
.map(|(p, u)| (*p, (*u, false))) | |
.collect::<HashMap<_, _>>(), | |
); | |
for (pos, (unit, is_elf)) in everything | |
.iter() | |
.sorted_by(|(pos1, _), (pos2, _)| pos1.cmp(pos2)) | |
{ | |
if unit.hp < 0 { | |
continue; | |
} | |
let mut bad = if *is_elf { &self.goblins } else { &self.elves }; | |
if bad.is_empty() { | |
break; | |
} | |
let adj = neighbors(pos.0, pos.1); | |
let mut has_bad: Vec<_> = bad | |
.iter() | |
.filter(|(p, u)| adj.contains(&(p.0, p.1))) | |
.map(|(p, u)| (u.hp, *p)) | |
.collect(); | |
if has_bad.is_empty() { | |
let mut frontier = VecDeque::from([*pos]); | |
let mut visited = HashMap::from([(*pos, 0)]); | |
while !frontier.is_empty() { | |
let curr = frontier.pop_front().unwrap(); | |
let curr_dist = visited[&curr]; | |
for (nr, nc) in neighbors(curr.0, curr.1) { | |
let n = (nr, nc); | |
if !self.elves.contains_key(&n) | |
&& !self.goblins.contains_key(&n) | |
&& !self.walls.contains(&n) | |
&& !visited.contains_key(&n) | |
{ | |
visited.insert(n, curr_dist + 1); | |
frontier.push_back(n); | |
} | |
} | |
} | |
let mut in_range = Vec::new(); | |
for (r, c) in bad.keys() { | |
for n in neighbors(*r, *c) { | |
if visited.contains_key(&n) { | |
in_range.push((visited[&n], n)); | |
} | |
} | |
} | |
if in_range.is_empty() { | |
continue; | |
} | |
let target = in_range.iter().min().unwrap().1; | |
let mut t_dist = HashMap::from([(target, 0)]); | |
frontier = VecDeque::from([target]); | |
while !frontier.is_empty() { | |
let curr = frontier.pop_front().unwrap(); | |
let curr_dist = visited[&curr]; | |
for (nr, nc) in neighbors(curr.0, curr.1) { | |
let n = (nr, nc); | |
if visited.contains_key(&n) && !t_dist.contains_key(&n) { | |
t_dist.insert(n, curr_dist + 1); | |
frontier.push_back(n); | |
} | |
} | |
} | |
let adj = neighbors(pos.0, pos.1); | |
let move_to = adj | |
.iter() | |
.filter(|p| visited.contains_key(&p)) | |
.map(|p| (t_dist[p], p)) | |
.min() | |
.unwrap(); | |
let mut good = if *is_elf { | |
&mut self.elves | |
} else { | |
&mut self.goblins | |
}; | |
good.insert(*move_to.1, good.remove(pos).unwrap()); | |
// stupid duplicated code...if only listcomps existed | |
has_bad = bad | |
.iter() | |
.filter(|(p, u)| adj.contains(&(p.0, p.1))) | |
.map(|(p, u)| (u.hp, *p)) | |
.collect(); | |
} | |
if !has_bad.is_empty() { | |
let (_, atk_pos) = has_bad | |
.iter() | |
.min_by(|(p1, _), (p2, _)| p1.cmp(p2)) | |
.unwrap(); | |
let mut to_atk = bad.get_mut(atk_pos).unwrap(); | |
to_atk.hp -= unit.atk; | |
if to_atk.hp <= 0 {} | |
} | |
} | |
round += 1; | |
break; | |
} | |
BattleResults { | |
elves_won: false, | |
flawless: false, | |
rem_hp: 0, | |
rounds_taken: round, | |
} | |
} | |
} | |
fn main() { | |
let read = fs::read_to_string("../input/day15.txt").unwrap(); | |
let mut elves = HashMap::new(); | |
let mut goblins = HashMap::new(); | |
let mut walls = HashSet::new(); | |
for (r, row) in read.lines().enumerate() { | |
for (c, char) in row.chars().enumerate() { | |
match char { | |
'G' => { | |
goblins.insert((r, c), Unit::default()); | |
} | |
'E' => { | |
elves.insert((r, c), Unit::default()); | |
} | |
'#' => { | |
walls.insert((r, c)); | |
} | |
_ => (), | |
} | |
} | |
} | |
let res = Battle { | |
elves, | |
goblins, | |
walls, | |
} | |
.sim(); | |
println!("initial outcome: {}", res.rem_hp); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment