1use utils::geometry::Vec2;
2use utils::hash::FastSet;
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 = Vec2::ORIGIN;
30 let mut dir = Vec2::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 total = self
46 .instructions
47 .iter()
48 .map(|&(_, steps)| usize::from(steps))
49 .sum();
50 let mut visited = FastSet::with_capacity(total);
51 let mut pos: Vec2<i32> = Vec2::ORIGIN;
52 let mut dir = Vec2::UP;
53
54 for &(turn, steps) in &self.instructions {
55 dir = match turn {
56 Turn::L => dir.turn_left(),
57 Turn::R => dir.turn_right(),
58 };
59 for _ in 0..steps {
60 pos += dir;
61 if !visited.insert(pos) {
62 return pos.manhattan_distance();
63 }
64 }
65 }
66
67 panic!("no location visited twice");
68 }
69}
70
71examples!(Day01 -> (u32, u32) [
72 {input: "R2, L3", part1: 5},
73 {input: "R2, R2, R2", part1: 2},
74 {input: "R5, L5, R5, R3", part1: 12},
75 {input: "R8, R4, R4, R8", part2: 4},
76]);