diff --git a/14/src/main.rs b/14/src/main.rs index 7864210..8fd4665 100644 --- a/14/src/main.rs +++ b/14/src/main.rs @@ -31,51 +31,57 @@ fn main() { #[derive(Debug)] struct Platform { rows: Vec>, + width: usize, + height: usize, } impl From> for Platform { fn from(lines: Lines) -> Self { + let rows: Vec> = lines.map(|line| line.unwrap().chars().collect()).collect(); + let width = rows[0].len(); + let height = rows.len(); Self { - rows: lines.map(|line| line.unwrap().chars().collect()).collect(), + rows, + width, + height, } } } -enum Direction { - North, - South, - East, - West, -} - #[derive(Debug, PartialEq)] enum Axis { Rows, Columns, } -impl Direction { - fn reverse_direction(&self) -> bool { - match self { - Direction::North | Direction::West => false, - Direction::South | Direction::East => true, - } - } - fn axis(&self) -> Axis { - match self { - Direction::North | Direction::South => Axis::Columns, - Direction::East | Direction::West => Axis::Rows, - } - } +struct North; +struct South; +struct East; +struct West; + +trait Direction { + const REVERSED: bool; + const AXIS: Axis; +} + +impl Direction for North { + const REVERSED: bool = false; + const AXIS: Axis = Axis::Columns; +} +impl Direction for South { + const REVERSED: bool = true; + const AXIS: Axis = Axis::Columns; +} +impl Direction for East { + const REVERSED: bool = true; + const AXIS: Axis = Axis::Rows; +} +impl Direction for West { + const REVERSED: bool = false; + const AXIS: Axis = Axis::Rows; } impl<'a> Platform { - fn width(&self) -> usize { - self.rows[0].len() - } - fn height(&self) -> usize { - self.col(0).len() - } fn col(&self, col: usize) -> Vec { self.rows.iter().map(|row| row[col]).collect() } @@ -84,97 +90,94 @@ impl<'a> Platform { self.rows[row_idx][idx] = col[row_idx]; } } - fn roll(&mut self, dir: &Direction) { - match dir.axis() { + fn roll(&mut self) { + match T::AXIS { Axis::Columns => { - for idx in 0..self.width() { - self.roll_one(idx, dir) + for idx in 0..self.width { + let new = self.roll_one::(idx); + self.replace_col(idx, new); } } - Axis::Rows => { - for idx in 0..self.height() { - self.roll_one(idx, dir) + for idx in 0..self.height { + let new = self.roll_one::(idx); + self.rows[idx].copy_from_slice(new.as_slice()); } } } } - fn roll_rock_at(old: &mut Vec, inner_idx: usize, dir: &Direction) { + fn roll_rock_at(old: &mut Vec, inner_idx: usize) { // Find the first # rock or the edge of the map, we can't look beyond this for a resting position - match dir.reverse_direction() { - true => { - let upper_limit = (inner_idx..old.len()) - .find(|c| old[*c] == '#') - .unwrap_or(old.len()); + if T::REVERSED { + // The furthest possible resting position is the first # below us, or the edge of the map + let upper_limit = (inner_idx..old.len() - 1) + .find(|c| old[*c] == '#') + .unwrap_or(old.len()); - if let Some(empty_pos) = (inner_idx..upper_limit).filter(|i| old[*i] == '.').last() - { - old.swap(inner_idx, empty_pos); - } + // We will roll to the position of the furthest '.' before the upper limit + if let Some(empty_pos) = (inner_idx..upper_limit).rev().find(|i| old[*i] == '.') { + old.swap(inner_idx, empty_pos); } - false => { - // Find the first # rock or the edge of the map, we can't look beyond this for a resting position - let lower_limit = (0..inner_idx).rev().find(|c| old[*c] == '#').unwrap_or(0); + } else { + // the furthest possible resting position is the first # above us, or the edge + let lower_limit = (0..inner_idx).rev().find(|c| old[*c] == '#').unwrap_or(0); - if let Some(empty_pos) = (lower_limit..inner_idx) - .rev() - .filter(|i| old[*i] == '.') - .last() - { - old.swap(inner_idx, empty_pos); - } + // We will roll to the position of the furthest '.' before the lower limit + if let Some(empty_pos) = (lower_limit..inner_idx).find(|i| old[*i] == '.') { + old.swap(inner_idx, empty_pos); } } } - fn roll_one(&mut self, idx: usize, dir: &Direction) { - let mut old = match dir.axis() { + + fn roll_one(&mut self, idx: usize) -> Vec { + let mut old = match T::AXIS { Axis::Columns => self.col(idx), Axis::Rows => self.rows[idx].clone(), }; - let idx_iter: Either<_, _> = match dir.reverse_direction() { + let idx_iter: Either<_, _> = match T::REVERSED { true => Either::Left((0..old.len()).rev()), false => Either::Right(0..old.len()), }; + let end_idx = if T::REVERSED { old.len() } else { 0 }; for inner_idx in idx_iter { match old[inner_idx] { '.' | '#' => continue, - 'O' if dir.reverse_direction() && inner_idx == old.len() => continue, - 'O' if !dir.reverse_direction() && inner_idx == 0 => continue, - 'O' => Self::roll_rock_at(&mut old, inner_idx, dir), + 'O' if inner_idx == end_idx => continue, + 'O' => Self::roll_rock_at::(&mut old, inner_idx), _ => panic!("invalid character"), } } - match dir.axis() { - Axis::Columns => self.replace_col(idx, old), - Axis::Rows => self.rows[idx] = old, + old + } + fn score(&self) -> u64 { + match T::AXIS { + Axis::Columns => self.score_columns::(), + Axis::Rows => self.score_rows::(), } } - fn score(&self, dir: &Direction) -> u64 { - match dir.axis() { - Axis::Columns => self.score_columns(dir), - Axis::Rows => self.score_rows(dir), - } - } - fn row_or_column_score(&self, idx: usize, dir: &Direction) -> usize { - if dir.reverse_direction() { + fn row_or_column_score(&self, idx: usize) -> usize { + if T::REVERSED { idx + 1 } else { - self.rows.len() - idx + match T::AXIS { + Axis::Rows => self.width - idx, + Axis::Columns => self.height - idx, + } } } - fn score_columns(&self, dir: &Direction) -> u64 { - (0..self.height()) + fn score_columns(&self) -> u64 { + (0..self.height) .map(|row| { self.rows[row].iter().filter(|c| **c == 'O').count() - * self.row_or_column_score(row, dir) + * self.row_or_column_score::(row) }) .sum::() as u64 } fn roll_cycle(&mut self) { - self.roll(&Direction::North); - self.roll(&Direction::West); - self.roll(&Direction::South); - self.roll(&Direction::East); + self.roll::(); + self.roll::(); + self.roll::(); + self.roll::(); } // find the first loop, return the iteration count when we first saw it and when we saw it again @@ -190,7 +193,7 @@ impl<'a> Platform { } } } - fn score_rows(&self, dir: &Direction) -> u64 { + fn score_rows(&self) -> u64 { unimplemented!() } } @@ -211,8 +214,8 @@ impl Display for Platform { fn problem1(input: Lines) -> u64 { let mut p = Platform::from(input); - p.roll(&Direction::North); - p.score(&Direction::North) + p.roll::(); + p.score::() } // PROBLEM 2 solution @@ -223,11 +226,12 @@ fn problem2(input: Lines) -> u64 { let loop_length = first_loop.1 - first_loop.0; let cycles_to_skip = ((ITERATIONS - first_loop.0) / loop_length) * loop_length; let iterations_remaining = ITERATIONS - first_loop.0 - cycles_to_skip; + for _ in 0..iterations_remaining { p.roll_cycle(); } - p.score(&Direction::North) + p.score::() } #[cfg(test)]