diff --git a/lib/src/day17/mod.rs b/lib/src/day17/mod.rs index b7f04bd..0b2752d 100644 --- a/lib/src/day17/mod.rs +++ b/lib/src/day17/mod.rs @@ -1,12 +1,13 @@ use crate::utils::Day; use array2d::Array2D; +use itertools::Itertools; #[derive(Debug, Default)] pub struct Day17 { pub input: String, } -#[derive(PartialEq)] +#[derive(PartialEq, Clone, Debug)] pub enum Direction { North, East, @@ -14,13 +15,38 @@ pub enum Direction { West, } +#[derive(Clone, Debug)] pub struct State { cost: usize, - x: usize, - y: usize, + row: usize, + column: usize, tentative_distance: Option, last_direction: Option, - moves_without_turn: Option, + moves_in_same_direction: Option, + prev: Option<(usize, usize)> +} + +impl Day17 { + pub fn calculate_node(current: &State, node: &mut State, direction: Direction) { + let new_distance = current.tentative_distance.unwrap_or_default() + node.cost; + if node.tentative_distance.is_some_and(|d| d > new_distance) || node.tentative_distance.is_none() { + node.prev = Some((current.row, current.column)); + node.tentative_distance = Some(new_distance); + node.last_direction = Some( + match (node.row as isize - current.row as isize, node.column as isize - current.column as isize) { + (0, -1) => Direction::West, + (0, 1) => Direction::East, + (1, 0) => Direction::South, + (-1, 0) => Direction::North, + _ => panic!() + } + ); + node.moves_in_same_direction = Some(match current.last_direction == node.last_direction { + true => current.moves_in_same_direction.unwrap() + 1, + false => 0 + }) + } + } } impl Day for Day17 { @@ -32,46 +58,111 @@ impl Day for Day17 { self.input.split('\n').enumerate().flat_map(|(i, str)| { str.chars().enumerate().map(move |(j, b)| State { cost: b.to_digit(10).unwrap() as usize, - x: i, - y: j, - tentative_distance: if i == 0 && j == 0 { - Some(b.to_digit(10).unwrap() as usize) - } else { - None - }, - moves_without_turn: if i == 0 && j == 0 { - Some(1) - } else { - None - }, - last_direction: if i == 0 && j == 0 { - Some(Direction::East) - } else { - None - }, + column: j, + row: i, + tentative_distance: None, + moves_in_same_direction: None, + last_direction: None, + prev: None }) }), rows, columns, ) .unwrap(); - let unvisited = Vec::<(usize, usize)>::from_iter( + let mut unvisited = Vec::<(usize, usize)>::from_iter( (0..columns).flat_map(|i| (0..rows).map(move |j| (i, j))), ); - let mut current = (0usize, 0usize); + let mut current_pos = (0usize, 0usize); loop { - let current = &parsed[current]; + let current = parsed[current_pos].clone(); + + if current.last_direction != Some(Direction::South) + && !(current.last_direction == Some(Direction::North) + && current.moves_in_same_direction == Some(2)) + && let Some(neighbor) = parsed.get_mut(current.row.wrapping_sub(1), current.column) + { + // Going north + Self::calculate_node(¤t, neighbor, Direction::North) + } + + if current.last_direction != Some(Direction::West) + && !(current.last_direction == Some(Direction::East) + && current.moves_in_same_direction == Some(2)) + && let Some(neighbor) = parsed.get_mut(current.row, current.column + 1) + { + // Going east + Self::calculate_node(¤t, neighbor, Direction::East) + } if current.last_direction != Some(Direction::North) - && !(current.last_direction == Some(Direction::West) && current.moves_without_turn == Some(3)) - && let Some(neighbor) = parsed.get_mut(current.x - 1, current.y) - { // Going west - + && !(current.last_direction == Some(Direction::South) + && current.moves_in_same_direction == Some(2)) + && let Some(neighbor) = parsed.get_mut(current.row + 1, current.column) + { + // Going south + Self::calculate_node(¤t, neighbor, Direction::South) + } + + if current.last_direction != Some(Direction::East) + && !(current.last_direction == Some(Direction::West) + && current.moves_in_same_direction == Some(2)) + && let Some(neighbor) = parsed.get_mut(current.row, current.column.wrapping_sub(1)) + { + // Going west + Self::calculate_node(¤t, neighbor, Direction::West) + } + + if (current.column, current.row) == (columns - 1, rows - 1) { + break; + } else { + unvisited.remove( + unvisited + .iter() + .find_position(|&&coords| coords == (current.row, current.column)) + .unwrap() + .0, + ); + current_pos = unvisited + .iter() + .filter_map(|&coords| { + parsed[coords] + .tentative_distance + .map(|distance| (coords.0, coords.1, distance)) + }) + .min_by_key(|x| x.2) + .map(|(row, column, _)| (row, column)) + .unwrap(); } } - todo!() + let mut path = Vec::<(usize, usize)>::new(); + let mut cursor = (columns - 1, rows - 1); + + while let Some(prev) = parsed[cursor].prev { + path.push(prev); + cursor = prev; + } + + for (y, row_iter) in parsed.rows_iter().enumerate() { + for (x, element) in row_iter.enumerate() { + if !path.contains(&(x, y)) { + print!("{}", element.cost); + } else { + print!("{}", match element.last_direction.as_ref() { + Some(Direction::North) => "^", + Some(Direction::East) => ">", + Some(Direction::South) => "v", + Some(Direction::West) => "<", + _ => "+" + }/*, element.moves_in_same_direction.unwrap_or_default() */); + } + } + println!(); + } + + parsed[(rows - 1, columns - 1)].tentative_distance.unwrap().to_string() } fn part2(&mut self) -> String {