From 594ffdda222751e1042b36f7c6b14506740c95c2 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sun, 17 Dec 2023 00:58:00 -0500 Subject: day 17 --- src/bin/2023/day17.rs | 307 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/bin/2023/main.rs | 2 + 2 files changed, 309 insertions(+) create mode 100644 src/bin/2023/day17.rs (limited to 'src/bin') diff --git a/src/bin/2023/day17.rs b/src/bin/2023/day17.rs new file mode 100644 index 0000000..b1579ca --- /dev/null +++ b/src/bin/2023/day17.rs @@ -0,0 +1,307 @@ +#![allow(dead_code)] +#![allow(unused_variables)] + +use advent_of_code::prelude::*; + +fn add_offset( + row: Row, + col: Col, + row_offset: IRow, + col_offset: ICol, + max_row: Row, + max_col: Col, +) -> Option<(Row, Col)> { + if let (Some(row), Some(col)) = ( + row.0.checked_add_signed(row_offset.0), + col.0.checked_add_signed(col_offset.0), + ) { + let row = Row(row); + let col = Col(col); + if row < max_row && col < max_col { + return Some((row, col)); + } + } + None +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +enum Direction { + Up, + Down, + Left, + Right, +} + +impl Direction { + fn from_pos(row: Row, col: Col, new_row: Row, new_col: Col) -> Self { + if row.abs_diff(new_row) == Row(1) && col.abs_diff(new_col) == Col(0) + { + if row > new_row { + Self::Up + } else { + Self::Down + } + } else if col.abs_diff(new_col) == Col(1) + && row.abs_diff(new_row) == Row(0) + { + if col > new_col { + Self::Left + } else { + Self::Right + } + } else { + panic!("invalid direction ({row:?}, {col:?}) -> ({new_row:?}, {new_col:?})") + } + } + + fn horizontal(&self) -> bool { + matches!(self, Self::Left | Self::Right) + } + + fn increasing(&self) -> bool { + matches!(self, Self::Down | Self::Right) + } + + fn turns(&self) -> [Self; 2] { + match self { + Self::Up | Self::Down => [Self::Left, Self::Right], + Self::Left | Self::Right => [Self::Up, Self::Down], + } + } + + fn offset(&self) -> (IRow, ICol) { + match self { + Self::Up => (IRow(-1), ICol(0)), + Self::Down => (IRow(1), ICol(0)), + Self::Left => (IRow(0), ICol(-1)), + Self::Right => (IRow(0), ICol(1)), + } + } +} + +#[derive(Debug)] +pub struct Crucible { + map: Grid, +} + +impl + advent_of_code::graph::Graph< + (Row, Col, Option, u8), + (Row, Col), + > for Crucible +{ + type Edges = Vec<(Row, Col)>; + + fn edges(&self, v: (Row, Col, Option, u8)) -> Self::Edges { + let (row, col, direction, length) = v; + + if let Some(direction) = direction { + let mut edges: Vec<_> = direction + .turns() + .into_iter() + .filter_map(|direction| { + let offset = direction.offset(); + add_offset( + row, + col, + offset.0, + offset.1, + self.map.rows(), + self.map.cols(), + ) + }) + .collect(); + if length + 1 < 3 { + let offset = direction.offset(); + if let Some((row, col)) = add_offset( + row, + col, + offset.0, + offset.1, + self.map.rows(), + self.map.cols(), + ) { + edges.push((row, col)); + } + } + edges + } else { + [ + Direction::Up, + Direction::Down, + Direction::Left, + Direction::Right, + ] + .into_iter() + .filter_map(|direction| { + let offset = direction.offset(); + add_offset( + row, + col, + offset.0, + offset.1, + self.map.rows(), + self.map.cols(), + ) + }) + .collect() + } + } + + fn edge( + &self, + v: (Row, Col, Option, u8), + e: (Row, Col), + ) -> ((Row, Col, Option, u8), u64) { + let (row, col, direction, length) = v; + let (new_row, new_col) = e; + let new_direction = Direction::from_pos(row, col, new_row, new_col); + + ( + ( + new_row, + new_col, + Some(new_direction), + if direction == Some(new_direction) { + length + 1 + } else { + 0 + }, + ), + u64::from(self.map[new_row][new_col]), + ) + } +} + +#[derive(Debug)] +pub struct UltraCrucible { + map: Grid, +} + +impl + advent_of_code::graph::Graph< + (Row, Col, Option, u8), + (Row, Col), + > for UltraCrucible +{ + type Edges = Vec<(Row, Col)>; + + fn edges(&self, v: (Row, Col, Option, u8)) -> Self::Edges { + let (row, col, direction, length) = v; + + if let Some(direction) = direction { + let mut edges = vec![]; + if length + 1 >= 4 { + edges.extend(direction.turns().into_iter().filter_map( + |direction| { + let offset = direction.offset(); + add_offset( + row, + col, + offset.0, + offset.1, + self.map.rows(), + self.map.cols(), + ) + }, + )); + } + if length + 1 < 10 { + let offset = direction.offset(); + if let Some((row, col)) = add_offset( + row, + col, + offset.0, + offset.1, + self.map.rows(), + self.map.cols(), + ) { + edges.push((row, col)); + } + } + edges + } else { + [ + Direction::Up, + Direction::Down, + Direction::Left, + Direction::Right, + ] + .into_iter() + .filter_map(|direction| { + let offset = direction.offset(); + add_offset( + row, + col, + offset.0, + offset.1, + self.map.rows(), + self.map.cols(), + ) + }) + .collect() + } + } + + fn edge( + &self, + v: (Row, Col, Option, u8), + e: (Row, Col), + ) -> ((Row, Col, Option, u8), u64) { + let (row, col, direction, length) = v; + let (new_row, new_col) = e; + let new_direction = Direction::from_pos(row, col, new_row, new_col); + + ( + ( + new_row, + new_col, + Some(new_direction), + if direction == Some(new_direction) { + length + 1 + } else { + 0 + }, + ), + u64::from(self.map[new_row][new_col]), + ) + } +} + +pub fn parse(fh: File) -> Result> { + Ok(parse::digit_grid(parse::raw_lines(fh))) +} + +pub fn part1(map: Grid) -> Result { + let crucible = Crucible { map }; + let (weight, shortest_path) = + crucible.dijkstra((Row(0), Col(0), None, 0), |(row, col, _, _)| { + row == crucible.map.rows() - 1 && col == crucible.map.cols() - 1 + }); + Ok(weight.try_into().unwrap()) +} + +pub fn part2(map: Grid) -> Result { + let crucible = UltraCrucible { map }; + let (weight, shortest_path) = crucible.dijkstra( + (Row(0), Col(0), None, 0), + |(row, col, _, length)| { + row == crucible.map.rows() - 1 + && col == crucible.map.cols() - 1 + && length + 1 >= 4 + }, + ); + Ok(weight.try_into().unwrap()) +} + +#[test] +fn test() { + assert_eq!( + part1(parse(parse::data(2023, 17).unwrap()).unwrap()).unwrap(), + 1076 + ); + assert_eq!( + part2(parse(parse::data(2023, 17).unwrap()).unwrap()).unwrap(), + 1219 + ); +} diff --git a/src/bin/2023/main.rs b/src/bin/2023/main.rs index 842092a..25f7c4d 100644 --- a/src/bin/2023/main.rs +++ b/src/bin/2023/main.rs @@ -27,6 +27,7 @@ mod day13; mod day14; mod day15; mod day16; +mod day17; // NEXT MOD #[paw::main] @@ -49,6 +50,7 @@ fn main(opt: Opt) -> Result<()> { 14 => advent_of_code::day!(2023, opt.day, opt.puzzle, day14), 15 => advent_of_code::day!(2023, opt.day, opt.puzzle, day15), 16 => advent_of_code::day!(2023, opt.day, opt.puzzle, day16), + 17 => advent_of_code::day!(2023, opt.day, opt.puzzle, day17), // NEXT PART _ => panic!("unknown day {}", opt.day), } -- cgit v1.2.3-54-g00ecf