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
use crate::input::Input;
use std::collections::{HashMap, HashSet};
pub fn solve(input: &mut Input) -> Result<u32, String> {
let mut sector_ids_sum = 0;
for (line_idx, line) in input.text.lines().enumerate() {
let on_error = || format!("Line {}: Invalid input", line_idx + 1);
let mut char_frequency = HashMap::new();
let (room_name, sector_id_and_checksum) = line.rsplit_once('-').ok_or_else(on_error)?;
for c in room_name.chars().filter(|&c| c != '-') {
*char_frequency.entry(c).or_insert(0) += 1;
}
let (sector_id, checksum) = sector_id_and_checksum
.split_once('[')
.and_then(|(id, checksum)| Some((id.parse::<u32>().ok()?, checksum.strip_suffix(']')?)))
.ok_or_else(on_error)?;
let mut sorted_by_frequency = room_name
.chars()
.filter(|&c| c != '-')
.collect::<HashSet<char>>()
.into_iter()
.collect::<Vec<char>>();
sorted_by_frequency.sort_unstable_by(|c1, c2| {
let f1 = char_frequency.get(c1).unwrap_or(&0);
let f2 = char_frequency.get(c2).unwrap_or(&0);
f2.cmp(f1).then(c1.cmp(c2))
});
sorted_by_frequency = sorted_by_frequency
.into_iter()
.take(5)
.collect::<Vec<char>>();
if sorted_by_frequency.into_iter().collect::<String>() == checksum {
sector_ids_sum += sector_id;
if input.is_part_two() {
let decrypted_name = room_name
.chars()
.map(|a| match a {
'-' => ' ',
_ => (((a as u32 - 'a' as u32 + sector_id) % 26_u32) as u8 + b'a') as char,
})
.collect::<String>();
if decrypted_name == "northpole object storage" {
return Ok(sector_id);
}
}
}
}
Ok(sector_ids_sum)
}
#[test]
pub fn tests() {
use crate::input::{test_part_one, test_part_two};
let real_input = include_str!("day04_input.txt");
test_part_one!(real_input => 245_102);
test_part_two!(real_input => 324);
}