Skip to content

Commit f9fd77b

Browse files
committed
Year 2019 speed and code quality improvements
1 parent b9350b6 commit f9fd77b

File tree

9 files changed

+129
-162
lines changed

9 files changed

+129
-162
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -238,18 +238,18 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
238238
| 1 | [The Tyranny of the Rocket Equation](https://adventofcode.com/2019/day/1) | [Source](src/year2019/day01.rs) | 1 |
239239
| 2 | [1202 Program Alarm](https://adventofcode.com/2019/day/2) | [Source](src/year2019/day02.rs) | 1 |
240240
| 3 | [Crossed Wires](https://adventofcode.com/2019/day/3) | [Source](src/year2019/day03.rs) | 17 |
241-
| 4 | [Secure Container](https://adventofcode.com/2019/day/4) | [Source](src/year2019/day04.rs) | 12 |
241+
| 4 | [Secure Container](https://adventofcode.com/2019/day/4) | [Source](src/year2019/day04.rs) | 7 |
242242
| 5 | [Sunny with a Chance of Asteroids](https://adventofcode.com/2019/day/5) | [Source](src/year2019/day05.rs) | 3 |
243243
| 6 | [Universal Orbit Map](https://adventofcode.com/2019/day/6) | [Source](src/year2019/day06.rs) | 29 |
244244
| 7 | [Amplification Circuit](https://adventofcode.com/2019/day/7) | [Source](src/year2019/day07.rs) | 79 |
245245
| 8 | [Space Image Format](https://adventofcode.com/2019/day/8) | [Source](src/year2019/day08.rs) | 4 |
246246
| 9 | [Sensor Boost](https://adventofcode.com/2019/day/9) | [Source](src/year2019/day09.rs) | 1008 |
247247
| 10 | [Monitoring Station](https://adventofcode.com/2019/day/10) | [Source](src/year2019/day10.rs) | 1092 |
248-
| 11 | [Space Police](https://adventofcode.com/2019/day/11) | [Source](src/year2019/day11.rs) | 341 |
249-
| 12 | [The N-Body Problem](https://adventofcode.com/2019/day/12) | [Source](src/year2019/day12.rs) | 1309 |
248+
| 11 | [Space Police](https://adventofcode.com/2019/day/11) | [Source](src/year2019/day11.rs) | 320 |
249+
| 12 | [The N-Body Problem](https://adventofcode.com/2019/day/12) | [Source](src/year2019/day12.rs) | 838 |
250250
| 13 | [Care Package](https://adventofcode.com/2019/day/13) | [Source](src/year2019/day13.rs) | 2527 |
251251
| 14 | [Space Stoichiometry](https://adventofcode.com/2019/day/14) | [Source](src/year2019/day14.rs) | 17 |
252-
| 15 | [Oxygen System](https://adventofcode.com/2019/day/15) | [Source](src/year2019/day15.rs) | 360 |
252+
| 15 | [Oxygen System](https://adventofcode.com/2019/day/15) | [Source](src/year2019/day15.rs) | 291 |
253253
| 16 | [Flawed Frequency Transmission](https://adventofcode.com/2019/day/16) | [Source](src/year2019/day16.rs) | 1956 |
254254
| 17 | [Set and Forget](https://adventofcode.com/2019/day/17) | [Source](src/year2019/day17.rs) | 338 |
255255
| 18 | [Many-Worlds Interpretation](https://adventofcode.com/2019/day/18) | [Source](src/year2019/day18.rs) | 1157 |

src/year2019/day02.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ fn search(input: &Input, x1: i32, x2: i32, y1: i32, y2: i32) -> Option<i32> {
6060

6161
let x = x1.midpoint(x2);
6262
let y = y1.midpoint(y2);
63-
let [a, b, c] = input;
63+
let [a, b, c] = *input;
6464
let result = a * x + b * y + c;
6565

6666
match result.cmp(&19690720) {

src/year2019/day04.rs

Lines changed: 33 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -3,89 +3,54 @@
33
//! We speed things up by only checking numbers that have digits in non-decreasing order for pairs.
44
//! These numbers become rapidly less dense as the password value increases and there
55
//! are only 3003 total of these numbers with 6 digits.
6+
use crate::util::iter::*;
67
use crate::util::parse::*;
7-
use crate::util::slice::*;
88

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);
2010

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();
3213

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);
3616

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;
4619

4720
// Increase the starting number to the next number that has all digits in non-decreasing order
4821
// to ensure that the incrementing logic in the search loop works correctly.
4922
// 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);
5726
}
5827

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);
6931

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;
7336

7437
// 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+
}
7942

80-
let next = digits[i] + 1;
81-
while i <= 5 {
82-
digits[i] = next;
83-
i += 1;
84-
}
43+
(part_one, part_two)
44+
}
8545

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+
}
8953

90-
count
54+
fn to_digits(n: u32) -> [u8; 6] {
55+
format!("{n:06}").into_bytes().try_into().unwrap()
9156
}

src/year2019/day06.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ pub fn parse(input: &str) -> Vec<usize> {
4949
let mut parent = vec![0; lines.len() + 2];
5050

5151
for line in lines {
52-
let left = lookup(&line[..3]);
53-
let right = lookup(&line[4..]);
52+
let left = lookup(&line[0..3]);
53+
let right = lookup(&line[4..7]);
5454
parent[right] = left;
5555
}
5656

@@ -71,8 +71,8 @@ pub fn part1(input: &[usize]) -> usize {
7171
}
7272

7373
let cache = &mut vec![None; input.len()];
74-
cache[0] = Some(0);
75-
cache[1] = Some(0);
74+
cache[0] = Some(0); // Special empty value
75+
cache[1] = Some(0); // COM
7676
(0..input.len()).map(|index| orbits(input, cache, index)).sum()
7777
}
7878

src/year2019/day08.rs

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
//! # Space Image Format
2+
const WIDTH: usize = 25;
3+
const HEIGHT: usize = 6;
4+
const LAYER_SIZE: usize = WIDTH * HEIGHT;
25

3-
pub fn parse(input: &str) -> &str {
4-
input
6+
pub fn parse(input: &str) -> &[u8] {
7+
input.as_bytes()
58
}
69

710
/// Each layer is 25 * 6 = 150 bytes and there are 100 layers total.
@@ -10,64 +13,56 @@ pub fn parse(input: &str) -> &str {
1013
/// so we must handle the last 6 bytes specially.
1114
///
1215
/// [`count_ones`]: u64::count_ones
13-
pub fn part1(input: &str) -> u32 {
14-
let bytes = input.as_bytes();
15-
let mut index = 0;
16-
let mut ones = 0;
17-
let mut twos = 0;
16+
pub fn part1(input: &[u8]) -> u32 {
1817
let mut most = 0;
1918
let mut result = 0;
2019

21-
for _ in 0..100 {
20+
for layer in input.chunks_exact(LAYER_SIZE) {
21+
let mut ones = 0;
22+
let mut twos = 0;
23+
2224
// First 144 of 150 bytes.
23-
for _ in 0..18 {
24-
let slice = &bytes[index..(index + 8)];
25+
for slice in layer.chunks_exact(8) {
2526
let n = u64::from_be_bytes(slice.try_into().unwrap());
2627
ones += (n & 0x0101010101010101).count_ones();
2728
twos += (n & 0x0202020202020202).count_ones();
28-
index += 8;
2929
}
3030

3131
// Handle remaining 6 bytes.
3232
// The masks exclude the most significant 2 bytes to prevent double counting.
33-
let slice = &bytes[(index - 2)..(index + 6)];
33+
let slice = &layer[142..150];
3434
let n = u64::from_be_bytes(slice.try_into().unwrap());
3535
ones += (n & 0x0000010101010101).count_ones();
3636
twos += (n & 0x0000020202020202).count_ones();
37-
index += 6;
3837

3938
if ones + twos > most {
4039
most = ones + twos;
4140
result = ones * twos;
4241
}
43-
44-
ones = 0;
45-
twos = 0;
4642
}
4743

4844
result
4945
}
5046

5147
/// Since a black or white pixel covers those in lower layers, it's faster to check each pixel
5248
/// stopping as soon as we hit a non-transparent value.
53-
pub fn part2(input: &str) -> String {
54-
let bytes = input.as_bytes();
55-
let mut image = ['.'; 150];
49+
pub fn part2(input: &[u8]) -> String {
50+
// Ensure enough capacity including newlines.
51+
let mut result = String::with_capacity((WIDTH + 1) * HEIGHT);
5652

57-
for (i, pixel) in image.iter_mut().enumerate() {
58-
let mut j = i;
53+
for y in 0..HEIGHT {
54+
result.push('\n');
5955

60-
while bytes[j] == b'2' {
61-
j += 150;
62-
}
56+
for x in 0..WIDTH {
57+
let mut i = WIDTH * y + x;
58+
59+
while input[i] == b'2' {
60+
i += LAYER_SIZE;
61+
}
6362

64-
if bytes[j] == b'1' {
65-
*pixel = '#';
63+
result.push(if input[i] == b'1' { '#' } else { '.' });
6664
}
6765
}
6866

69-
let mut result =
70-
image.chunks_exact(25).map(|row| row.iter().collect()).collect::<Vec<String>>().join("\n");
71-
result.insert(0, '\n');
7267
result
7368
}

src/year2019/day11.rs

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//!
33
//! This problem is a variant of [Langton's Ant](https://en.wikipedia.org/wiki/Langton%27s_ant).
44
use super::intcode::*;
5+
use crate::util::grid::*;
56
use crate::util::hash::*;
67
use crate::util::parse::*;
78
use crate::util::point::*;
@@ -21,38 +22,24 @@ pub fn part2(input: &[i64]) -> String {
2122
let panels: Vec<_> = hull.iter().filter(|&(_, &v)| v == 1).map(|(&k, _)| k).collect();
2223

2324
// Get maximum extents
24-
let mut x1 = i32::MAX;
25-
let mut x2 = i32::MIN;
26-
let mut y1 = i32::MAX;
27-
let mut y2 = i32::MIN;
28-
29-
for &point in &panels {
30-
x1 = x1.min(point.x);
31-
x2 = x2.max(point.x);
32-
y1 = y1.min(point.y);
33-
y2 = y2.max(point.y);
34-
}
25+
let (x1, x2, y1, y2) = panels.iter().fold(
26+
(i32::MAX, i32::MIN, i32::MAX, i32::MIN),
27+
|(min_x, max_x, min_y, max_y), p| {
28+
(min_x.min(p.x), max_x.max(p.x), min_y.min(p.y), max_y.max(p.y))
29+
},
30+
);
3531

3632
// Convert panels to characters
37-
let width = (x2 - x1 + 1) as usize;
38-
let height = (y2 - y1 + 1) as usize;
39-
let offset = Point::new(x1, y1);
40-
let mut image = vec!['.'; width * height];
33+
let width = x2 - x1 + 2; // Leave room for newline character.
34+
let height = y2 - y1 + 1;
35+
let mut image = Grid::new(width, height, b'.');
4136

42-
for &point in &panels {
43-
let adjusted = point - offset;
44-
let index = (width * adjusted.y as usize) + (adjusted.x as usize);
45-
image[index] = '#';
46-
}
37+
let offset = Point::new(x1 - 1, y1);
38+
panels.iter().for_each(|&point| image[point - offset] = b'#');
39+
40+
(0..height).for_each(|y| image[Point::new(0, y)] = b'\n');
4741

48-
// Convert to multiline string
49-
let mut result = image
50-
.chunks_exact(width)
51-
.map(|row| row.iter().collect())
52-
.collect::<Vec<String>>()
53-
.join("\n");
54-
result.insert(0, '\n');
55-
result
42+
String::from_utf8(image.bytes).unwrap()
5643
}
5744

5845
fn paint(input: &[i64], initial: i64) -> FastMap<Point, i64> {
@@ -64,12 +51,12 @@ fn paint(input: &[i64], initial: i64) -> FastMap<Point, i64> {
6451
hull.insert(position, initial);
6552

6653
loop {
67-
let panel = hull.get(&position).unwrap_or(&0);
54+
let panel = hull.entry(position).or_default();
6855
computer.input(*panel);
6956

7057
match computer.run() {
7158
State::Output(color) => {
72-
hull.insert(position, color);
59+
*panel = color;
7360
}
7461
_ => break,
7562
}

src/year2019/day12.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//! [`signum`]: i32::signum
1717
use crate::util::math::*;
1818
use crate::util::parse::*;
19+
use std::array::from_fn;
1920

2021
type Axis = [i32; 8];
2122
type Input = [Axis; 3];
@@ -39,16 +40,16 @@ pub fn part1(input: &Input) -> i32 {
3940
z = step(z);
4041
}
4142

42-
let e: Vec<_> = (0..8).map(|i| x[i].abs() + y[i].abs() + z[i].abs()).collect();
43-
e[0] * e[4] + e[1] * e[5] + e[2] * e[6] + e[3] * e[7]
43+
let [p0, p1, p2, p3, v0, v1, v2, v3] = from_fn(|i| x[i].abs() + y[i].abs() + z[i].abs());
44+
p0 * v0 + p1 * v1 + p2 * v2 + p3 * v3
4445
}
4546

4647
pub fn part2(input: &Input) -> usize {
4748
let [mut x, mut y, mut z] = *input;
4849
let [mut a, mut b, mut c] = [0, 0, 0];
4950
let mut count = 0;
5051

51-
while a == 0 || b == 0 || c == 0 {
52+
while a * b * c == 0 {
5253
count += 1;
5354

5455
if a == 0 {
@@ -81,10 +82,17 @@ fn step(axis: Axis) -> Axis {
8182
// "p" is position and "v" velocity
8283
let [p0, p1, p2, p3, v0, v1, v2, v3] = axis;
8384

84-
let n0 = v0 + (p1 - p0).signum() + (p2 - p0).signum() + (p3 - p0).signum();
85-
let n1 = v1 + (p0 - p1).signum() + (p2 - p1).signum() + (p3 - p1).signum();
86-
let n2 = v2 + (p0 - p2).signum() + (p1 - p2).signum() + (p3 - p2).signum();
87-
let n3 = v3 + (p0 - p3).signum() + (p1 - p3).signum() + (p2 - p3).signum();
85+
let a = (p1 - p0).signum();
86+
let b = (p2 - p0).signum();
87+
let c = (p3 - p0).signum();
88+
let d = (p2 - p1).signum();
89+
let e = (p3 - p1).signum();
90+
let f = (p3 - p2).signum();
91+
92+
let n0 = v0 + a + b + c;
93+
let n1 = v1 - a + d + e;
94+
let n2 = v2 - b - d + f;
95+
let n3 = v3 - c - e - f;
8896

8997
[p0 + n0, p1 + n1, p2 + n2, p3 + n3, n0, n1, n2, n3]
9098
}

0 commit comments

Comments
 (0)