Skip to content

Commit b9350b6

Browse files
committed
Year 2018 speed and code quality improvements
1 parent 49af580 commit b9350b6

File tree

9 files changed

+74
-120
lines changed

9 files changed

+74
-120
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,8 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
267267

268268
| Day | Problem | Solution | Benchmark (μs) |
269269
| --- | --- | --- | --: |
270-
| 1 | [Chronal Calibration](https://adventofcode.com/2018/day/1) | [Source](src/year2018/day01.rs) | 16 |
271-
| 2 | [Inventory Management System](https://adventofcode.com/2018/day/2) | [Source](src/year2018/day02.rs) | 49 |
270+
| 1 | [Chronal Calibration](https://adventofcode.com/2018/day/1) | [Source](src/year2018/day01.rs) | 11 |
271+
| 2 | [Inventory Management System](https://adventofcode.com/2018/day/2) | [Source](src/year2018/day02.rs) | 59 |
272272
| 3 | [No Matter How You Slice It](https://adventofcode.com/2018/day/3) | [Source](src/year2018/day03.rs) | 55 |
273273
| 4 | [Repose Record](https://adventofcode.com/2018/day/4) | [Source](src/year2018/day04.rs) | 46 |
274274
| 5 | [Alchemical Reduction](https://adventofcode.com/2018/day/5) | [Source](src/year2018/day05.rs) | 390 |
@@ -279,7 +279,7 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
279279
| 10 | [The Stars Align](https://adventofcode.com/2018/day/10) | [Source](src/year2018/day10.rs) | 11 |
280280
| 11 | [Chronal Charge](https://adventofcode.com/2018/day/11) | [Source](src/year2018/day11.rs) | 1156 |
281281
| 12 | [Subterranean Sustainability](https://adventofcode.com/2018/day/12) | [Source](src/year2018/day12.rs) | 77 |
282-
| 13 | [Mine Cart Madness](https://adventofcode.com/2018/day/13) | [Source](src/year2018/day13.rs) | 382 |
282+
| 13 | [Mine Cart Madness](https://adventofcode.com/2018/day/13) | [Source](src/year2018/day13.rs) | 349 |
283283
| 14 | [Chocolate Charts](https://adventofcode.com/2018/day/14) | [Source](src/year2018/day14.rs) | 24000 |
284284
| 15 | [Beverage Bandits](https://adventofcode.com/2018/day/15) | [Source](src/year2018/day15.rs) | 583 |
285285
| 16 | [Chronal Classification](https://adventofcode.com/2018/day/16) | [Source](src/year2018/day16.rs) | 37 |

src/year2018/day01.rs

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,16 @@ pub fn part2(input: &[i32]) -> i32 {
5353

5454
seen.sort_unstable();
5555

56-
// Compare each adjacent pair of tuples to find candidates, then sort by smallest gap first,
57-
// tie breaking with index if needed.
58-
let mut pairs = Vec::new();
56+
// Compare each adjacent pair of tuples to find candidates, then find the first tuple
57+
// sorting by smallest gap first, tie breaking with index if needed.
58+
seen.windows(2)
59+
.filter_map(|window| {
60+
let (remainder0, freq0, index0) = window[0];
61+
let (remainder1, freq1, _) = window[1];
5962

60-
for window in seen.windows(2) {
61-
let (remainder0, freq0, index0) = window[0];
62-
let (remainder1, freq1, _) = window[1];
63-
64-
if remainder0 == remainder1 {
65-
pairs.push((freq1 - freq0, index0, freq1));
66-
}
67-
}
68-
69-
pairs.sort_unstable();
70-
71-
// Result is the frequency of the first tuple.
72-
let (_, _, freq) = pairs[0];
73-
freq
63+
(remainder0 == remainder1).then_some((freq1 - freq0, index0, freq1))
64+
})
65+
.min()
66+
.map(|(_, _, freq)| freq)
67+
.unwrap()
7468
}

src/year2018/day02.rs

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,22 @@ pub fn parse(input: &str) -> Vec<&[u8]> {
66
}
77

88
pub fn part1(input: &[&[u8]]) -> u32 {
9-
let mut total_twos = 0;
10-
let mut total_threes = 0;
9+
let mut twos = 0;
10+
let mut threes = 0;
1111

1212
for &id in input {
1313
// Ids are lowercase ASCII only with cardinality of 26.
14-
let mut freq = [0; 26];
15-
let mut twos = 0;
16-
let mut threes = 0;
14+
let mut freq = [0_u8; 26];
1715

1816
for &b in id {
19-
let index = (b - b'a') as usize;
20-
let current = freq[index];
21-
22-
match current {
23-
0 => (),
24-
1 => twos += 1,
25-
2 => {
26-
twos -= 1;
27-
threes += 1;
28-
}
29-
_ => threes -= 1,
30-
}
31-
32-
freq[index] += 1;
17+
freq[(b - b'a') as usize] += 1;
3318
}
3419

35-
if twos > 0 {
36-
total_twos += 1;
37-
}
38-
if threes > 0 {
39-
total_threes += 1;
40-
}
20+
twos += freq.contains(&2) as u32;
21+
threes += freq.contains(&3) as u32;
4122
}
4223

43-
total_twos * total_threes
24+
twos * threes
4425
}
4526

4627
pub fn part2(input: &[&[u8]]) -> String {

src/year2018/day03.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,18 @@ use crate::util::parse::*;
77

88
type Input = (u32, usize);
99

10+
/// Each square inch of fabric is stored in a single bit.
11+
/// The fabric is 1000 inches wide requiring sixteen `u64`.
12+
const WIDTH: usize = 16;
13+
const HEIGHT: usize = 1000;
14+
1015
pub fn parse(input: &str) -> Input {
1116
let claims: Vec<_> = input
1217
.iter_unsigned::<usize>()
1318
.chunk::<5>()
1419
.map(|[_, x1, y1, width, height]| {
15-
let start = 16 * y1 + (x1 / 64);
16-
let end = start + 16 * height;
20+
let start = WIDTH * y1 + (x1 / 64);
21+
let end = start + WIDTH * height;
1722

1823
// Create bitmask for each claim, for example `#123 @ 3,2: 5x4` becomes `11111000`.
1924
// Use an intermediate u128 to handle claims up to 65 inches wide.
@@ -25,13 +30,11 @@ pub fn parse(input: &str) -> Input {
2530
})
2631
.collect();
2732

28-
// Each square inch of fabric is stored in a single bit.
29-
// The fabric is 1000 inches wide requiring sixteen `u64`.
30-
let mut fabric = vec![0; 16 * 1000];
31-
let mut overlap = vec![0; 16 * 1000];
33+
let mut fabric = vec![0; WIDTH * HEIGHT];
34+
let mut overlap = vec![0; WIDTH * HEIGHT];
3235

3336
for &(start, end, lower, upper) in &claims {
34-
for index in (start..end).step_by(16) {
37+
for index in (start..end).step_by(WIDTH) {
3538
overlap[index] |= fabric[index] & lower;
3639
fabric[index] |= lower;
3740

@@ -42,15 +45,20 @@ pub fn parse(input: &str) -> Input {
4245
}
4346
}
4447

48+
// Count the area of overlapping claims.
49+
let part_one = overlap.iter().map(|n| n.count_ones()).sum();
50+
4551
// Find the first claim that doesn't overlap with any other claim.
46-
let position = claims.iter().position(|&(start, end, lower, upper)| {
47-
(start..end).step_by(16).all(|index| {
48-
(overlap[index] & lower == 0) && (upper == 0 || overlap[index + 1] & upper == 0)
52+
let part_two = claims
53+
.iter()
54+
.position(|&(start, end, lower, upper)| {
55+
(start..end).step_by(WIDTH).all(|index| {
56+
(overlap[index] & lower == 0) && (upper == 0 || overlap[index + 1] & upper == 0)
57+
})
4958
})
50-
});
59+
.map(|id| id + 1)
60+
.unwrap();
5161

52-
let part_one = overlap.iter().map(|n| n.count_ones()).sum();
53-
let part_two = position.unwrap() + 1;
5462
(part_one, part_two)
5563
}
5664

src/year2018/day04.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ type Input = FastMap<usize, [u32; 60]>;
66

77
pub fn parse(input: &str) -> Input {
88
// Records need to be in chronological order.
9-
let mut records: Vec<_> = input.lines().map(str::as_bytes).collect();
9+
let mut records: Vec<_> = input.lines().collect();
1010
records.sort_unstable();
1111

1212
// Build each sleep schedule
@@ -16,13 +16,13 @@ pub fn parse(input: &str) -> Input {
1616

1717
for record in records {
1818
match record.len() {
19-
31 => start = to_index(&record[15..17]),
19+
31 => start = (&record[15..17]).unsigned(),
2020
27 => {
21-
let end = to_index(&record[15..17]);
21+
let end = (&record[15..17]).unsigned();
2222
let minutes = guards.entry(id).or_insert_with(|| [0; 60]);
2323
(start..end).for_each(|i| minutes[i] += 1);
2424
}
25-
_ => id = to_index(&record[26..record.len() - 13]),
25+
_ => id = (&record[26..record.len() - 13]).unsigned(),
2626
}
2727
}
2828

@@ -39,15 +39,11 @@ pub fn part2(input: &Input) -> usize {
3939
choose(input, |(_, m)| *m.iter().max().unwrap())
4040
}
4141

42-
fn choose(input: &Input, strategy: impl Fn(&(&usize, &[u32; 60])) -> u32) -> usize {
42+
fn choose(input: &Input, strategy: fn(&(&usize, &[u32; 60])) -> u32) -> usize {
4343
// Find the guard using a specific strategy.
4444
let (id, minutes) = input.iter().max_by_key(strategy).unwrap();
4545
// Find the minute spent asleep the most
4646
let (minute, _) = minutes.iter().enumerate().max_by_key(|(_, m)| **m).unwrap();
4747
// Return result
4848
id * minute
4949
}
50-
51-
fn to_index(slice: &[u8]) -> usize {
52-
slice.iter().fold(0, |acc, n| 10 * acc + n.to_decimal() as usize)
53-
}

src/year2018/day07.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,19 @@ pub struct Step {
1414
}
1515

1616
pub fn parse(input: &str) -> Input {
17-
let mut steps = FastMap::new();
17+
let mut steps: Input = FastMap::new();
1818

1919
for line in input.lines().map(str::as_bytes) {
2020
// Each step is a single uppercase letter.
2121
let from = line[5];
2222
let to = line[36];
2323

2424
// Add all steps that depend on this one to children vec.
25-
let step = steps.entry(from).or_insert(Step::default());
26-
step.children.push(to);
25+
steps.entry(from).or_default().children.push(to);
2726

2827
// Count how many steps must finish before this step is ready.
2928
// We only need the total count, the exact steps are not necessary.
30-
let step = steps.entry(to).or_insert(Step::default());
31-
step.remaining += 1;
29+
steps.entry(to).or_default().remaining += 1;
3230
}
3331

3432
steps

src/year2018/day08.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ pub fn part2(input: &Input) -> usize {
1919
}
2020

2121
fn parse_node(iter: &mut impl Iterator<Item = usize>, stack: &mut Vec<usize>) -> (usize, usize) {
22+
// Save stack size
23+
let stack_base = stack.len();
24+
2225
// Parse header
2326
let child_count = iter.next().unwrap();
2427
let metadata_count = iter.next().unwrap();
@@ -42,12 +45,12 @@ fn parse_node(iter: &mut impl Iterator<Item = usize>, stack: &mut Vec<usize>) ->
4245
if child_count == 0 {
4346
score += n;
4447
} else if n > 0 && n <= child_count {
45-
score += stack[stack.len() - child_count + (n - 1)];
48+
score += stack[stack_base + (n - 1)];
4649
}
4750
}
4851

4952
// Pop child nodes from the stack.
50-
stack.truncate(stack.len() - child_count);
53+
stack.truncate(stack_base);
5154

5255
(metadata, score)
5356
}

src/year2018/day10.rs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,19 +60,12 @@ pub fn part2(input: &Input) -> i32 {
6060
}
6161

6262
fn bounding_box(points: &[Point]) -> (i32, i32, i32, i32) {
63-
let mut min_x = i32::MAX;
64-
let mut max_x = i32::MIN;
65-
let mut min_y = i32::MAX;
66-
let mut max_y = i32::MIN;
67-
68-
for p in points {
69-
min_x = min_x.min(p.x);
70-
max_x = max_x.max(p.x);
71-
min_y = min_y.min(p.y);
72-
max_y = max_y.max(p.y);
73-
}
74-
75-
(min_x, max_x, min_y, max_y)
63+
points.iter().fold(
64+
(i32::MAX, i32::MIN, i32::MAX, i32::MIN),
65+
|(min_x, max_x, min_y, max_y), p| {
66+
(min_x.min(p.x), max_x.max(p.x), min_y.min(p.y), max_y.max(p.y))
67+
},
68+
)
7669
}
7770

7871
fn size(points: &[Point]) -> i32 {

src/year2018/day13.rs

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub struct Input {
1616
pub struct Cart {
1717
position: Point,
1818
direction: Point,
19-
turns: u32,
19+
turns: u8,
2020
active: bool,
2121
}
2222

@@ -29,30 +29,13 @@ impl Cart {
2929
self.position += self.direction;
3030

3131
match grid[self.position] {
32-
b'\\' => {
33-
self.direction = match self.direction {
34-
UP => LEFT,
35-
DOWN => RIGHT,
36-
LEFT => UP,
37-
RIGHT => DOWN,
38-
_ => unreachable!(),
39-
}
40-
}
41-
b'/' => {
42-
self.direction = match self.direction {
43-
UP => RIGHT,
44-
DOWN => LEFT,
45-
LEFT => DOWN,
46-
RIGHT => UP,
47-
_ => unreachable!(),
48-
}
49-
}
32+
b'\\' => self.direction = Point::new(self.direction.y, self.direction.x),
33+
b'/' => self.direction = Point::new(-self.direction.y, -self.direction.x),
5034
b'+' => {
5135
self.direction = match self.turns {
5236
0 => self.direction.counter_clockwise(),
5337
1 => self.direction,
54-
2 => self.direction.clockwise(),
55-
_ => unreachable!(),
38+
_ => self.direction.clockwise(), // 2 turns
5639
};
5740
self.turns = (self.turns + 1) % 3;
5841
}
@@ -66,19 +49,17 @@ pub fn parse(input: &str) -> Input {
6649
let mut carts = Vec::new();
6750

6851
for (i, b) in grid.bytes.iter().enumerate() {
69-
let result = match b {
70-
b'^' => Some(UP),
71-
b'v' => Some(DOWN),
72-
b'<' => Some(LEFT),
73-
b'>' => Some(RIGHT),
74-
_ => None,
52+
let direction = match b {
53+
b'^' => UP,
54+
b'v' => DOWN,
55+
b'<' => LEFT,
56+
b'>' => RIGHT,
57+
_ => continue,
7558
};
7659

77-
if let Some(direction) = result {
78-
let x = i as i32 % grid.width;
79-
let y = i as i32 / grid.width;
80-
carts.push(Cart::new(Point::new(x, y), direction));
81-
}
60+
let x = i as i32 % grid.width;
61+
let y = i as i32 / grid.width;
62+
carts.push(Cart::new(Point::new(x, y), direction));
8263
}
8364

8465
Input { grid, carts }

0 commit comments

Comments
 (0)