1use crate::util::parse::*;
11
12pub struct Room<'a> {
13 name: &'a str,
14 sector_id: u32,
15}
16
17pub fn parse(input: &str) -> Vec<Room<'_>> {
18 let mut valid = Vec::new();
19
20 for line in input.lines() {
21 let size = line.len();
23 let name = &line[..size - 11];
24 let checksum = &line.as_bytes()[size - 6..size - 1];
25
26 let mut freq = [0; 26];
29 let mut fof = [0; 26];
30 let mut highest = 0;
31
32 for b in name.bytes() {
33 if b != b'-' {
34 let index = to_index(b);
35 let current = freq[index];
36 let next = freq[index] + 1;
37
38 freq[index] = next;
39 fof[current] -= 1;
40 fof[next] += 1;
41
42 highest = highest.max(next);
43 }
44 }
45
46 if freq[to_index(checksum[0])] == highest && rules(checksum, &freq, &mut fof) {
48 let sector_id = (&line[size - 10..size - 7]).unsigned();
49 valid.push(Room { name, sector_id });
50 }
51 }
52
53 valid
54}
55
56pub fn part1(input: &[Room<'_>]) -> u32 {
57 input.iter().map(|room| room.sector_id).sum()
58}
59
60pub fn part2(input: &[Room<'_>]) -> u32 {
61 for &Room { name, sector_id } in input {
62 let bytes = name.as_bytes();
63
64 if bytes.len() == 24 && bytes[9] == b'-' && bytes[16] == b'-' {
66 let mut buffer = String::with_capacity(24);
67
68 for b in name.bytes() {
70 if b == b'-' {
71 buffer.push(' ');
72 } else {
73 let rotate = (sector_id % 26) as u8;
74 let decrypted = (b - b'a' + rotate) % 26 + b'a';
75 buffer.push(decrypted as char);
76 }
77 }
78
79 if buffer == "northpole object storage" {
80 return sector_id;
81 }
82 }
83 }
84
85 unreachable!()
86}
87
88fn rules(checksum: &[u8], freq: &[usize], fof: &mut [i32]) -> bool {
92 checksum.windows(2).all(|w| {
93 let end = freq[to_index(w[0])];
94 let start = freq[to_index(w[1])];
95 fof[end] -= 1;
96 !(start > end
97 || (start == end && w[1] <= w[0])
98 || (start + 1..end + 1).any(|i| fof[i] != 0))
99 })
100}
101
102fn to_index(b: u8) -> usize {
103 (b - b'a') as usize
104}