Solve 06
This commit is contained in:
		
							parent
							
								
									84dba789f8
								
							
						
					
					
						commit
						d21a6a6d97
					
				
					 1 changed files with 181 additions and 0 deletions
				
			
		
							
								
								
									
										181
									
								
								src/bin/06.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								src/bin/06.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,181 @@ | |||
| use aoc::*; | ||||
| use glam::IVec2; | ||||
| use std::collections::HashMap; | ||||
| use std::iter; | ||||
| 
 | ||||
| const INPUT: &str = include_str!("../../input/06"); | ||||
| 
 | ||||
| fn main() { | ||||
|     assert_example!(part1, "06-test", 41); | ||||
|     println!("Part 1: {}", part1(INPUT)); | ||||
|     assert_example!(part2, "06-test", 6); | ||||
|     println!("Part 2: {}", part2(INPUT)); | ||||
| } | ||||
| 
 | ||||
| fn part1(input: &str) -> usize { | ||||
|     Lab::parse(input).patrol().count_visited() | ||||
| } | ||||
| 
 | ||||
| fn part2(input: &str) -> usize { | ||||
|     let lab = Lab::parse(input); | ||||
| 
 | ||||
|     lab.clone() | ||||
|         .patrol() | ||||
|         .tiles | ||||
|         .into_iter() | ||||
|         .filter_map(|(pos, tile)| match tile { | ||||
|             Tile::Visited => Some(pos), | ||||
|             _ => None, | ||||
|         }) | ||||
|         .filter(|&modification| modification != lab.start) | ||||
|         .filter(|&modification| { | ||||
|             let mut lab = lab.clone(); | ||||
|             lab.place_wall(modification); | ||||
|             lab.loops() | ||||
|         }) | ||||
|         .count() | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, PartialEq, Eq, Debug)] | ||||
| enum Tile { | ||||
|     Start, | ||||
|     Empty, | ||||
|     Wall, | ||||
|     Visited, | ||||
| } | ||||
| 
 | ||||
| impl Tile { | ||||
|     fn parse(input: char) -> Self { | ||||
|         match input { | ||||
|             '^' => Tile::Start, | ||||
|             '.' => Tile::Empty, | ||||
|             '#' => Tile::Wall, | ||||
|             other => panic!("unexpected tile '{other}'"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| struct Lab { | ||||
|     start: IVec2, | ||||
|     tiles: HashMap<IVec2, Tile>, | ||||
|     size: IVec2, | ||||
| } | ||||
| 
 | ||||
| impl Lab { | ||||
|     fn patrol(mut self) -> Self { | ||||
|         let mut pos = self.start; | ||||
|         let mut dir = Direction::Up; | ||||
| 
 | ||||
|         while self.contains(pos) { | ||||
|             self.mark_visited(pos); | ||||
|             (pos, dir) = self.advance(pos, dir); | ||||
|         } | ||||
| 
 | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     /// <https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_tortoise_and_hare>
 | ||||
|     fn loops(&self) -> bool { | ||||
|         let (mut tortoise_pos, mut tortoise_dir) = (self.start, Direction::Up); | ||||
|         let (mut hare_pos, mut hare_dir) = (self.start, Direction::Up); | ||||
| 
 | ||||
|         loop { | ||||
|             (tortoise_pos, tortoise_dir) = self.advance(tortoise_pos, tortoise_dir); | ||||
|             (hare_pos, hare_dir) = self.advance(hare_pos, hare_dir); | ||||
|             (hare_pos, hare_dir) = self.advance(hare_pos, hare_dir); | ||||
| 
 | ||||
|             if !self.contains(hare_pos) { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             if hare_pos == tortoise_pos && hare_dir == tortoise_dir { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn advance(&self, pos: IVec2, mut dir: Direction) -> (IVec2, Direction) { | ||||
|         while self.looking_at_wall(pos, dir) { | ||||
|             dir = dir.rotate(); | ||||
|         } | ||||
|         (pos + dir.vec(), dir) | ||||
|     } | ||||
| 
 | ||||
|     fn place_wall(&mut self, pos: IVec2) { | ||||
|         self.tiles.insert(pos, Tile::Wall); | ||||
|     } | ||||
| 
 | ||||
|     fn mark_visited(&mut self, pos: IVec2) { | ||||
|         self.tiles.insert(pos, Tile::Visited); | ||||
|     } | ||||
| 
 | ||||
|     fn contains(&self, position: IVec2) -> bool { | ||||
|         position.x <= self.size.x && position.y <= self.size.y && position.x >= 0 && position.y >= 0 | ||||
|     } | ||||
| 
 | ||||
|     fn looking_at_wall(&self, position: IVec2, d: Direction) -> bool { | ||||
|         let check = position + d.vec(); | ||||
|         self.tiles.get(&check) == Some(&Tile::Wall) | ||||
|     } | ||||
| 
 | ||||
|     fn count_visited(&self) -> usize { | ||||
|         self.tiles | ||||
|             .values() | ||||
|             .filter(|x| matches!(x, Tile::Visited)) | ||||
|             .count() | ||||
|     } | ||||
| 
 | ||||
|     fn parse(input: &str) -> Self { | ||||
|         let tiles: HashMap<_, _> = input | ||||
|             .lines() | ||||
|             .enumerate() | ||||
|             .flat_map(|(y, line)| line.chars().enumerate().zip(iter::repeat(y))) | ||||
|             .map(|((x, c), y)| { | ||||
|                 let pos = IVec2::new(x as i32, y as i32); | ||||
|                 let tile = Tile::parse(c); | ||||
|                 (pos, tile) | ||||
|             }) | ||||
|             .collect(); | ||||
| 
 | ||||
|         let start = *tiles | ||||
|             .iter() | ||||
|             .find(|(_, c)| matches!(c, Tile::Start)) | ||||
|             .unwrap() | ||||
|             .0; | ||||
| 
 | ||||
|         let size = tiles.keys().fold(IVec2::new(0, 0), |a, b| { | ||||
|             IVec2::new(a.x.max(b.x), a.y.max(b.y)) | ||||
|         }); | ||||
| 
 | ||||
|         Self { tiles, start, size } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Debug)] | ||||
| enum Direction { | ||||
|     Up, | ||||
|     Right, | ||||
|     Down, | ||||
|     Left, | ||||
| } | ||||
| 
 | ||||
| impl Direction { | ||||
|     fn rotate(self) -> Direction { | ||||
|         match self { | ||||
|             Direction::Up => Direction::Right, | ||||
|             Direction::Right => Direction::Down, | ||||
|             Direction::Down => Direction::Left, | ||||
|             Direction::Left => Direction::Up, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn vec(self) -> IVec2 { | ||||
|         match self { | ||||
|             Direction::Up => IVec2::new(0, -1), | ||||
|             Direction::Right => IVec2::new(1, 0), | ||||
|             Direction::Down => IVec2::new(0, 1), | ||||
|             Direction::Left => IVec2::new(-1, 0), | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue