generated from janezicmatej/aoc-template
	solution: day 17
This commit is contained in:
		
							
								
								
									
										13
									
								
								data/examples/17.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								data/examples/17.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					2413432311323
 | 
				
			||||||
 | 
					3215453535623
 | 
				
			||||||
 | 
					3255245654254
 | 
				
			||||||
 | 
					3446585845452
 | 
				
			||||||
 | 
					4546657867536
 | 
				
			||||||
 | 
					1438598798454
 | 
				
			||||||
 | 
					4457876987766
 | 
				
			||||||
 | 
					3637877979653
 | 
				
			||||||
 | 
					4654967986887
 | 
				
			||||||
 | 
					4564679986453
 | 
				
			||||||
 | 
					1224686865563
 | 
				
			||||||
 | 
					2546548887735
 | 
				
			||||||
 | 
					4322674655533
 | 
				
			||||||
							
								
								
									
										192
									
								
								src/bin/17.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								src/bin/17.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,192 @@
 | 
				
			|||||||
 | 
					use std::{
 | 
				
			||||||
 | 
					    collections::{BinaryHeap, HashMap},
 | 
				
			||||||
 | 
					    ops::Neg,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 | 
				
			||||||
 | 
					enum Direction {
 | 
				
			||||||
 | 
					    Up,
 | 
				
			||||||
 | 
					    Down,
 | 
				
			||||||
 | 
					    Left,
 | 
				
			||||||
 | 
					    Right,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Direction {
 | 
				
			||||||
 | 
					    const ALL: [Direction; 4] = [
 | 
				
			||||||
 | 
					        Direction::Up,
 | 
				
			||||||
 | 
					        Direction::Down,
 | 
				
			||||||
 | 
					        Direction::Left,
 | 
				
			||||||
 | 
					        Direction::Right,
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Neg for Direction {
 | 
				
			||||||
 | 
					    type Output = Self;
 | 
				
			||||||
 | 
					    fn neg(self) -> Self::Output {
 | 
				
			||||||
 | 
					        use Direction::*;
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Up => Down,
 | 
				
			||||||
 | 
					            Down => Up,
 | 
				
			||||||
 | 
					            Left => Right,
 | 
				
			||||||
 | 
					            Right => Left,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, PartialEq, Eq)]
 | 
				
			||||||
 | 
					struct State {
 | 
				
			||||||
 | 
					    heat: usize,
 | 
				
			||||||
 | 
					    position: (usize, usize),
 | 
				
			||||||
 | 
					    direction: Direction,
 | 
				
			||||||
 | 
					    steps: usize,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl PartialOrd for State {
 | 
				
			||||||
 | 
					    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
 | 
				
			||||||
 | 
					        Some(self.cmp(other))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Ord for State {
 | 
				
			||||||
 | 
					    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
 | 
				
			||||||
 | 
					        other
 | 
				
			||||||
 | 
					            .heat
 | 
				
			||||||
 | 
					            .cmp(&self.heat)
 | 
				
			||||||
 | 
					            .then_with(|| self.position.cmp(&other.position))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn find_path(
 | 
				
			||||||
 | 
					    grid: &[Vec<usize>],
 | 
				
			||||||
 | 
					    start: (usize, usize),
 | 
				
			||||||
 | 
					    target: (usize, usize),
 | 
				
			||||||
 | 
					    min_steps: usize,
 | 
				
			||||||
 | 
					    max_steps: usize,
 | 
				
			||||||
 | 
					) -> Option<usize> {
 | 
				
			||||||
 | 
					    let valid_indexing = |y: usize, x: usize| grid.get(y).and_then(|row| row.get(x)).is_some();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut heap = BinaryHeap::new();
 | 
				
			||||||
 | 
					    heap.push(State {
 | 
				
			||||||
 | 
					        heat: 0,
 | 
				
			||||||
 | 
					        position: (0, 0),
 | 
				
			||||||
 | 
					        direction: Direction::Right,
 | 
				
			||||||
 | 
					        steps: 0,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut heatmap = HashMap::new();
 | 
				
			||||||
 | 
					    heatmap.insert((start, Direction::Down, 0), 0);
 | 
				
			||||||
 | 
					    heatmap.insert((start, Direction::Up, 0), 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while let Some(State {
 | 
				
			||||||
 | 
					        heat,
 | 
				
			||||||
 | 
					        position,
 | 
				
			||||||
 | 
					        direction,
 | 
				
			||||||
 | 
					        steps,
 | 
				
			||||||
 | 
					    }) = heap.pop()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if position == target {
 | 
				
			||||||
 | 
					            return Some(heat);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if *heatmap
 | 
				
			||||||
 | 
					            .get(&(position, direction, steps))
 | 
				
			||||||
 | 
					            .unwrap_or(&usize::MAX)
 | 
				
			||||||
 | 
					            < heat
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let (y, x) = position;
 | 
				
			||||||
 | 
					        for d in Direction::ALL.iter().filter(|&x| *x != -direction) {
 | 
				
			||||||
 | 
					            if steps < min_steps && *d != direction {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let (ny, nx) = match d {
 | 
				
			||||||
 | 
					                Direction::Up => (y.wrapping_sub(1), x),
 | 
				
			||||||
 | 
					                Direction::Down => (y + 1, x),
 | 
				
			||||||
 | 
					                Direction::Left => (y, x.wrapping_sub(1)),
 | 
				
			||||||
 | 
					                Direction::Right => (y, x + 1),
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if !valid_indexing(ny, nx) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let new_steps = if *d == direction { steps + 1 } else { 1 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if new_steps > max_steps {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let state = State {
 | 
				
			||||||
 | 
					                heat: heat + grid[ny][nx],
 | 
				
			||||||
 | 
					                position: (ny, nx),
 | 
				
			||||||
 | 
					                direction: *d,
 | 
				
			||||||
 | 
					                steps: new_steps,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if *heatmap
 | 
				
			||||||
 | 
					                .get(&((ny, nx), *d, new_steps))
 | 
				
			||||||
 | 
					                .unwrap_or(&usize::MAX)
 | 
				
			||||||
 | 
					                > state.heat
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                heatmap.insert(((ny, nx), *d, new_steps), state.heat);
 | 
				
			||||||
 | 
					                heap.push(state);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    None
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn part_one(input: &str) -> Option<usize> {
 | 
				
			||||||
 | 
					    let grid: Vec<Vec<_>> = input
 | 
				
			||||||
 | 
					        .lines()
 | 
				
			||||||
 | 
					        .map(|x| {
 | 
				
			||||||
 | 
					            x.chars()
 | 
				
			||||||
 | 
					                .filter_map(|x| x.to_digit(10).map(|x| x as usize))
 | 
				
			||||||
 | 
					                .collect()
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let target = (grid.len() - 1, grid[0].len() - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    find_path(&grid, (0, 0), target, 0, 3)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn part_two(input: &str) -> Option<usize> {
 | 
				
			||||||
 | 
					    let grid: Vec<Vec<_>> = input
 | 
				
			||||||
 | 
					        .lines()
 | 
				
			||||||
 | 
					        .map(|x| {
 | 
				
			||||||
 | 
					            x.chars()
 | 
				
			||||||
 | 
					                .filter_map(|x| x.to_digit(10).map(|x| x as usize))
 | 
				
			||||||
 | 
					                .collect()
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let target = (grid.len() - 1, grid[0].len() - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    find_path(&grid, (0, 0), target, 4, 10)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					aoc::solution!(17);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_part_one() {
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            part_one(&aoc::template::read_file("examples", 17)),
 | 
				
			||||||
 | 
					            Some(102)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_part_two() {
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            part_two(&aoc::template::read_file("examples", 17)),
 | 
				
			||||||
 | 
					            Some(94)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user