generated from janezicmatej/aoc-template
	solution: day 16
This commit is contained in:
		
							
								
								
									
										10
									
								
								data/examples/16.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								data/examples/16.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					.|...\....
 | 
				
			||||||
 | 
					|.-.\.....
 | 
				
			||||||
 | 
					.....|-...
 | 
				
			||||||
 | 
					........|.
 | 
				
			||||||
 | 
					..........
 | 
				
			||||||
 | 
					.........\
 | 
				
			||||||
 | 
					..../.\\..
 | 
				
			||||||
 | 
					.-.-/..|..
 | 
				
			||||||
 | 
					.|....-|.\
 | 
				
			||||||
 | 
					..//.|....
 | 
				
			||||||
							
								
								
									
										173
									
								
								src/bin/16.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								src/bin/16.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,173 @@
 | 
				
			|||||||
 | 
					use std::collections::HashSet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
 | 
				
			||||||
 | 
					enum Direction {
 | 
				
			||||||
 | 
					    Up,
 | 
				
			||||||
 | 
					    Down,
 | 
				
			||||||
 | 
					    Left,
 | 
				
			||||||
 | 
					    Right,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Direction {
 | 
				
			||||||
 | 
					    fn next(&self, (y, x): (usize, usize)) -> (usize, usize) {
 | 
				
			||||||
 | 
					        use Direction::*;
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Up => (y.checked_sub(1).unwrap_or(usize::MAX), x),
 | 
				
			||||||
 | 
					            Down => (y + 1, x),
 | 
				
			||||||
 | 
					            Left => (y, x.checked_sub(1).unwrap_or(usize::MAX)),
 | 
				
			||||||
 | 
					            Right => (y, x + 1),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy)]
 | 
				
			||||||
 | 
					enum Mirror {
 | 
				
			||||||
 | 
					    Vertical,
 | 
				
			||||||
 | 
					    Horizontal,
 | 
				
			||||||
 | 
					    EvenSymmetric,
 | 
				
			||||||
 | 
					    OddSymmetric,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ParseMirrorError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl TryFrom<u8> for Mirror {
 | 
				
			||||||
 | 
					    type Error = ParseMirrorError;
 | 
				
			||||||
 | 
					    fn try_from(value: u8) -> Result<Self, Self::Error> {
 | 
				
			||||||
 | 
					        Ok(match value {
 | 
				
			||||||
 | 
					            b'|' => Self::Vertical,
 | 
				
			||||||
 | 
					            b'-' => Self::Horizontal,
 | 
				
			||||||
 | 
					            b'\\' => Self::EvenSymmetric,
 | 
				
			||||||
 | 
					            b'/' => Self::OddSymmetric,
 | 
				
			||||||
 | 
					            _ => return Err(ParseMirrorError),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Mirror {
 | 
				
			||||||
 | 
					    fn bounce(&self, d: &Direction) -> (Direction, Option<Direction>) {
 | 
				
			||||||
 | 
					        use Direction::*;
 | 
				
			||||||
 | 
					        use Mirror::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Vertical => match d {
 | 
				
			||||||
 | 
					                Up | Down => (*d, None),
 | 
				
			||||||
 | 
					                Left | Right => (Up, Some(Down)),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Horizontal => match d {
 | 
				
			||||||
 | 
					                Up | Down => (Left, Some(Right)),
 | 
				
			||||||
 | 
					                Left | Right => (*d, None),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            EvenSymmetric => (
 | 
				
			||||||
 | 
					                match d {
 | 
				
			||||||
 | 
					                    Up => Left,
 | 
				
			||||||
 | 
					                    Down => Right,
 | 
				
			||||||
 | 
					                    Left => Up,
 | 
				
			||||||
 | 
					                    Right => Down,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                None,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            OddSymmetric => (
 | 
				
			||||||
 | 
					                match d {
 | 
				
			||||||
 | 
					                    Up => Right,
 | 
				
			||||||
 | 
					                    Down => Left,
 | 
				
			||||||
 | 
					                    Left => Down,
 | 
				
			||||||
 | 
					                    Right => Up,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                None,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn solve_with_start(layout: &[Vec<Option<Mirror>>], start: (usize, usize, Direction)) -> usize {
 | 
				
			||||||
 | 
					    let mut queue = vec![start];
 | 
				
			||||||
 | 
					    let mut visited = HashSet::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while let Some((y, x, d)) = queue.pop() {
 | 
				
			||||||
 | 
					        let point = layout.get(y).and_then(|row| row.get(x));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if point.is_none() {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if !visited.insert((y, x, d)) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(Some(m)) = point {
 | 
				
			||||||
 | 
					            let (new_d, opt_new_d) = m.bounce(&d);
 | 
				
			||||||
 | 
					            let (ny, nx) = new_d.next((y, x));
 | 
				
			||||||
 | 
					            queue.push((ny, nx, new_d));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if let Some(od) = opt_new_d {
 | 
				
			||||||
 | 
					                let (ony, onx) = od.next((y, x));
 | 
				
			||||||
 | 
					                queue.push((ony, onx, od));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            let (ny, nx) = d.next((y, x));
 | 
				
			||||||
 | 
					            queue.push((ny, nx, d));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    HashSet::<(usize, usize)>::from_iter(visited.into_iter().map(|(y, x, _)| (y, x))).len()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn part_one(input: &str) -> Option<usize> {
 | 
				
			||||||
 | 
					    let layout: Vec<Vec<_>> = input
 | 
				
			||||||
 | 
					        .lines()
 | 
				
			||||||
 | 
					        .map(|x| x.as_bytes().iter().map(|&x| x.try_into().ok()).collect())
 | 
				
			||||||
 | 
					        .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Some(solve_with_start(&layout, (0, 0, Direction::Right)))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn part_two(input: &str) -> Option<usize> {
 | 
				
			||||||
 | 
					    let layout: Vec<Vec<_>> = input
 | 
				
			||||||
 | 
					        .lines()
 | 
				
			||||||
 | 
					        .map(|x| x.as_bytes().iter().map(|&x| x.try_into().ok()).collect())
 | 
				
			||||||
 | 
					        .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let h = layout.len();
 | 
				
			||||||
 | 
					    let w = layout[0].len();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut scores = Vec::with_capacity(2 * (h + w) + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for hh in 0..h {
 | 
				
			||||||
 | 
					        let left = solve_with_start(&layout, (hh, 0, Direction::Right));
 | 
				
			||||||
 | 
					        let right = solve_with_start(&layout, (hh, w - 1, Direction::Left));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        scores.push(left);
 | 
				
			||||||
 | 
					        scores.push(right);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for ww in 0..w {
 | 
				
			||||||
 | 
					        let downward = solve_with_start(&layout, (0, ww, Direction::Down));
 | 
				
			||||||
 | 
					        let upward = solve_with_start(&layout, (h - 1, ww, Direction::Up));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        scores.push(downward);
 | 
				
			||||||
 | 
					        scores.push(upward);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    scores.into_iter().max()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					aoc::solution!(16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_part_one() {
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            part_one(&aoc::template::read_file("examples", 16)),
 | 
				
			||||||
 | 
					            Some(46)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_part_two() {
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            part_two(&aoc::template::read_file("examples", 16)),
 | 
				
			||||||
 | 
					            Some(51)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user