From 88b022007867da84f10ba5f5769fbc0bac4e30ff Mon Sep 17 00:00:00 2001 From: mirsella Date: Sat, 16 Dec 2023 13:46:47 +0100 Subject: [PATCH] add day16 --- 2023/Cargo.toml | 2 +- 2023/day16/.gitignore | 19 +++++ 2023/day16/Cargo.toml | 12 ++++ 2023/day16/build.rs | 28 ++++++++ 2023/day16/src/main.rs | 160 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 2023/day16/.gitignore create mode 100644 2023/day16/Cargo.toml create mode 100644 2023/day16/build.rs create mode 100644 2023/day16/src/main.rs diff --git a/2023/Cargo.toml b/2023/Cargo.toml index db661d1..ad1a414 100644 --- a/2023/Cargo.toml +++ b/2023/Cargo.toml @@ -17,7 +17,7 @@ members = [ "day13", "day14", "day15", - # "day16", + "day16", # "day17", # "day18", # "day19", diff --git a/2023/day16/.gitignore b/2023/day16/.gitignore new file mode 100644 index 0000000..e983cca --- /dev/null +++ b/2023/day16/.gitignore @@ -0,0 +1,19 @@ +input.txt +flamegraph.svg +perf.data* +### Rust +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + diff --git a/2023/day16/Cargo.toml b/2023/day16/Cargo.toml new file mode 100644 index 0000000..a1ea2de --- /dev/null +++ b/2023/day16/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "day16" +authors = ["mirsella "] +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +grid = { git = "https://github.com/becheran/grid" } +itertools = "0.12.0" diff --git a/2023/day16/build.rs b/2023/day16/build.rs new file mode 100644 index 0000000..b6a19a0 --- /dev/null +++ b/2023/day16/build.rs @@ -0,0 +1,28 @@ +use std::fs::File; +use std::io::{self, Read}; +use std::path::PathBuf; +use std::{env, fs}; + +fn replace_in_file(file_path: &PathBuf, old: &str, new: &str) -> io::Result<()> { + let mut contents = String::new(); + File::open(file_path)?.read_to_string(&mut contents)?; + let new_contents = contents.replace(old, new); + if contents != new_contents { + println!("Updating {}", file_path.display()); + fs::write(file_path, new_contents)?; + } + Ok(()) +} + +fn main() -> io::Result<()> { + let pkg_name = env::var("CARGO_PKG_NAME").unwrap(); + replace_in_file( + &"../Cargo.toml".into(), + &format!("# \"{pkg_name}\""), + &format!("\"{pkg_name}\""), + )?; + + replace_in_file(&"./Cargo.toml".into(), "\n[workspace]", "")?; + + Ok(()) +} diff --git a/2023/day16/src/main.rs b/2023/day16/src/main.rs new file mode 100644 index 0000000..283b373 --- /dev/null +++ b/2023/day16/src/main.rs @@ -0,0 +1,160 @@ +use grid::Grid; +use itertools::Itertools; +use std::{collections::BTreeSet, sync::Arc}; + +#[derive(Debug, Ord, PartialEq, PartialOrd, Eq, Copy, Clone)] +enum Direction { + Up, + Down, + Left, + Right, +} +/// x, y +impl From for (isize, isize) { + fn from(val: Direction) -> Self { + match val { + Direction::Up => (0, -1), + Direction::Down => (0, 1), + Direction::Left => (-1, 0), + Direction::Right => (1, 0), + } + } +} + +fn _print_energized_grid(grid: &Grid, energized: &BTreeSet<(isize, isize, Direction)>) { + let mut grid = grid.clone(); + for (x, y, _) in energized { + *grid.get_mut(*y as usize, *x as usize).unwrap() = '#'; + } + for row in grid.iter_rows() { + println!("{}", row.collect::()); + } + println!(); +} + +/// x, y current pos, direction the ray was going +fn fill_ray( + grid: &Grid, + history: &mut BTreeSet<(isize, isize, Direction)>, + x: isize, + y: isize, + direction: Direction, +) { + let c = match grid.get(y as usize, x as usize) { + Some(c) => c, + None => return, + }; + let (dx, dy) = direction.into(); + if !history.insert((x, y, direction)) { + return; + } + match c { + '|' if matches!(direction, Direction::Left | Direction::Right) => { + fill_ray(grid, history, x, y - 1, Direction::Up); + fill_ray(grid, history, x, y + 1, Direction::Down); + } + '-' if matches!(direction, Direction::Up | Direction::Down) => { + fill_ray(grid, history, x - 1, y, Direction::Left); + fill_ray(grid, history, x + 1, y, Direction::Right); + } + '/' => match direction { + Direction::Up => fill_ray(grid, history, x + 1, y, Direction::Right), + Direction::Down => fill_ray(grid, history, x - 1, y, Direction::Left), + Direction::Left => fill_ray(grid, history, x, y + 1, Direction::Down), + Direction::Right => fill_ray(grid, history, x, y - 1, Direction::Up), + }, + '\\' => match direction { + Direction::Down => fill_ray(grid, history, x + 1, y, Direction::Right), + Direction::Up => fill_ray(grid, history, x - 1, y, Direction::Left), + Direction::Right => fill_ray(grid, history, x, y + 1, Direction::Down), + Direction::Left => fill_ray(grid, history, x, y - 1, Direction::Up), + }, + _ => fill_ray(grid, history, x + dx, y + dy, direction), + } +} + +fn part1(input: &str) -> usize { + let grid = Grid::from_vec( + input.chars().filter(|&c| c != '\n').collect(), + input.find('\n').unwrap(), + ); + let mut energized_tiles = BTreeSet::new(); + fill_ray(&grid, &mut energized_tiles, 0, 0, Direction::Right); + // print_energized_grid(&grid, &energized_tiles); + energized_tiles + .into_iter() + .unique_by(|&(x, y, _)| (x, y)) + .count() +} +fn part2(input: &str) -> usize { + let grid = Grid::from_vec( + input.chars().filter(|&c| c != '\n').collect(), + input.find('\n').unwrap(), + ); + let grid = Arc::new(grid); + let starts = [ + (0..grid.cols() - 1, 0..1, Direction::Down), + ( + 0..grid.cols() - 1, + (grid.cols() - 1)..grid.cols(), + Direction::Up, + ), + (0..1, 0..grid.rows() - 1, Direction::Right), + ( + (grid.cols() - 1)..grid.cols(), + 0..grid.rows() - 1, + Direction::Left, + ), + ]; + let mut counts = Vec::with_capacity(grid.rows() * 2 + grid.cols() * 2); + for (x_range, y_range, direction) in starts.iter() { + for x in x_range.clone() { + for y in y_range.clone() { + let grid = grid.clone(); + let direction = *direction; + counts.push(std::thread::spawn(move || { + let mut energized_tiles = BTreeSet::new(); + fill_ray( + &grid, + &mut energized_tiles, + x as isize, + y as isize, + direction, + ); + energized_tiles + .iter() + .unique_by(|&(x, y, _)| (x, y)) + .count() + })) + } + } + } + counts.into_iter().map(|h| h.join().unwrap()).max().unwrap() +} +fn main() { + let input = include_str!("../input.txt"); + println!("Part 1: {}", part1(input)); + println!("Part 2: {}", part2(input)); +} + +#[cfg(test)] +mod tests { + const INPUT: &str = r".|...\.... +|.-.\..... +.....|-... +........|. +.......... +.........\ +..../.\\.. +.-.-/..|.. +.|....-|.\ +..//.|...."; + #[test] + fn part1() { + assert_eq!(super::part1(INPUT), 46); + } + #[test] + fn part2() { + assert_eq!(super::part2(INPUT), 51); + } +}