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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use utils::prelude::*;

/// Calculating spiral patterns.
#[derive(Clone, Debug)]
pub struct Day03 {
    input: u32,
}

impl Day03 {
    pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
        Ok(Self {
            input: parser::u32().parse_complete(input)?,
        })
    }

    #[must_use]
    pub fn part1(&self) -> u32 {
        let ring = (self.input as f64).sqrt().ceil() as u32 / 2;
        let side_length = ring * 2 + 1;
        let bottom_right = side_length * side_length;
        let middles = [
            bottom_right - ring,       // Bottom
            bottom_right - (ring * 3), // Left
            bottom_right - (ring * 5), // Top
            bottom_right - (ring * 7), // Right
        ];
        let offset = middles
            .iter()
            .map(|m| m.abs_diff(self.input))
            .min()
            .unwrap();
        ring + offset
    }

    #[must_use]
    pub fn part2(&self) -> u64 {
        // To cover 0..=u32::MAX only the first 150 values in the sequence are necessary. The 150th
        // value is in the 6th ring, meaning a grid of 13x13 is required, or 15x15 with an extra
        // ring around the edge to avoid needing bounds checks.
        const LEN: usize = 15;

        let mut grid = [[0; LEN]; LEN];
        let (mut x, mut y) = (LEN / 2, LEN / 2);
        grid[x][y] = 1;

        // Store the number of turns and remaining steps until the next turn. The number of previous
        // turns gives you the current direction and next number of steps by following the pattern:
        //  1x Right
        //  1x Up
        //  2x Left
        //  2x Down
        //  3x Right
        //  3x Up
        //  4x Left
        //  ...
        let mut turns = 0;
        let mut steps = 1;

        while grid[x][y] < self.input as u64 {
            if steps == 0 {
                turns += 1;
                steps = (turns / 2) + 1;
            }
            steps -= 1;
            match turns % 4 {
                0 => x += 1,
                1 => y += 1,
                2 => x -= 1,
                3 => y -= 1,
                _ => unreachable!(),
            }

            grid[x][y] = grid[x - 1][y - 1]
                + grid[x - 1][y]
                + grid[x - 1][y + 1]
                + grid[x][y - 1]
                + grid[x][y + 1]
                + grid[x + 1][y - 1]
                + grid[x + 1][y]
                + grid[x + 1][y + 1];
        }

        grid[x][y]
    }
}

examples!(Day03 -> (u32, u64) [
    {input: "1", part1: 0},
    {input: "12", part1: 3},
    {input: "23", part1: 2},
    {input: "1024", part1: 31},
    // Custom examples
    {input: "100", part2: 122},
    {input: "200", part2: 304},
    {input: "4294967295", part2: 4429173742},
]);