generated from janezicmatej/aoc-template
	solution: day 23
This commit is contained in:
		
							
								
								
									
										23
									
								
								data/examples/23.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								data/examples/23.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					#.#####################
 | 
				
			||||||
 | 
					#.......#########...###
 | 
				
			||||||
 | 
					#######.#########.#.###
 | 
				
			||||||
 | 
					###.....#.>.>.###.#.###
 | 
				
			||||||
 | 
					###v#####.#v#.###.#.###
 | 
				
			||||||
 | 
					###.>...#.#.#.....#...#
 | 
				
			||||||
 | 
					###v###.#.#.#########.#
 | 
				
			||||||
 | 
					###...#.#.#.......#...#
 | 
				
			||||||
 | 
					#####.#.#.#######.#.###
 | 
				
			||||||
 | 
					#.....#.#.#.......#...#
 | 
				
			||||||
 | 
					#.#####.#.#.#########v#
 | 
				
			||||||
 | 
					#.#...#...#...###...>.#
 | 
				
			||||||
 | 
					#.#.#v#######v###.###v#
 | 
				
			||||||
 | 
					#...#.>.#...>.>.#.###.#
 | 
				
			||||||
 | 
					#####v#.#.###v#.#.###.#
 | 
				
			||||||
 | 
					#.....#...#...#.#.#...#
 | 
				
			||||||
 | 
					#.#########.###.#.#.###
 | 
				
			||||||
 | 
					#...###...#...#...#.###
 | 
				
			||||||
 | 
					###.###.#.###v#####v###
 | 
				
			||||||
 | 
					#...#...#.#.>.>.#.>.###
 | 
				
			||||||
 | 
					#.###.###.#.###.#.#v###
 | 
				
			||||||
 | 
					#.....###...###...#...#
 | 
				
			||||||
 | 
					#####################.#
 | 
				
			||||||
							
								
								
									
										161
									
								
								src/bin/23.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								src/bin/23.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,161 @@
 | 
				
			|||||||
 | 
					use std::{
 | 
				
			||||||
 | 
					    collections::{HashMap, HashSet},
 | 
				
			||||||
 | 
					    iter::once,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DIRS: [(isize, isize); 4] = [(1, 0), (0, 1), (-1, 0), (0, -1)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Default)]
 | 
				
			||||||
 | 
					struct Node {
 | 
				
			||||||
 | 
					    neighbours: Vec<(usize, usize)>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn build_graph(input: &str, two_way: bool) -> (Vec<Node>, usize) {
 | 
				
			||||||
 | 
					    let mut grid: Vec<Vec<_>> = input.lines().map(|x| x.as_bytes().to_vec()).collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let l = grid[0].len();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let border: Vec<_> = once(b'#').cycle().take(l).collect();
 | 
				
			||||||
 | 
					    grid.insert(0, border.clone());
 | 
				
			||||||
 | 
					    grid.push(border);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let start = (1, 1);
 | 
				
			||||||
 | 
					    let end = (grid.len() - 2, grid[0].len() - 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut nodes = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut mapper = HashMap::new();
 | 
				
			||||||
 | 
					    mapper.insert(start, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut visited: HashSet<(usize, usize)> = HashSet::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut stack = Vec::new();
 | 
				
			||||||
 | 
					    // (from_node, at, len)
 | 
				
			||||||
 | 
					    stack.push((start, start, 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let forced = |x| match x {
 | 
				
			||||||
 | 
					        b'>' => (0, 1),
 | 
				
			||||||
 | 
					        b'v' => (1, 0),
 | 
				
			||||||
 | 
					        _ => (0, 0),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while let Some((from, (y, x), len)) = stack.pop() {
 | 
				
			||||||
 | 
					        let is_node = DIRS
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .filter(|(dy, dx)| {
 | 
				
			||||||
 | 
					                grid[((y as isize) + dy) as usize][((x as isize) + dx) as usize] != b'#'
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .count()
 | 
				
			||||||
 | 
					            > 2
 | 
				
			||||||
 | 
					            || (y, x) == start
 | 
				
			||||||
 | 
					            || (y, x) == end;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if is_node {
 | 
				
			||||||
 | 
					            if !visited.contains(&(y, x)) {
 | 
				
			||||||
 | 
					                mapper.insert((y, x), nodes.len());
 | 
				
			||||||
 | 
					                nodes.push(Node::default());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if from != (y, x) {
 | 
				
			||||||
 | 
					                nodes[mapper[&from]].neighbours.push((mapper[&(y, x)], len));
 | 
				
			||||||
 | 
					                if two_way {
 | 
				
			||||||
 | 
					                    nodes[mapper[&(y, x)]].neighbours.push((mapper[&from], len));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if !visited.insert((y, x)) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (ny, nx, _, _) in DIRS
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .map(|(dy, dx)| {
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    ((y as isize) + dy) as usize,
 | 
				
			||||||
 | 
					                    ((x as isize) + dx) as usize,
 | 
				
			||||||
 | 
					                    dy,
 | 
				
			||||||
 | 
					                    dx,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .filter(|(ny, nx, _, _)| grid[*ny][*nx] != b'#')
 | 
				
			||||||
 | 
					            .filter(|(ny, nx, dy, dx)| {
 | 
				
			||||||
 | 
					                let (fy, fx) = forced(grid[*ny][*nx]);
 | 
				
			||||||
 | 
					                (fy + **dy, fx + **dx) != (0, 0)
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            let new_len = if is_node { 1 } else { len + 1 };
 | 
				
			||||||
 | 
					            let new_from = if is_node { (y, x) } else { from };
 | 
				
			||||||
 | 
					            stack.push((new_from, (ny, nx), new_len));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (nodes, mapper[&end])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn longest_path(
 | 
				
			||||||
 | 
					    nodes: &[Node],
 | 
				
			||||||
 | 
					    mut visited: usize,
 | 
				
			||||||
 | 
					    location: usize,
 | 
				
			||||||
 | 
					    target: usize,
 | 
				
			||||||
 | 
					    length: usize,
 | 
				
			||||||
 | 
					) -> Option<usize> {
 | 
				
			||||||
 | 
					    if location == target {
 | 
				
			||||||
 | 
					        return Some(length);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // binary mask for visited since < 64 nodes in input
 | 
				
			||||||
 | 
					    // nth bit tells if location n was visited already
 | 
				
			||||||
 | 
					    visited |= 1 << location;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut max_len = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (n, l) in nodes[location]
 | 
				
			||||||
 | 
					        .neighbours
 | 
				
			||||||
 | 
					        .iter()
 | 
				
			||||||
 | 
					        .copied()
 | 
				
			||||||
 | 
					        .filter(|(n, _)| (visited & 1 << n) == 0)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if let Some(new_len) = longest_path(nodes, visited, n, target, length + l) {
 | 
				
			||||||
 | 
					            if max_len < new_len {
 | 
				
			||||||
 | 
					                max_len = new_len;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if max_len > 0 {
 | 
				
			||||||
 | 
					        Some(max_len)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn part_one(input: &str) -> Option<usize> {
 | 
				
			||||||
 | 
					    let (nodes, target) = build_graph(input, false);
 | 
				
			||||||
 | 
					    longest_path(&nodes, 0, 0, target, 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn part_two(input: &str) -> Option<usize> {
 | 
				
			||||||
 | 
					    let (nodes, target) = build_graph(input, true);
 | 
				
			||||||
 | 
					    longest_path(&nodes, 0, 0, target, 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					aoc::solution!(23);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_part_one() {
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            part_one(&aoc::template::read_file("examples", 23)),
 | 
				
			||||||
 | 
					            Some(94)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_part_two() {
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            part_two(&aoc::template::read_file("examples", 23)),
 | 
				
			||||||
 | 
					            Some(154)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user