day16: much cleaner, nicely factored solution, and much faster
This commit is contained in:
		
							
								
								
									
										245
									
								
								16/src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										245
									
								
								16/src/main.rs
									
									
									
									
									
								
							| @@ -28,12 +28,72 @@ fn main() { | |||||||
|  |  | ||||||
| // PARSE | // PARSE | ||||||
|  |  | ||||||
|  | enum Interaction { | ||||||
|  |     One(FromDirection), | ||||||
|  |     Two((FromDirection, FromDirection)), | ||||||
|  | } | ||||||
|  |  | ||||||
| #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] | ||||||
| enum FromDirection { | enum FromDirection { | ||||||
|     Left, |     Left = 0, | ||||||
|     Above, |     Above = 1, | ||||||
|     Right, |     Right = 2, | ||||||
|     Below, |     Below = 3, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl FromDirection { | ||||||
|  |     fn mask(&self) -> u8 { | ||||||
|  |         1 << *self as u8 | ||||||
|  |     } | ||||||
|  |     // return the new pos for a ray that will be from the direction. | ||||||
|  |     // a ray 'from' left 'goes' right | ||||||
|  |     fn goes_pos(&self, pos: (i64, i64)) -> (i64, i64) { | ||||||
|  |         match self { | ||||||
|  |             Self::Left => (pos.0 + 1, pos.1), | ||||||
|  |             Self::Right => (pos.0 - 1, pos.1), | ||||||
|  |             Self::Above => (pos.0, pos.1 + 1), | ||||||
|  |             Self::Below => (pos.0, pos.1 - 1), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     fn reflect_ne(&self) -> Self { | ||||||
|  |         match self { | ||||||
|  |             Self::Left => Self::Below, | ||||||
|  |             Self::Right => Self::Above, | ||||||
|  |             Self::Above => Self::Right, | ||||||
|  |             Self::Below => Self::Left, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     fn opposite(&self) -> FromDirection { | ||||||
|  |         match self { | ||||||
|  |             Self::Left => Self::Right, | ||||||
|  |             Self::Right => Self::Left, | ||||||
|  |             Self::Above => Self::Below, | ||||||
|  |             Self::Below => Self::Above, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     fn reflect_se(&self) -> FromDirection { | ||||||
|  |         self.reflect_ne().opposite() | ||||||
|  |     } | ||||||
|  |     fn interact(&self, tile: char) -> Interaction { | ||||||
|  |         match tile { | ||||||
|  |             '.' => Interaction::One(*self), | ||||||
|  |             '/' => Interaction::One(self.reflect_ne()), | ||||||
|  |             '\\' => Interaction::One(self.reflect_se()), | ||||||
|  |             '|' => match self { | ||||||
|  |                 FromDirection::Above | FromDirection::Below => Interaction::One(*self), | ||||||
|  |                 FromDirection::Left | FromDirection::Right => { | ||||||
|  |                     Interaction::Two((FromDirection::Above, FromDirection::Below)) | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             '-' => match self { | ||||||
|  |                 FromDirection::Left | FromDirection::Right => Interaction::One(*self), | ||||||
|  |                 FromDirection::Above | FromDirection::Below => { | ||||||
|  |                     Interaction::Two((FromDirection::Left, FromDirection::Right)) | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             c => unimplemented!("invalid tile {}", c), | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Display for FromDirection { | impl Display for FromDirection { | ||||||
| @@ -52,44 +112,55 @@ struct Contraption { | |||||||
| } | } | ||||||
|  |  | ||||||
| struct VisitState { | struct VisitState { | ||||||
|     energized: Vec<Vec<bool>>, |     visited_from: Vec<Vec<u8>>, | ||||||
|     visited_rays: HashSet<((i64, i64), FromDirection)>, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| impl VisitState { | impl VisitState { | ||||||
|     fn score(&self) -> u64 { |     fn visit(&mut self, pos: (i64, i64), dir: FromDirection) -> bool { | ||||||
|         self.energized.iter().flatten().filter(|c| **c).count() as u64 |         let pos_state = &mut self.visited_from[pos.1 as usize][pos.0 as usize]; | ||||||
|  |         if *pos_state & dir.mask() > 0 { | ||||||
|  |             false | ||||||
|  |         } else { | ||||||
|  |             *pos_state |= dir.mask(); | ||||||
|  |             true | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |     fn score(&self) -> u64 { | ||||||
|  |         self.visited_from | ||||||
|  |             .iter() | ||||||
|  |             .flatten() | ||||||
|  |             .filter(|c| **c != 0) | ||||||
|  |             .count() as u64 | ||||||
|  |     } | ||||||
|  |     #[allow(dead_code)] | ||||||
|     fn dump(&self) { |     fn dump(&self) { | ||||||
|         println!("Score {}:", self.score()); |         println!("Score {}:", self.score()); | ||||||
|         for line in &self.energized { |         for line in &self.visited_from { | ||||||
|             println!( |             println!( | ||||||
|                 "  {}", |                 "  {}", | ||||||
|                 String::from_iter(line.iter().map(|b| if *b { '#' } else { '.' })) |                 String::from_iter(line.iter().map(|b| if *b == 0 { '.' } else { '#' })) | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| impl Contraption { | impl Contraption { | ||||||
|     fn cast_ray<'a>( |     fn height(&self) -> i64 { | ||||||
|         &'a mut self, |         self.tiles.len() as i64 | ||||||
|         state: &'a mut VisitState, |     } | ||||||
|         pos: (i64, i64), |     fn width(&self) -> i64 { | ||||||
|         dir: FromDirection, |         self.tiles[0].len() as i64 | ||||||
|     ) { |     } | ||||||
|         let mut new_rays = self.cast_ray_inner(state, pos, dir); |     fn cast_ray<'a>(&'a mut self, pos: (i64, i64), dir: FromDirection) -> VisitState { | ||||||
|  |         let mut state = self.empty_state(); | ||||||
|         loop { |         let mut new_rays = self.cast_ray_inner(&mut state, pos, dir); | ||||||
|  |         while new_rays.len() != 0 { | ||||||
|             new_rays = new_rays |             new_rays = new_rays | ||||||
|                 .iter() |                 .iter() | ||||||
|                 .flat_map(|(pos, dir)| self.cast_ray_inner(state, *pos, *dir)) |                 .flat_map(|(pos, dir)| self.cast_ray_inner(&mut state, *pos, *dir)) | ||||||
|                 .collect(); |                 .collect(); | ||||||
|             if new_rays.len() == 0 { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|  |         state | ||||||
|     } |     } | ||||||
|     fn cast_ray_inner<'a>( |     fn cast_ray_inner<'a>( | ||||||
|         &'a mut self, |         &'a mut self, | ||||||
| @@ -97,68 +168,29 @@ impl Contraption { | |||||||
|         mut pos: (i64, i64), |         mut pos: (i64, i64), | ||||||
|         mut dir: FromDirection, |         mut dir: FromDirection, | ||||||
|     ) -> Vec<((i64, i64), FromDirection)> { |     ) -> Vec<((i64, i64), FromDirection)> { | ||||||
|         let width = state.energized[0].len(); |  | ||||||
|         let height = state.energized.len(); |  | ||||||
|         let mut new_rays = Vec::new(); |         let mut new_rays = Vec::new(); | ||||||
|         while pos.0 >= 0 && pos.1 >= 0 && pos.0 < width as i64 && pos.1 < height as i64 { |  | ||||||
|             // visit pos |         while pos.0 >= 0 && pos.1 >= 0 && pos.0 < self.width() && pos.1 < self.height() { | ||||||
|             state.energized[pos.1 as usize][pos.0 as usize] = true; |             if !state.visit(pos, dir) { | ||||||
|             if !state.visited_rays.insert((pos, dir)) { |  | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             (pos, dir) = match self.tiles[pos.1 as usize][pos.0 as usize] { |             dir = match dir.interact(self.tiles[pos.1 as usize][pos.0 as usize]) { | ||||||
|                 '.' => match dir { |                 Interaction::One(dir) => dir, | ||||||
|                     FromDirection::Left => ((pos.0 + 1, pos.1), dir), |                 Interaction::Two((dir1, dir2)) => { | ||||||
|                     FromDirection::Right => ((pos.0 - 1, pos.1), dir), |                     new_rays.push((pos, dir1)); | ||||||
|                     FromDirection::Above => ((pos.0, pos.1 + 1), dir), |                     dir2 | ||||||
|                     FromDirection::Below => ((pos.0, pos.1 - 1), dir), |                 } | ||||||
|                 }, |             }; | ||||||
|                 '/' => match dir { |             pos = dir.goes_pos(pos); | ||||||
|                     // from left, go up, from below |  | ||||||
|                     FromDirection::Left => ((pos.0, pos.1 - 1), FromDirection::Below), |  | ||||||
|                     // from up, go left, from the right |  | ||||||
|                     FromDirection::Above => ((pos.0 - 1, pos.1), FromDirection::Right), |  | ||||||
|                     // from right, go down, from above |  | ||||||
|                     FromDirection::Right => ((pos.0, pos.1 + 1), FromDirection::Above), |  | ||||||
|                     // from below, go right, from left |  | ||||||
|                     FromDirection::Below => ((pos.0 + 1, pos.1), FromDirection::Left), |  | ||||||
|                 }, |  | ||||||
|                 '\\' => match dir { |  | ||||||
|                     FromDirection::Left => ((pos.0, pos.1 + 1), FromDirection::Above), |  | ||||||
|                     FromDirection::Above => ((pos.0 + 1, pos.1), FromDirection::Left), |  | ||||||
|                     FromDirection::Right => ((pos.0, pos.1 - 1), FromDirection::Below), |  | ||||||
|                     FromDirection::Below => ((pos.0 - 1, pos.1), FromDirection::Right), |  | ||||||
|                 }, |  | ||||||
|                 '-' => match dir { |  | ||||||
|                     FromDirection::Left => ((pos.0 + 1, pos.1), dir), |  | ||||||
|                     FromDirection::Right => ((pos.0 - 1, pos.1), dir), |  | ||||||
|                     FromDirection::Above | FromDirection::Below => { |  | ||||||
|                         new_rays.push(((pos.0 + 1, pos.1), FromDirection::Left)); |  | ||||||
|                         ((pos.0 - 1, pos.1), FromDirection::Right) |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 '|' => match dir { |  | ||||||
|                     FromDirection::Above => ((pos.0, pos.1 + 1), dir), |  | ||||||
|                     FromDirection::Below => ((pos.0, pos.1 - 1), dir), |  | ||||||
|                     FromDirection::Left | FromDirection::Right => { |  | ||||||
|                         new_rays.push(((pos.0, pos.1 + 1), FromDirection::Above)); |  | ||||||
|                         ((pos.0, pos.1 - 1), FromDirection::Below) |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 c => unimplemented!("invalid character {}", c), |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|         new_rays |         new_rays | ||||||
|     } |     } | ||||||
|     fn empty_state(&self) -> VisitState { |     fn empty_state(&self) -> VisitState { | ||||||
|         let mut energized = Vec::new(); |         let mut visited_from = Vec::new(); | ||||||
|         for _ in 0..self.tiles.len() { |         for _ in 0..self.height() { | ||||||
|             energized.push(Vec::from_iter(repeat(false).take(self.tiles[0].len()))); |             visited_from.push(Vec::from_iter(repeat(0).take(self.width() as usize))); | ||||||
|         } |  | ||||||
|         VisitState { |  | ||||||
|             energized, |  | ||||||
|             visited_rays: HashSet::new(), |  | ||||||
|         } |         } | ||||||
|  |         VisitState { visited_from } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -176,48 +208,39 @@ impl<T: BufRead> From<Lines<T>> for Contraption { | |||||||
|  |  | ||||||
| fn problem1<T: BufRead>(input: Lines<T>) -> u64 { | fn problem1<T: BufRead>(input: Lines<T>) -> u64 { | ||||||
|     let mut contraption = Contraption::from(input); |     let mut contraption = Contraption::from(input); | ||||||
|     let mut state = contraption.empty_state(); |  | ||||||
|  |  | ||||||
|     contraption.cast_ray(&mut state, (0,0), FromDirection::Left); |     contraption.cast_ray((0, 0), FromDirection::Left).score() | ||||||
|  |  | ||||||
|     state.energized.iter().flatten().filter(|c| **c).count() as u64 |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // PROBLEM 2 solution | // PROBLEM 2 solution | ||||||
| fn problem2<T: BufRead>(input: Lines<T>) -> u64 { | fn problem2<T: BufRead>(input: Lines<T>) -> u64 { | ||||||
|     let mut contraption = Contraption::from(input); |     let mut contraption = Contraption::from(input); | ||||||
|     let mut max_tiles = 0u64; |  | ||||||
|  |  | ||||||
|     for y in 0..contraption.tiles.len() as i64 { |     let rows_max = (0..contraption.height()).fold(0, |max_tiles, y| { | ||||||
|         let mut left_state = contraption.empty_state(); |         std::cmp::max( | ||||||
|         contraption.cast_ray(&mut left_state, (0, y), FromDirection::Left); |  | ||||||
|         let mut right_state = contraption.empty_state(); |  | ||||||
|         contraption.cast_ray( |  | ||||||
|             &mut right_state, |  | ||||||
|             (contraption.tiles[0].len() as i64 - 1, y), |  | ||||||
|             FromDirection::Right, |  | ||||||
|         ); |  | ||||||
|         max_tiles = std::cmp::max( |  | ||||||
|             max_tiles, |             max_tiles, | ||||||
|             std::cmp::max(left_state.score(), right_state.score()), |             std::cmp::max( | ||||||
|         ); |                 contraption.cast_ray((0, y), FromDirection::Left).score(), | ||||||
|     } |                 contraption | ||||||
|     for x in 0..contraption.tiles[0].len() as i64 { |                     .cast_ray((contraption.width() - 1, y), FromDirection::Right) | ||||||
|         let mut top_state = contraption.empty_state(); |                     .score(), | ||||||
|         contraption.cast_ray(&mut top_state, (x, 0), FromDirection::Above); |             ), | ||||||
|         let mut bottom_state = contraption.empty_state(); |         ) | ||||||
|         contraption.cast_ray( |     }); | ||||||
|             &mut bottom_state, |  | ||||||
|             (x, contraption.tiles.len() as i64 - 1), |  | ||||||
|             FromDirection::Below, |  | ||||||
|         ); |  | ||||||
|         max_tiles = std::cmp::max( |  | ||||||
|             max_tiles, |  | ||||||
|             std::cmp::max(top_state.score(), bottom_state.score()), |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     max_tiles |     let cols_max = (0..contraption.width()).fold(0, |max_tiles, x| { | ||||||
|  |         std::cmp::max( | ||||||
|  |             max_tiles, | ||||||
|  |             std::cmp::max( | ||||||
|  |                 contraption.cast_ray((x, 0), FromDirection::Above).score(), | ||||||
|  |                 contraption | ||||||
|  |                     .cast_ray((x, contraption.height() - 1), FromDirection::Below) | ||||||
|  |                     .score(), | ||||||
|  |             ), | ||||||
|  |         ) | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     std::cmp::max(rows_max, cols_max) | ||||||
| } | } | ||||||
|  |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user