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);