Skip to content

Instantly share code, notes, and snippets.

@SansPapyrus683
Created April 26, 2024 03:24
Show Gist options
  • Save SansPapyrus683/dfd4cad37eac5a52a35d0f8db3d6fa3f to your computer and use it in GitHub Desktop.
Save SansPapyrus683/dfd4cad37eac5a52a35d0f8db3d6fa3f to your computer and use it in GitHub Desktop.
incomplete advent of code 2018 day 15
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