Advertisement
saaoijeefhiuh

2024 day 6

Dec 6th, 2024
34
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 5.68 KB | None | 0 0
  1. advent_of_code::solution!(6);
  2. use std::collections::HashSet;
  3.  
  4. #[derive(Eq, Hash, PartialEq, Clone, Debug)]
  5. struct VisitedMap {
  6.     data: Vec<u64>,
  7.     h: u32,
  8.     w: u32,
  9.     d: u32,
  10. }
  11.  
  12. impl VisitedMap {
  13.     pub fn new(h: u32, w: u32, d: u32) -> Self {
  14.         Self {
  15.             data: vec![0; ((h*w*d) as usize).div_ceil(64)],
  16.             h: h,
  17.             w: w,
  18.             d: d,
  19.         }
  20.     }
  21.  
  22.     fn visit(&mut self, i: u32, j: u32, d: u32) -> bool {
  23.         let address: usize = (i * self.w * self.d + j * self.d + d) as usize;
  24.         let idx: usize = address >> 6;
  25.         let bit: usize = address & 0x3F;
  26.  
  27.         let set: bool = ((self.data[idx] >> bit) & 1) == 1;
  28.         self.data[idx] |= 1 << bit;
  29.         return set;
  30.     }
  31.  
  32.     fn visited(&self, i: u32, j: u32, d: u32) -> bool {
  33.         let address: usize = (i * self.w * self.d + j * self.d + d) as usize;
  34.         let idx: usize = address >> 6;
  35.         let bit: usize = address & 0x3F;
  36.         ((self.data[idx] >> bit) & 1) == 1
  37.     }
  38.  
  39.     fn count(&self) -> usize {
  40.         self.data.iter().map(|v| v.count_ones() as usize).sum()
  41.     }
  42. }
  43.  
  44. #[derive(Eq, Hash, PartialEq, Clone, Copy, Debug)]
  45. enum Dir {
  46.     Up = 0,
  47.     Right = 1,
  48.     Down = 2,
  49.     Left = 3,
  50. }
  51.  
  52. #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
  53. struct State {
  54.     dir: Dir,
  55.     i: u32,
  56.     j: u32,
  57.     h: u32,
  58.     w: u32,
  59. }
  60.  
  61. impl State {
  62.     fn evolve<F: Fn(u32, u32) -> bool>(&mut self, is_obstacle: F) -> bool {
  63.         let (ni, nj): (i32, i32) = match self.dir {
  64.             Dir::Up => (self.i as i32 - 1, self.j as i32),
  65.             Dir::Right => (self.i as i32, self.j as i32 + 1),
  66.             Dir::Down => (self.i as i32 + 1, self.j as i32),
  67.             Dir::Left => (self.i as i32, self.j as i32 - 1),
  68.         };
  69.  
  70.         if ni < 0 || ni >= self.h as i32 || nj < 0 || nj >= self.w as i32 {
  71.             return false;
  72.         }
  73.  
  74.         if is_obstacle(ni as u32, nj as u32) {
  75.             self.dir = match self.dir {
  76.                 Dir::Up => Dir::Right,
  77.                 Dir::Right => Dir::Down,
  78.                 Dir::Down => Dir::Left,
  79.                 Dir::Left => Dir::Up,
  80.             };
  81.         } else {
  82.             self.i = ni as u32;
  83.             self.j = nj as u32;
  84.         }
  85.  
  86.         true
  87.     }
  88.  
  89.     fn new_visited(&self, d: u32) -> VisitedMap {
  90.         VisitedMap::new(self.h, self.w, d)
  91.     }
  92. }
  93.  
  94. fn parse(input: &str) -> (State, VisitedMap) {
  95.     let points: HashSet<(char, u32, u32)> = input
  96.         .split("\n")
  97.         .enumerate()
  98.         .map(|(i, l)| {
  99.             l.chars().enumerate().filter_map(move |(j, c)| match c {
  100.                 '#' | '^' => Some((c, i as u32, j as u32)),
  101.                 _ => None,
  102.             })
  103.         })
  104.         .flatten()
  105.         .collect();
  106.     let h = input.split("\n").count() as u32;
  107.     let w = input
  108.         .split_once("\n")
  109.         .and_then(|(l, _)| Some(l.chars().count()))
  110.         .unwrap() as u32;
  111.  
  112.     let mut obstacles = VisitedMap::new(h, w, 1);
  113.     points
  114.         .iter()
  115.         .filter(|(c, _, _)| *c == '#')
  116.         .for_each(|(_, i, j)| {
  117.             obstacles.visit(*i, *j, 0);
  118.         });
  119.  
  120.     let start: (u32, u32) = points
  121.         .iter()
  122.         .filter(|(c, _, _)| *c == '^')
  123.         .map(|(_, i, j)| (*i, *j))
  124.         .next()
  125.         .unwrap();
  126.  
  127.     (
  128.         State {
  129.             dir: Dir::Up,
  130.             i: start.0,
  131.             j: start.1,
  132.             h: h,
  133.             w: w,
  134.         },
  135.         obstacles,
  136.     )
  137. }
  138.  
  139. pub fn part_one(input: &str) -> Option<u32> {
  140.     let (mut state, obstacles) = parse(input);
  141.     let mut visited = state.new_visited(1);
  142.     loop {
  143.         visited.visit(state.i, state.j, 0);
  144.  
  145.         if !state.evolve(|i, j| obstacles.visited(i, j, 0)) {
  146.             break;
  147.         }
  148.     }
  149.  
  150.     Some(visited.count() as u32)
  151. }
  152.  
  153. pub fn part_two(input: &str) -> Option<u32> {
  154.     let (mut init_state, obstacles) = parse(input);
  155.     let mut init_visited = init_state.new_visited(4);
  156.     init_visited.visit(init_state.i, init_state.j, init_state.dir as u32);
  157.  
  158.     let mut count: u32 = 0;
  159.     let mut added: HashSet<(u32, u32)> = HashSet::new();
  160.     let mut state = init_state.clone();
  161.     let mut visited = init_visited.clone();
  162.     loop {
  163.         // advance the outer route one step so we can take its location as the start point
  164.         if !init_state.evolve(|i, j| obstacles.visited(i, j, 0)) {
  165.             break; // left the grid
  166.         }
  167.  
  168.         if !added.contains(&(init_state.i, init_state.j)) {
  169.             added.insert((init_state.i, init_state.j));
  170.             loop {
  171.                 // last is the location of the obstacle added for this point in original loop
  172.                 if !state.evolve(|i, j| obstacles.visited(i, j, 0) || (i, j) == (init_state.i, init_state.j)) {
  173.                     break; // left the grid
  174.                 }
  175.                 if visited.visit(state.i, state.j, state.dir as u32) {
  176.                     // it's a loop - so terminate
  177.                     count += 1;
  178.                     break;
  179.                 }
  180.             }
  181.         }
  182.  
  183.         init_visited.visit(init_state.i, init_state.j, init_state.dir as u32);
  184.  
  185.         // fork the route starting from this point
  186.         state.clone_from(&init_state);
  187.         visited.clone_from(&init_visited);
  188.     }
  189.  
  190.     Some(count)
  191. }
  192.  
  193. #[cfg(test)]
  194. mod tests {
  195.     use super::*;
  196.  
  197.     #[test]
  198.     fn test_part_one() {
  199.         let result = part_one(&advent_of_code::template::read_file("examples", DAY));
  200.         assert_eq!(result, Some(41));
  201.     }
  202.  
  203.     #[test]
  204.     fn test_part_two() {
  205.         let result = part_two(&advent_of_code::template::read_file("examples", DAY));
  206.         assert_eq!(result, Some(6));
  207.     }
  208. }
  209.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement