advent_of_code/year2021/
day13.rs

1use crate::common::character_recognition::{recognize, CHAR_HEIGHT, CHAR_WIDTH};
2use crate::input::Input;
3
4pub fn solve(input: &Input) -> Result<String, String> {
5    const NUM_LETTERS: usize = 8;
6    let mut dots = Vec::new();
7
8    let parse_number = |num_str: &str| {
9        num_str
10            .parse::<u16>()
11            .map_err(|_| "Invalid number - not an u16")
12    };
13
14    for line in input.text.lines() {
15        if let Some((x, y)) = line.split_once(',') {
16            let x = parse_number(x)?;
17            let y = parse_number(y)?;
18            dots.push((x, y));
19        } else if let Some((prefix, coord)) = line.split_once('=') {
20            let coord = parse_number(coord)?;
21            let updater = |n: &mut u16| {
22                if *n > coord {
23                    if *n > 2 * coord {
24                        return Err("Folding would create dot with negative coordinate".to_string());
25                    }
26                    *n = 2 * coord - *n;
27                }
28                Ok(())
29            };
30
31            if prefix.ends_with('x') {
32                for p in dots.iter_mut() {
33                    updater(&mut p.0)?;
34                }
35            } else if prefix.ends_with('y') {
36                for p in dots.iter_mut() {
37                    updater(&mut p.1)?;
38                }
39            } else {
40                return Err("Invalid line not ending width x=.. or y=..".to_string());
41            }
42
43            if input.is_part_one() {
44                dots.sort_unstable();
45                dots.dedup();
46                return Ok(dots.len().to_string());
47            }
48        }
49    }
50
51    let mut screen = [false; NUM_LETTERS * CHAR_HEIGHT * CHAR_WIDTH];
52    for (x, y) in dots {
53        if x >= (NUM_LETTERS * CHAR_WIDTH) as u16 || y >= CHAR_HEIGHT as u16 {
54            return Err("Dot outside of range".into());
55        }
56        screen[usize::from(y) * NUM_LETTERS * CHAR_WIDTH + usize::from(x)] = true;
57    }
58    recognize(&screen)
59}
60
61#[test]
62pub fn tests() {
63    use crate::input::{test_part_one, test_part_one_error, test_part_two};
64
65    let example = "6,10
660,14
679,10
680,3
6910,4
704,11
716,0
726,12
734,1
740,13
7510,12
763,4
773,0
788,4
791,10
802,14
818,10
829,0
83
84fold along y=7
85fold along x=5";
86    test_part_one!(example => "17".to_string());
87
88    let real_input = include_str!("day13_input.txt");
89    test_part_one!(real_input => "763".to_string());
90    test_part_two!(real_input => "RHALRCRA".to_string());
91
92    test_part_one_error!("189,403" => "Dot outside of range".to_string());
93    test_part_one_error!("189,4" => "Dot outside of range".to_string());
94}