Skip to main content

advent_of_code/year2017/
day22.rs

1use crate::input::Input;
2use std::collections::HashMap;
3use std::collections::hash_map::Entry;
4
5#[derive(Copy, Clone, Eq, PartialEq)]
6enum NodeFlag {
7    Weakened,
8    Infected,
9    Flagged,
10}
11
12const fn turn(direction: (i32, i32), right: bool) -> (i32, i32) {
13    if right {
14        (-direction.1, direction.0)
15    } else {
16        (direction.1, -direction.0)
17    }
18}
19
20const fn reverse(direction: (i32, i32)) -> (i32, i32) {
21    (-direction.0, -direction.1)
22}
23
24pub fn solve(input: &Input) -> Result<u32, String> {
25    let mut map = HashMap::new();
26
27    let mut cols = -1;
28    let mut rows = -1;
29    for (line_idx, line) in input.text.lines().enumerate() {
30        rows += 1;
31        for (char_idx, char) in line.chars().enumerate() {
32            if rows == 0 {
33                cols += 1;
34            }
35            if char == '#' {
36                map.insert((char_idx as i32, line_idx as i32), NodeFlag::Infected);
37            }
38        }
39    }
40
41    let mut carrier_position = (cols / 2 + cols % 2, rows / 2 + rows % 2);
42    let mut carrier_direction = (0, -1);
43    let mut bursts_causing_infection = 0;
44    for _burst in 0..input.part_values(10_000, 10_000_000) {
45        match map.entry(carrier_position) {
46            Entry::Vacant(entry) => {
47                carrier_direction = turn(carrier_direction, false);
48
49                if input.is_part_one() {
50                    // Clean nodes become infected.
51                    bursts_causing_infection += 1;
52                    entry.insert(NodeFlag::Infected);
53                } else {
54                    // Clean nodes become weakened.
55                    entry.insert(NodeFlag::Weakened);
56                }
57            }
58            Entry::Occupied(mut entry) => {
59                match entry.get() {
60                    NodeFlag::Weakened => {
61                        // Weakened nodes become infected.
62                        bursts_causing_infection += 1;
63                        entry.insert(NodeFlag::Infected);
64                    }
65                    NodeFlag::Infected => {
66                        carrier_direction = turn(carrier_direction, true);
67
68                        if input.is_part_one() {
69                            // Infected nodes become cleaned.
70                            entry.remove();
71                        } else {
72                            // Infected nodes become flagged.
73                            entry.insert(NodeFlag::Flagged);
74                        }
75                    }
76                    NodeFlag::Flagged => {
77                        carrier_direction = reverse(carrier_direction);
78
79                        // Flagged nodes become clean.
80                        entry.remove();
81                    }
82                }
83            }
84        }
85
86        carrier_position = (
87            carrier_position.0 + carrier_direction.0,
88            carrier_position.1 + carrier_direction.1,
89        );
90    }
91
92    Ok(bursts_causing_infection)
93}
94
95#[test]
96pub fn tests() {
97    let real_input = include_str!("day22_input.txt");
98    test_part_one!(real_input => 5246);
99    test_part_two!(real_input => 2_512_059);
100}