1use std::collections::HashSet;
2use utils::point::Point2D;
3use utils::prelude::*;
4
5#[derive(Clone, Debug)]
7pub struct Day03 {
8 directions: Vec<Point2D<i32>>,
9}
10
11impl Day03 {
12 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
13 Ok(Self {
14 directions: input
15 .chars()
16 .map(|c| match c {
17 '^' => Ok(Point2D::UP),
18 '>' => Ok(Point2D::RIGHT),
19 'v' => Ok(Point2D::DOWN),
20 '<' => Ok(Point2D::LEFT),
21 _ => Err(InputError::new(input, c, "expected one of ^>v<")),
22 })
23 .collect::<Result<Vec<Point2D<i32>>, InputError>>()?,
24 })
25 }
26
27 #[must_use]
28 pub fn part1(&self) -> usize {
29 let mut pos = Point2D::default();
30
31 self.count_positions(|dir| {
32 pos += dir;
33 pos
34 })
35 }
36
37 #[must_use]
38 pub fn part2(&self) -> usize {
39 let (mut pos1, mut pos2) = Default::default();
40
41 self.count_positions(|dir| {
42 (pos1, pos2) = (pos2, pos1);
43 pos1 += dir;
44 pos1
45 })
46 }
47
48 fn count_positions(&self, mut f: impl FnMut(Point2D<i32>) -> Point2D<i32>) -> usize {
49 let mut set = HashSet::with_capacity(self.directions.len());
50 set.insert(Point2D::default());
51
52 for &dir in &self.directions {
53 set.insert(f(dir));
54 }
55
56 set.len()
57 }
58}
59
60examples!(Day03 -> (usize, usize) [
61 {input: ">", part1: 2},
62 {input: "^>", part2: 3},
63 {input: "^>v<", part1: 4, part2: 3},
64 {input: "^v^v^v^v^v", part1: 2, part2: 11},
65]);