advent_of_code/year2017/
day20.rs

1use crate::input::Input;
2
3// Parse input in the format "A=<211,-141,-45>".
4fn parse_vector(input: &str) -> Option<(i32, i32, i32)> {
5    if input.len() > 3 {
6        if let Some(stripped) = input[2..]
7            .strip_prefix('<')
8            .and_then(|s| s.strip_suffix('>'))
9        {
10            let mut number_parts = stripped.split(',');
11            let x = number_parts.next()?.parse::<i16>().ok()?;
12            let y = number_parts.next()?.parse::<i16>().ok()?;
13            let z = number_parts.next()?.parse::<i16>().ok()?;
14            return Some((i32::from(x), i32::from(y), i32::from(z)));
15        }
16    }
17    None
18}
19
20pub fn solve(input: &Input) -> Result<u32, String> {
21    let mut particles = Vec::new();
22
23    for (line_idx, line) in input.text.lines().enumerate() {
24        let on_error = || format!("Line {}: Invalid format", line_idx + 1);
25
26        let mut parts = line.split(", ");
27        let position_part =
28            parse_vector(parts.next().ok_or_else(on_error)?).ok_or_else(on_error)?;
29        let speed_part = parse_vector(parts.next().ok_or_else(on_error)?).ok_or_else(on_error)?;
30        let acceleration_part =
31            parse_vector(parts.next().ok_or_else(on_error)?).ok_or_else(on_error)?;
32
33        particles.push((position_part, speed_part, acceleration_part));
34    }
35
36    for _ in 0..500 {
37        particles = particles
38            .iter()
39            .filter_map(|particle| {
40                if input.is_part_two()
41                    && particles
42                        .iter()
43                        .filter(|other_particle| other_particle.0 == particle.0)
44                        .count()
45                        > 1
46                {
47                    None
48                } else {
49                    let new_speed = (
50                        particle.1 .0 + particle.2 .0,
51                        particle.1 .1 + particle.2 .1,
52                        particle.1 .2 + particle.2 .2,
53                    );
54                    let new_position = (
55                        particle.0 .0 + new_speed.0,
56                        particle.0 .1 + new_speed.1,
57                        particle.0 .2 + new_speed.2,
58                    );
59                    let new_acceleration = particle.2;
60                    Some((new_position, new_speed, new_acceleration))
61                }
62            })
63            .collect();
64    }
65
66    if input.is_part_one() {
67        Ok(particles
68            .iter()
69            .enumerate()
70            .fold((0, i32::MAX), |acc, (idx, particle)| {
71                let dist = particle.0 .0.abs() + particle.0 .1.abs() + particle.0 .2.abs();
72                if dist < acc.1 {
73                    (idx, dist)
74                } else {
75                    acc
76                }
77            })
78            .0 as u32)
79    } else {
80        Ok(particles.len() as u32)
81    }
82}
83
84#[test]
85pub fn tests() {
86    use crate::input::{test_part_one, test_part_two};
87
88    let real_input = include_str!("day20_input.txt");
89    test_part_one!(real_input => 91);
90    test_part_two!(real_input => 567);
91}