|
3 | 3 | //! We speed things up by only checking numbers that have digits in non-decreasing order for pairs.
|
4 | 4 | //! These numbers become rapidly less dense as the password value increases and there
|
5 | 5 | //! are only 3003 total of these numbers with 6 digits.
|
| 6 | +use crate::util::iter::*; |
6 | 7 | use crate::util::parse::*;
|
7 |
| -use crate::util::slice::*; |
8 | 8 |
|
9 |
| -pub fn parse(input: &str) -> Vec<u32> { |
10 |
| - input.iter_unsigned().collect() |
11 |
| -} |
12 |
| - |
13 |
| -/// Password must contain at least one pair. |
14 |
| -pub fn part1(input: &[u32]) -> u32 { |
15 |
| - let predicate = |first: bool, second: bool, third: bool, fourth: bool, fifth: bool| { |
16 |
| - first || second || third || fourth || fifth |
17 |
| - }; |
18 |
| - passwords(input, predicate) |
19 |
| -} |
| 9 | +type Input = (u32, u32); |
20 | 10 |
|
21 |
| -/// Password must contain at least one pair that's not part of a larger group. |
22 |
| -pub fn part2(input: &[u32]) -> u32 { |
23 |
| - let predicate = |first: bool, second: bool, third: bool, fourth: bool, fifth: bool| { |
24 |
| - (first && !second) |
25 |
| - || (!first && second && !third) |
26 |
| - || (!second && third && !fourth) |
27 |
| - || (!third && fourth && !fifth) |
28 |
| - || (!fourth && fifth) |
29 |
| - }; |
30 |
| - passwords(input, predicate) |
31 |
| -} |
| 11 | +pub fn parse(input: &str) -> Input { |
| 12 | + let [start, end] = input.iter_unsigned::<u32>().chunk::<2>().next().unwrap(); |
32 | 13 |
|
33 |
| -fn passwords(input: &[u32], predicate: impl Fn(bool, bool, bool, bool, bool) -> bool) -> u32 { |
34 |
| - let start = input[0]; |
35 |
| - let end = input[1]; |
| 14 | + let mut digits = to_digits(start); |
| 15 | + let end = to_digits(end); |
36 | 16 |
|
37 |
| - // Split into six digits. |
38 |
| - let mut digits = [ |
39 |
| - start / 100000, |
40 |
| - (start / 10000) % 10, |
41 |
| - (start / 1000) % 10, |
42 |
| - (start / 100) % 10, |
43 |
| - (start / 10) % 10, |
44 |
| - start % 10, |
45 |
| - ]; |
| 17 | + let mut part_one = 0; |
| 18 | + let mut part_two = 0; |
46 | 19 |
|
47 | 20 | // Increase the starting number to the next number that has all digits in non-decreasing order
|
48 | 21 | // to ensure that the incrementing logic in the search loop works correctly.
|
49 | 22 | // For example 223450 => 223455, 120568 => 122222 and 439999 => 444444.
|
50 |
| - for i in 1..6 { |
51 |
| - if digits[i] < digits[i - 1] { |
52 |
| - for j in i..6 { |
53 |
| - digits[j] = digits[i - 1]; |
54 |
| - } |
55 |
| - break; |
56 |
| - } |
| 23 | + if let Some(index) = digits.windows(2).position(|w| w[0] > w[1]) { |
| 24 | + let next = digits[index]; |
| 25 | + digits[index..].fill(next); |
57 | 26 | }
|
58 | 27 |
|
59 |
| - let mut n = 0; |
60 |
| - let mut count = 0; |
61 |
| - |
62 |
| - while n <= end { |
63 |
| - // Check current number |
64 |
| - let first = digits[0] == digits[1]; |
65 |
| - let second = digits[1] == digits[2]; |
66 |
| - let third = digits[2] == digits[3]; |
67 |
| - let fourth = digits[3] == digits[4]; |
68 |
| - let fifth = digits[4] == digits[5]; |
| 28 | + while digits <= end { |
| 29 | + // Build a 5 bit binary mask with a `1` if two adjacent digits are equal. |
| 30 | + let mask = digits.windows(2).fold(0, |acc, w| (acc << 1) | (w[0] == w[1]) as u32); |
69 | 31 |
|
70 |
| - if predicate(first, second, third, fourth, fifth) { |
71 |
| - count += 1; |
72 |
| - } |
| 32 | + // Password must contain at least one pair. |
| 33 | + part_one += (mask != 0) as u32; |
| 34 | + // Password must contain at least one pair that's not part of a larger group. |
| 35 | + part_two += (mask & !(mask >> 1) & !(mask << 1) != 0) as u32; |
73 | 36 |
|
74 | 37 | // Find the next number with all digits in non-decreasing order.
|
75 |
| - let mut i = 5; |
76 |
| - while digits[i] == 9 { |
77 |
| - i -= 1; |
78 |
| - } |
| 38 | + let index = digits.iter().rposition(|&d| d < b'9').unwrap(); |
| 39 | + let next = digits[index] + 1; |
| 40 | + digits[index..].fill(next); |
| 41 | + } |
79 | 42 |
|
80 |
| - let next = digits[i] + 1; |
81 |
| - while i <= 5 { |
82 |
| - digits[i] = next; |
83 |
| - i += 1; |
84 |
| - } |
| 43 | + (part_one, part_two) |
| 44 | +} |
85 | 45 |
|
86 |
| - // Convert number to `u32`. |
87 |
| - n = digits.fold_decimal(); |
88 |
| - } |
| 46 | +pub fn part1(input: &Input) -> u32 { |
| 47 | + input.0 |
| 48 | +} |
| 49 | + |
| 50 | +pub fn part2(input: &Input) -> u32 { |
| 51 | + input.1 |
| 52 | +} |
89 | 53 |
|
90 |
| - count |
| 54 | +fn to_digits(n: u32) -> [u8; 6] { |
| 55 | + format!("{n:06}").into_bytes().try_into().unwrap() |
91 | 56 | }
|
0 commit comments