1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//! # Rain Risk
//!
//! Our [`Point`] utility class comes in handy for this problem.
//!
//! [`Point`]: crate::util::point
use crate::util::parse::*;
use crate::util::point::*;

type Command = (u8, i32);

pub fn parse(input: &str) -> Vec<Command> {
    input.lines().map(|line| (line.as_bytes()[0], (&line[1..]).signed())).collect()
}

pub fn part1(input: &[Command]) -> i32 {
    let mut position = ORIGIN;
    let mut direction = Point::new(1, 0);

    for &(command, amount) in input {
        match command {
            b'N' => position.y -= amount,
            b'S' => position.y += amount,
            b'E' => position.x += amount,
            b'W' => position.x -= amount,
            b'L' => direction = rotate(direction, -amount),
            b'R' => direction = rotate(direction, amount),
            b'F' => position += direction * amount,
            _ => unreachable!(),
        }
    }

    position.manhattan(ORIGIN)
}

pub fn part2(input: &[Command]) -> i32 {
    let mut position = ORIGIN;
    let mut waypoint = Point::new(10, -1);

    for &(command, amount) in input {
        match command {
            b'N' => waypoint.y -= amount,
            b'S' => waypoint.y += amount,
            b'E' => waypoint.x += amount,
            b'W' => waypoint.x -= amount,
            b'L' => waypoint = rotate(waypoint, -amount),
            b'R' => waypoint = rotate(waypoint, amount),
            b'F' => position += waypoint * amount,
            _ => unreachable!(),
        }
    }

    position.manhattan(ORIGIN)
}

fn rotate(point: Point, amount: i32) -> Point {
    match amount {
        90 | -270 => point.clockwise(),
        180 | -180 => point * -1,
        270 | -90 => point.counter_clockwise(),
        _ => unreachable!(),
    }
}