aoc2024/src/day08.rs
2024-12-11 11:20:28 +01:00

189 lines
6.5 KiB
Rust

#![allow(clippy::cast_sign_loss, clippy::cast_possible_wrap)]
use std::collections::HashSet;
use tracing::{debug, trace};
pub fn pt1(input: &str) -> usize {
let char_positions = input
.lines()
.map(str::chars)
.enumerate()
.flat_map(|(y, line)| line.enumerate().map(move |(x, char)| (char, x, y)))
.collect::<Vec<_>>();
let dims = (input.lines().next().unwrap().len(), input.lines().count());
debug!("dims: {dims:?}");
let node_positions = char_positions
.iter()
.filter(|(c, _, _)| *c != '.')
.collect::<Vec<_>>();
let unique_nodes = node_positions
.iter()
.map(|(c, _, _)| *c)
.collect::<HashSet<_>>();
let mut antinode_positions = HashSet::<(usize, usize)>::new();
for node in unique_nodes {
let matching_node_positions = node_positions
.iter()
.filter(|(c, _, _)| *c == node)
.collect::<Vec<_>>()
.into_iter();
for (_, node_x, node_y) in matching_node_positions.clone() {
for (_, other_node_x, other_node_y) in matching_node_positions.clone() {
trace!(
"comparing ({node_x}, {node_y}) with ({other_node_x}, {other_node_y}): {:?}, {:?}",
node_x.cmp(other_node_x), node_y.cmp(other_node_y)
);
if node_x == other_node_x && node_y == other_node_y {
continue;
}
let dx = *node_x as isize - *other_node_x as isize;
let dy = *node_y as isize - *other_node_y as isize;
let prev_x = *node_x as isize + dx;
let prev_y = *node_y as isize + dy;
let post_x = *other_node_x as isize - dx;
let post_y = *other_node_y as isize - dy;
if prev_x >= 0
&& (prev_x as usize) < dims.0
&& prev_y >= 0
&& (prev_y as usize) < dims.1
{
antinode_positions.insert((prev_x as usize, prev_y as usize));
}
if post_x >= 0
&& (post_x as usize) < dims.0
&& post_y >= 0
&& (post_y as usize) < dims.1
{
antinode_positions.insert((post_x as usize, post_y as usize));
}
}
}
debug!("antinodes for {node}: {antinode_positions:?}");
}
let new_map = input
.lines()
.enumerate()
.map(|(y, line)| {
line.chars()
.enumerate()
.map(|(x, c)| {
if c != '.' || !antinode_positions.contains(&(x, y)) {
c
} else {
'#'
}
})
.collect::<String>()
})
.collect::<Vec<_>>()
.join("\n");
debug!("new Map: \n{new_map}");
antinode_positions.len()
}
// 225 is too high
pub fn pt2(input: &str) -> usize {
let char_positions = input
.lines()
.map(str::chars)
.enumerate()
.flat_map(|(y, line)| line.enumerate().map(move |(x, char)| (char, x, y)))
.collect::<Vec<_>>();
let dims = (input.lines().next().unwrap().len(), input.lines().count());
debug!("dims: {dims:?}");
let node_positions = char_positions
.iter()
.filter(|(c, _, _)| *c != '.')
.collect::<Vec<_>>();
let unique_nodes = node_positions
.iter()
.map(|(c, _, _)| *c)
.collect::<HashSet<_>>();
let mut antinode_positions = HashSet::<(usize, usize)>::new();
for node in unique_nodes {
let matching_node_positions = node_positions
.iter()
.filter(|(c, _, _)| *c == node)
.collect::<Vec<_>>()
.into_iter();
for (_, node_x, node_y) in matching_node_positions.clone() {
for (_, other_node_x, other_node_y) in matching_node_positions.clone() {
trace!(
"comparing ({node_x}, {node_y}) with ({other_node_x}, {other_node_y}): {:?}, {:?}",
node_x.cmp(other_node_x), node_y.cmp(other_node_y)
);
if node_x == other_node_x && node_y == other_node_y {
continue;
}
let dx = *node_x as isize - *other_node_x as isize;
let dy = *node_y as isize - *other_node_y as isize;
let mut prev_x = *node_x as isize;
let mut prev_y = *node_y as isize;
while prev_x >= 0
&& (prev_x as usize) < dims.0
&& prev_y >= 0
&& (prev_y as usize) < dims.1
{
antinode_positions.insert((prev_x as usize, prev_y as usize));
prev_x += dx;
prev_y += dy;
}
let mut post_x = *other_node_x as isize;
let mut post_y = *other_node_y as isize;
while post_x >= 0
&& (post_x as usize) < dims.0
&& post_y >= 0
&& (post_y as usize) < dims.1
{
antinode_positions.insert((post_x as usize, post_y as usize));
post_x -= dx;
post_y -= dy;
}
}
}
debug!("antinodes for {node}: {antinode_positions:?}");
}
let new_map = input
.lines()
.enumerate()
.map(|(y, line)| {
line.chars()
.enumerate()
.map(|(x, c)| {
if c != '.' || !antinode_positions.contains(&(x, y)) {
c
} else {
'#'
}
})
.collect::<String>()
})
.collect::<Vec<_>>()
.join("\n");
debug!("new Map: \n{new_map}");
antinode_positions.len()
}
#[cfg(test)]
mod tests {
use tracing_test::traced_test;
use super::{pt1, pt2};
use std::fs::read_to_string;
#[traced_test]
#[test]
pub fn test_pt1() {
let input =
read_to_string("./inputs/day08/example.txt").expect("Missing example.txt for day08");
assert_eq!(pt1(&input), 14)
}
#[traced_test]
#[test]
pub fn test_pt2() {
let input =
read_to_string("./inputs/day08/example.txt").expect("Missing example.txt for day08");
assert_eq!(pt2(&input), 34)
}
}