1use std::collections::HashSet;
2use utils::point::Point2D;
3use utils::prelude::*;
4
5#[derive(Clone, Debug)]
7pub struct Day01 {
8 instructions: Vec<(Turn, u16)>,
9}
10
11#[derive(Copy, Clone, Eq, PartialEq, Debug)]
12enum Turn {
13 L,
14 R,
15}
16
17impl Day01 {
18 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
19 Ok(Self {
20 instructions: parser::literal_map!("L" => Turn::L, "R" => Turn::R)
21 .then(parser::u16())
22 .with_suffix(", ".or(parser::eof()))
23 .parse_all(input)?,
24 })
25 }
26
27 #[must_use]
28 pub fn part1(&self) -> u32 {
29 let mut pos = Point2D::ORIGIN;
30 let mut dir = Point2D::UP;
31
32 for &(turn, steps) in &self.instructions {
33 dir = match turn {
34 Turn::L => dir.turn_left(),
35 Turn::R => dir.turn_right(),
36 };
37 pos += dir * i32::from(steps);
38 }
39
40 pos.manhattan_distance()
41 }
42
43 #[must_use]
44 pub fn part2(&self) -> u32 {
45 let mut pos: Point2D<i32> = Point2D::ORIGIN;
46 let mut dir = Point2D::UP;
47 let mut visited = HashSet::new();
48
49 for &(turn, steps) in &self.instructions {
50 dir = match turn {
51 Turn::L => dir.turn_left(),
52 Turn::R => dir.turn_right(),
53 };
54 for _ in 0..steps {
55 pos += dir;
56 if !visited.insert(pos) {
57 return pos.manhattan_distance();
58 }
59 }
60 }
61
62 panic!("no location visited twice");
63 }
64}
65
66examples!(Day01 -> (u32, u32) [
67 {input: "R2, L3", part1: 5},
68 {input: "R2, R2, R2", part1: 2},
69 {input: "R5, L5, R5, R3", part1: 12},
70 {input: "R8, R4, R4, R8", part2: 4},
71]);