diff --git a/day12/src/main.rs b/day12/src/main.rs index bcd9154..7ba79af 100644 --- a/day12/src/main.rs +++ b/day12/src/main.rs @@ -1,33 +1,83 @@ +mod node; +use node::Node; + use aoc_shared::enums::Direction; use aoc_shared::grid::Grid as BaseGrid; use aoc_shared::impl_grid_newtype; +#[derive(Debug, Copy, Clone, PartialEq)] +enum CellType { + Start, + End, + Waypoint(u8), +} + +#[derive(Debug, Clone)] +struct Cell { + kind: CellType, + idx: usize, + coord: (usize, usize), +} + +impl Cell { + pub fn new(kind: CellType, idx: usize, coord: (usize, usize)) -> Self { + Cell { kind, idx, coord } + } + + pub fn get_height(&self) -> u8 { + match self.kind { + CellType::Start => 0, + CellType::End => 25, + CellType::Waypoint(c) => c, + } + } +} + +// ---------------------------------------------------------------------------- + #[derive(Debug)] pub struct Grid(BaseGrid); -impl_grid_newtype!(Grid, BaseGrid, char); +impl_grid_newtype!(Grid, BaseGrid, Cell); -impl Grid { +impl Grid { pub fn from_file_str(file_str: &str) -> Self { - let lines: Vec<&str> = file_str.lines().collect(); - let width = lines[0].len(); + let first_line = file_str.lines().next().unwrap(); + let width = first_line.len(); let mut grid = Grid::new(width); - lines - .into_iter() - .map(|line| line.chars()) - .for_each(|line_chars| grid.vec.append(&mut line_chars.collect::>())); + let mut idx = 0usize; + + for c in file_str.chars() { + let kind = match c { + 'S' => CellType::Start, + 'E' => CellType::End, + 'a'..='z' => CellType::Waypoint(c as u8 - b'a'), + '\r' | '\n' => continue, + _ => panic!("Invalid character: {c}"), + }; + + let (x, y) = (idx % width, idx / width); + let cell = Cell::new(kind, idx, (x, y)); + + grid.vec.push(cell); + + idx += 1; + } grid } - fn find_pos(&self, value: char) -> Option { - self.vec.iter().position(|item| *item == value) + fn find_pos(&self, value: CellType) -> Option { + self.vec.iter().position(|item| item.kind == value) } pub fn print(&self) { for r in 0usize..self.num_rows() { let range = self.row_first_idx(r)..=self.row_last_idx(r); - let line: String = self.vec[range].iter().collect(); + let line: String = self.vec[range] + .iter() + .map(|n| (n.get_height() + b'a') as char) + .collect(); println!("{}", line); } @@ -78,35 +128,20 @@ impl Grid { return false; } - // Is the character a lowercase letter? - let start_char = *start_char.unwrap(); - let end_char = *end_char.unwrap(); - if !(start_char.is_ascii_lowercase() && end_char.is_ascii_lowercase()) { + let start_char = start_char.unwrap(); + let end_char = end_char.unwrap(); + let start_elevation = start_char.get_height(); + let end_elevation = end_char.get_height(); + if (end_elevation < start_elevation) || end_elevation.abs_diff(start_elevation) > 1 { return false; } - // Is the elevation change 0 or 1? - let start_char = start_char - .to_digit(36) - .expect(&format!("Should be a digit: {}", start_char)); - let end_char = end_char - .to_digit(36) - .expect(&format!("Should be a digit: {}", end_char)); - let diff = u32::abs_diff(end_char, start_char); - if diff > 1 { - return false; - } - - let (start_x, start_y) = self.idx_xy(start); - let (end_x, end_y) = self.idx_xy(end); - let x_diff = usize::abs_diff(end_x, start_x); - let y_diff = usize::abs_diff(end_y, start_y); + let ((start_x, start_y), (end_x, end_y)) = (start_char.coord, end_char.coord); + let x_diff = end_x.abs_diff(start_x); + let y_diff = end_y.abs_diff(start_y); // Have we moved 0 or 1 in a cardinal direction? - match (x_diff, y_diff) { - (0, 0) | (0, 1) | (1, 0) => true, - _ => false, - } + matches!((x_diff, y_diff), (0, 0) | (0, 1) | (1, 0)) } fn find_moves(&self, start: usize) -> Vec { @@ -116,11 +151,9 @@ impl Grid { Direction::Left, Direction::Right, ] - .into_iter() - .map(|d| self.get_index_for_move(start, d)) - .filter(|m| m.is_some()) - .map(|m| m.unwrap()) - .collect() + .into_iter() + .filter_map(|d| self.get_index_for_move(start, d)) + .collect() } fn find_valid_moves(&self, start: usize) -> Vec { @@ -131,117 +164,7 @@ impl Grid { } fn has_valid_neighbor(&self, idx: usize) -> bool { - self.find_valid_moves(idx).len() > 0 - } - - fn filter_invalid(&mut self, from: usize) { - let (ch, col_indexes, row_indexes) = { - let ch = self.get(from).unwrap(); - let (col, row) = self.idx_xy(from); - let col_indexes = self.get_column_indexes(col); - let row_indexes = self.get_row_indexes(row); - - (ch, col_indexes, row_indexes) - }; - - self.vec = self.vec - .clone() - .into_iter() - .enumerate() - .map(|(idx, c)| { - if c != *ch { - return c; - } - - return if self.has_valid_neighbor(idx) && (col_indexes.contains(&idx) || row_indexes.contains(&idx)) { - c - } else { - '0' - } - }) - .collect(); - - } -} - -// ---------------------------------------------------------------------------- - -#[derive(Debug, Default, Clone)] -pub struct Node { - idx: usize, - parents: Vec, - children: Option>>, -} - -impl Node { - pub fn new(idx: usize) -> Self { - Node { - idx, - ..Node::default() - } - } - - pub fn add_child(&mut self, value: usize) -> &mut Self { - let mut child = Node::new(value); - child.parents.append(&mut self.parents.clone()); - child.parents.push(self.idx); - - self.append(child); - - self.children - .as_mut() - .expect("There should be a Vec here!") - .last_mut() - .expect("There should be a Box here!") - } - - fn append(&mut self, node: Node) -> &mut Self { - match &mut self.children { - Some(c) => { - c.push(Box::new(node)); - } - None => self.children = Some(vec![Box::new(node)]), - }; - - self - } - - fn is_leaf(&self) -> bool { - self.children.is_none() - } - - pub fn contains(&self, value: usize) -> bool { - if self.idx == value { - return true; - } - - return self.parents.contains(&value); - } - - pub fn get_leaves(&self) -> Vec<&Node> { - if self.is_leaf() { - return vec![self]; - } - - let mut leaves = Vec::new(); - - let children = self - .children - .as_ref() - .unwrap() - .iter() - .map(|boxed| boxed.as_ref()); - - for child in children { - let mut child_leaves = child.get_leaves(); - leaves.append(&mut child_leaves); - } - - leaves - } - - pub fn get_len(&self) -> usize { - self.parents.len() + !self.find_valid_moves(idx).is_empty() } } @@ -251,7 +174,7 @@ impl Node { pub struct Pathfinder { start_idx: usize, end_idx: usize, - grid: Grid, + grid: Grid, tree: Node, } @@ -263,28 +186,21 @@ impl Pathfinder { grid: Grid::from_file_str(file_str), tree: Node::default(), }; - pf.go_to_start(); + + let start = pf.grid.find_pos(CellType::Start).unwrap(); + let end = pf.grid.find_pos(CellType::End).unwrap(); + + pf.start_idx = start; + pf.end_idx = end; + pf.tree.idx = start; pf } - fn go_to_start(&mut self) { - let start = self.grid.find_pos('S').unwrap(); - let end = self.grid.find_pos('E').unwrap(); - - self.start_idx = start; - self.end_idx = end; - self.grid.vec[start] = 'a'; - self.grid.vec[end] = 'z'; - // self.grid.filter_invalid(start); - - self.tree.idx = start; - } - fn add_children(&mut self, node: &mut Node, idx: usize) { let possible_moves = self.grid.find_valid_moves(idx); - if possible_moves.len() == 0 { + if possible_moves.is_empty() { return; } @@ -293,9 +209,9 @@ impl Pathfinder { continue; } - let mut n = node.add_child(m); + let n = node.add_child(m); if m != self.end_idx { - self.add_children(&mut n, m); + self.add_children(n, m); } else { break; } @@ -310,7 +226,7 @@ impl Pathfinder { self.tree = tree; } - fn get_paths(&self) -> impl Iterator { + fn get_paths(&self) -> impl Iterator { self.tree .get_leaves() .into_iter()