/ Rust / 2022 / 09.rs
09.rs
 1  #![feature(test)]
 2  
 3  use aoc::grid::Direction;
 4  use rustc_hash::FxHashSet;
 5  
 6  type Input = Vec<Motion>;
 7  
 8  #[derive(Debug)]
 9  struct Motion {
10      direction: Direction,
11      count: u32,
12  }
13  
14  struct Solver<'a> {
15      motions: &'a [Motion],
16      knots: Vec<(isize, isize)>,
17      visited: FxHashSet<(isize, isize)>,
18  }
19  
20  impl<'a> Solver<'a> {
21      fn new(input: &'a Input, n: usize) -> Self {
22          Self {
23              motions: input,
24              knots: vec![(0, 0); n],
25              visited: FxHashSet::default(),
26          }
27      }
28  
29      fn follow(&mut self, i: usize) {
30          let p = self.knots[i - 1].0 - self.knots[i].0;
31          let q = self.knots[i - 1].1 - self.knots[i].1;
32          if p.abs() > 1 || q.abs() > 1 {
33              self.knots[i] = (
34                  self.knots[i].0 + p.clamp(-1, 1),
35                  self.knots[i].1 + q.clamp(-1, 1),
36              );
37          }
38      }
39  
40      fn solve(mut self) -> usize {
41          for Motion { direction, count } in self.motions {
42              for _ in 0..*count {
43                  self.knots[0] = direction.step_signed(self.knots[0]);
44                  (1..self.knots.len()).for_each(|i| self.follow(i));
45                  self.visited.insert(*self.knots.last().unwrap());
46              }
47          }
48          self.visited.len()
49      }
50  }
51  
52  fn setup(input: &str) -> Input {
53      input
54          .trim()
55          .lines()
56          .map(|line| Motion {
57              direction: line.chars().next().unwrap().into(),
58              count: line[2..].parse().unwrap(),
59          })
60          .collect()
61  }
62  
63  fn part1(input: &Input) -> usize {
64      Solver::new(input, 2).solve()
65  }
66  
67  fn part2(input: &Input) -> usize {
68      Solver::new(input, 10).solve()
69  }
70  
71  aoc::main!(2022, 9, ex: 1, 2);