diff --git a/lib/src/day16/mod.rs b/lib/src/day16/mod.rs new file mode 100644 index 0000000..2ea8959 --- /dev/null +++ b/lib/src/day16/mod.rs @@ -0,0 +1,194 @@ +use itertools::Itertools; + +use crate::utils::Day; + +pub struct Beam { + pub position: Position, + pub direction: Direction +} + +#[derive(Clone, PartialEq, Eq, Debug, PartialOrd, Ord)] +pub struct Position { + pub x: usize, + pub y: usize +} + +#[derive(Clone, PartialEq, Debug)] +pub enum Direction { + Up, + Down, + Right, + Left +} + +#[derive(Debug, Default)] +pub struct Day16 { + pub input: String, +} + +impl Day for Day16 { + fn part1(&mut self) -> String { + let grid = self.input.lines().map(|l| l.chars().collect_vec()).collect_vec(); + let mut heated = Vec::<(Position, Direction)>::new(); + let mut beams = Vec::from(const { [Beam { position: Position { x: 0, y: 0 }, direction: Direction::Right }] }); + + while beams.len() > 0 { + + // Step all beams + for mut beam in std::mem::take(&mut beams).into_iter() { + let already_heated = heated.iter().filter(|(pos, dir)| pos == &beam.position && dir == &beam.direction).collect_vec(); + + if already_heated.len() == 0 { + heated.push((beam.position.clone(), beam.direction.clone())); + } else { + continue; + } + + match grid[beam.position.y][beam.position.x] { + '/' => match beam.direction { + Direction::Up => beam.direction = Direction::Right, + Direction::Down => beam.direction = Direction::Left, + Direction::Right => beam.direction = Direction::Up, + Direction::Left => beam.direction = Direction::Down, + }, + '\\' => match beam.direction { + Direction::Up => beam.direction = Direction::Left, + Direction::Down => beam.direction = Direction::Right, + Direction::Right => beam.direction = Direction::Down, + Direction::Left => beam.direction = Direction::Up, + }, + '|' => match beam.direction { + Direction::Left | Direction::Right => { + if beam.position.y != 0 { beams.push(Beam { position: Position { x: beam.position.x, y: beam.position.y - 1 }, direction: Direction::Up }) } + if beam.position.y != grid.len() - 1 { beams.push(Beam { position: Position { x: beam.position.x, y: beam.position.y + 1 }, direction: Direction::Down }) } + continue + }, + _ => () + }, + '-' => match beam.direction { + Direction::Up | Direction::Down => { + if beam.position.x != 0 { beams.push(Beam { position: Position { x: beam.position.x - 1, y: beam.position.y }, direction: Direction::Left }) } + if beam.position.x != grid[0].len() - 1 { beams.push(Beam { position: Position { x: beam.position.x + 1, y: beam.position.y }, direction: Direction::Right }) } + continue + }, + _ => () + }, + _ => () + } + + match beam.direction { + Direction::Up if beam.position.y != 0 => beam.position.y -= 1, + Direction::Down if beam.position.y != grid.len() - 1 => beam.position.y += 1, + Direction::Right if beam.position.x != grid[0].len() - 1 => beam.position.x += 1, + Direction::Left if beam.position.x != 0 => beam.position.x -= 1, + _ => continue // If it hit the wall, skull issue, don't add it back to the vec + } + + beams.push(beam); + } + } + + // println!( + // "{}", + // grid + // .iter() + // .enumerate() + // .map(|(y, l)| l.iter().enumerate().map(|(x, b)| if heated.iter().any(|(pos, _)| pos.x == x && pos.y == y) { '#' } else { '.' }).collect::()) + // .collect::>() + // .join("\n") + // ); + + heated.into_iter().map(|(pos, _)| pos).sorted().dedup().count().to_string() + } + + // TODO Don't do this the stupid way, use DP/caching lol + fn part2(&mut self) -> String { + let grid = self.input.lines().map(|l| l.chars().collect_vec()).collect_vec(); + let mut max = 0usize; + + for y in 0..grid.len() { + for x in 0..grid[0].len() { + let dir = + if x == 0 { Direction::Left } + else if x == grid[0].len() - 1 { Direction::Right } + else if y == 0 { Direction::Down } + else if y == grid.len() - 1 { Direction::Up } + else { continue }; + + let mut heated = Vec::<(Position, Direction)>::new(); + let mut beams = Vec::from([Beam { position: Position { x, y }, direction: dir }]); + + while beams.len() > 0 { + + // Step all beams + for mut beam in std::mem::take(&mut beams).into_iter() { + let already_heated = heated.iter().filter(|(pos, dir)| pos == &beam.position && dir == &beam.direction).collect_vec(); + + if already_heated.len() == 0 { + heated.push((beam.position.clone(), beam.direction.clone())); + } else { + continue; + } + + match grid[beam.position.y][beam.position.x] { + '/' => match beam.direction { + Direction::Up => beam.direction = Direction::Right, + Direction::Down => beam.direction = Direction::Left, + Direction::Right => beam.direction = Direction::Up, + Direction::Left => beam.direction = Direction::Down, + }, + '\\' => match beam.direction { + Direction::Up => beam.direction = Direction::Left, + Direction::Down => beam.direction = Direction::Right, + Direction::Right => beam.direction = Direction::Down, + Direction::Left => beam.direction = Direction::Up, + }, + '|' => match beam.direction { + Direction::Left | Direction::Right => { + if beam.position.y != 0 { beams.push(Beam { position: Position { x: beam.position.x, y: beam.position.y - 1 }, direction: Direction::Up }) } + if beam.position.y != grid.len() - 1 { beams.push(Beam { position: Position { x: beam.position.x, y: beam.position.y + 1 }, direction: Direction::Down }) } + continue + }, + _ => () + }, + '-' => match beam.direction { + Direction::Up | Direction::Down => { + if beam.position.x != 0 { beams.push(Beam { position: Position { x: beam.position.x - 1, y: beam.position.y }, direction: Direction::Left }) } + if beam.position.x != grid[0].len() - 1 { beams.push(Beam { position: Position { x: beam.position.x + 1, y: beam.position.y }, direction: Direction::Right }) } + continue + }, + _ => () + }, + _ => () + } + + match beam.direction { + Direction::Up if beam.position.y != 0 => beam.position.y -= 1, + Direction::Down if beam.position.y != grid.len() - 1 => beam.position.y += 1, + Direction::Right if beam.position.x != grid[0].len() - 1 => beam.position.x += 1, + Direction::Left if beam.position.x != 0 => beam.position.x -= 1, + _ => continue // If it hit the wall, skull issue, don't add it back to the vec + } + + beams.push(beam); + } + } + + // println!( + // "{}", + // grid + // .iter() + // .enumerate() + // .map(|(y, l)| l.iter().enumerate().map(|(x, b)| if heated.iter().any(|(pos, _)| pos.x == x && pos.y == y) { '#' } else { '.' }).collect::()) + // .collect::>() + // .join("\n") + // ); + + let result = heated.into_iter().map(|(pos, _)| pos).sorted().dedup().count(); + if max < result { max = result; } + } + } + + max.to_string() + } +} diff --git a/lib/src/lib.rs b/lib/src/lib.rs index f4d60c6..0d6918f 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -10,6 +10,7 @@ use day12::Day12; use day13::Day13; use day14::Day14; use day15::Day15; +use day16::Day16; use day2::Day2; use day3::Day3; use day4::Day4; @@ -30,6 +31,7 @@ pub mod day12; pub mod day13; pub mod day14; pub mod day15; +pub mod day16; pub mod day2; pub mod day3; pub mod day4; @@ -76,6 +78,7 @@ pub fn get_day(day: u8, input: String) -> Box { 13 => Box::new(Day13 { input }), 14 => Box::new(Day14 { input }), 15 => Box::new(Day15 { input }), + 16 => Box::new(Day16 { input }), _ => panic!("Invalid day #"), } }