From 1e9030960d3b42cfe10c9343b1adc52f3a7f31e2 Mon Sep 17 00:00:00 2001 From: Lucy Date: Wed, 11 Dec 2024 11:20:28 +0100 Subject: [PATCH] feat: day 8 --- Cargo.toml | 3 +- inputs/day08/example.txt | 12 +++ src/day08.rs | 188 +++++++++++++++++++++++++++++++++++++++ src/main.rs | 18 +++- 4 files changed, 216 insertions(+), 5 deletions(-) create mode 100644 inputs/day08/example.txt create mode 100644 src/day08.rs diff --git a/Cargo.toml b/Cargo.toml index 2c86cb5..4de3f8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [features] -full = ["day1", "day2", "day3", "day4", "day5", "day6", "day7"] +full = ["day1", "day2", "day3", "day4", "day5", "day6", "day7", "day8"] default = ["full"] day1 = [] day2 = [] @@ -13,6 +13,7 @@ day4 = [] day5 = [] day6 = [] day7 = [] +day8 = [] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/inputs/day08/example.txt b/inputs/day08/example.txt new file mode 100644 index 0000000..de0f909 --- /dev/null +++ b/inputs/day08/example.txt @@ -0,0 +1,12 @@ +............ +........0... +.....0...... +.......0.... +....0....... +......A..... +............ +............ +........A... +.........A.. +............ +............ \ No newline at end of file diff --git a/src/day08.rs b/src/day08.rs new file mode 100644 index 0000000..6ff8fb4 --- /dev/null +++ b/src/day08.rs @@ -0,0 +1,188 @@ +#![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::>(); + let dims = (input.lines().next().unwrap().len(), input.lines().count()); + debug!("dims: {dims:?}"); + let node_positions = char_positions + .iter() + .filter(|(c, _, _)| *c != '.') + .collect::>(); + let unique_nodes = node_positions + .iter() + .map(|(c, _, _)| *c) + .collect::>(); + 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::>() + .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::() + }) + .collect::>() + .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::>(); + let dims = (input.lines().next().unwrap().len(), input.lines().count()); + debug!("dims: {dims:?}"); + let node_positions = char_positions + .iter() + .filter(|(c, _, _)| *c != '.') + .collect::>(); + let unique_nodes = node_positions + .iter() + .map(|(c, _, _)| *c) + .collect::>(); + 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::>() + .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::() + }) + .collect::>() + .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) + } +} diff --git a/src/main.rs b/src/main.rs index e26d0d4..a0e4a18 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,8 @@ use std::{fmt::Display, fs::read_to_string, time::Instant}; +use color_eyre::eyre; +use tracing_subscriber::EnvFilter; + #[cfg(feature = "day1")] mod day01; #[cfg(feature = "day2")] @@ -14,10 +17,14 @@ mod day05; mod day06; #[cfg(feature = "day7")] mod day07; +#[cfg(feature = "day8")] +mod day08; -fn main() { - color_eyre::install().unwrap(); - tracing_subscriber::fmt().init(); +fn main() -> eyre::Result<()> { + color_eyre::install()?; + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .init(); #[cfg(feature = "day1")] solve_day(1, day01::pt1, day01::pt2); #[cfg(feature = "day2")] @@ -32,6 +39,9 @@ fn main() { solve_day(6, day06::pt1, day06::pt2); #[cfg(feature = "day7")] solve_day(7, day07::pt1, day07::pt2); + #[cfg(feature = "day8")] + solve_day(8, day08::pt1, day08::pt2); + Ok(()) } fn solve_day(day: u8, part_a: Fa, part_b: Fb) @@ -52,6 +62,6 @@ where } Err(_) => { tracing::warn!("could not open ./inputs/day{day:02}/input.txt"); - }, + } } }