Skip to content

Commit a8f4b25

Browse files
committed
Year 2021 speed and code quality improvements
1 parent 02e285c commit a8f4b25

File tree

8 files changed

+120
-149
lines changed

8 files changed

+120
-149
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,14 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
173173
| --- | --- | --- | --: |
174174
| 1 | [Sonar Sweep](https://adventofcode.com/2021/day/1) | [Source](src/year2021/day01.rs) | 6 |
175175
| 2 | [Dive!](https://adventofcode.com/2021/day/2) | [Source](src/year2021/day02.rs) | 12 |
176-
| 3 | [Binary Diagnostic](https://adventofcode.com/2021/day/3) | [Source](src/year2021/day03.rs) | 20 |
177-
| 4 | [Giant Squid](https://adventofcode.com/2021/day/4) | [Source](src/year2021/day04.rs) | 12 |
176+
| 3 | [Binary Diagnostic](https://adventofcode.com/2021/day/3) | [Source](src/year2021/day03.rs) | 22 |
177+
| 4 | [Giant Squid](https://adventofcode.com/2021/day/4) | [Source](src/year2021/day04.rs) | 8 |
178178
| 5 | [Hydrothermal Venture](https://adventofcode.com/2021/day/5) | [Source](src/year2021/day05.rs) | 181 |
179179
| 6 | [Lanternfish](https://adventofcode.com/2021/day/6) | [Source](src/year2021/day06.rs) | 1 |
180180
| 7 | [The Treachery of Whales](https://adventofcode.com/2021/day/7) | [Source](src/year2021/day07.rs) | 8 |
181181
| 8 | [Seven Segment Search](https://adventofcode.com/2021/day/8) | [Source](src/year2021/day08.rs) | 14 |
182182
| 9 | [Smoke Basin](https://adventofcode.com/2021/day/9) | [Source](src/year2021/day09.rs) | 64 |
183-
| 10 | [Syntax Scoring](https://adventofcode.com/2021/day/10) | [Source](src/year2021/day10.rs) | 25 |
183+
| 10 | [Syntax Scoring](https://adventofcode.com/2021/day/10) | [Source](src/year2021/day10.rs) | 14 |
184184
| 11 | [Dumbo Octopus](https://adventofcode.com/2021/day/11) | [Source](src/year2021/day11.rs) | 55 |
185185
| 12 | [Passage Pathing](https://adventofcode.com/2021/day/12) | [Source](src/year2021/day12.rs) | 25 |
186186
| 13 | [Transparent Origami](https://adventofcode.com/2021/day/13) | [Source](src/year2021/day13.rs) | 22 |

src/year2021/day02.rs

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,37 @@ pub enum Sub {
1616
}
1717

1818
pub fn parse(input: &str) -> Vec<Sub> {
19-
let helper = |[a, b]: [&str; 2]| {
20-
let amount = b.signed();
21-
match a {
22-
"up" => Sub::Up(amount),
23-
"down" => Sub::Down(amount),
24-
"forward" => Sub::Forward(amount),
25-
_ => unreachable!(),
26-
}
27-
};
28-
input.split_ascii_whitespace().chunk::<2>().map(helper).collect()
19+
input
20+
.split_ascii_whitespace()
21+
.chunk::<2>()
22+
.map(|[first, second]| {
23+
let amount = second.signed();
24+
match first {
25+
"up" => Sub::Up(amount),
26+
"down" => Sub::Down(amount),
27+
"forward" => Sub::Forward(amount),
28+
_ => unreachable!(),
29+
}
30+
})
31+
.collect()
2932
}
3033

3134
pub fn part1(input: &[Sub]) -> i32 {
32-
let helper = |(position, depth), next| match next {
33-
Sub::Up(n) => (position, depth - n),
34-
Sub::Down(n) => (position, depth + n),
35-
Sub::Forward(n) => (position + n, depth),
36-
};
37-
let (position, depth) = input.iter().copied().fold((0, 0), helper);
35+
let (position, depth) =
36+
input.iter().copied().fold((0, 0), |(position, depth), next| match next {
37+
Sub::Up(n) => (position, depth - n),
38+
Sub::Down(n) => (position, depth + n),
39+
Sub::Forward(n) => (position + n, depth),
40+
});
3841
position * depth
3942
}
4043

4144
pub fn part2(input: &[Sub]) -> i32 {
42-
let helper = |(position, depth, aim), next| match next {
43-
Sub::Up(n) => (position, depth, aim - n),
44-
Sub::Down(n) => (position, depth, aim + n),
45-
Sub::Forward(n) => (position + n, depth + aim * n, aim),
46-
};
47-
let (position, depth, _) = input.iter().copied().fold((0, 0, 0), helper);
45+
let (position, depth, _) =
46+
input.iter().copied().fold((0, 0, 0), |(position, depth, aim), next| match next {
47+
Sub::Up(n) => (position, depth, aim - n),
48+
Sub::Down(n) => (position, depth, aim + n),
49+
Sub::Forward(n) => (position + n, depth + aim * n, aim),
50+
});
4851
position * depth
4952
}

src/year2021/day03.rs

Lines changed: 25 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,77 +2,51 @@
22
//!
33
//! Part 1 uses bit manipulation to build up the binary numbers directly one digit at a time.
44
//!
5-
//! Part 2 clones the input `vec` then uses [`swap_remove`] to efficiently discard numbers that
6-
//! don't meet the criteria without having to move all subsequent elements.
5+
//! Part 2 clones the input `vec` then uses [`retain`] to efficiently discard numbers that
6+
//! don't meet the criteria.
77
//!
8-
//! [`swap_remove`]: Vec::swap_remove
9-
10-
pub struct Input<'a> {
11-
width: usize,
12-
numbers: Vec<&'a [u8]>,
13-
}
14-
15-
pub fn parse(input: &str) -> Input<'_> {
16-
let numbers: Vec<_> = input.lines().map(str::as_bytes).collect();
17-
Input { width: numbers[0].len(), numbers }
8+
//! [`retain`]: Vec::retain
9+
pub fn parse(input: &str) -> Vec<&[u8]> {
10+
input.lines().map(str::as_bytes).collect()
1811
}
1912

20-
pub fn part1(input: &Input<'_>) -> u32 {
13+
pub fn part1(input: &[&[u8]]) -> u32 {
2114
let mut gamma = 0;
2215
let mut epsilon = 0;
2316

24-
for i in 0..input.width {
25-
let sum = sum(&input.numbers, i);
26-
if sum > input.numbers.len() - sum {
27-
gamma = (gamma << 1) | 1;
28-
epsilon <<= 1;
29-
} else {
30-
gamma <<= 1;
31-
epsilon = (epsilon << 1) | 1;
32-
}
17+
for column in 0..input[0].len() {
18+
let ones = ones(input, column);
19+
let zeros = input.len() - ones;
20+
21+
gamma = (gamma << 1) | (ones > zeros) as u32;
22+
epsilon = (epsilon << 1) | (zeros > ones) as u32;
3323
}
3424

3525
gamma * epsilon
3626
}
3727

38-
pub fn part2(input: &Input<'_>) -> u32 {
28+
pub fn part2(input: &[&[u8]]) -> u32 {
3929
let gamma = rating(input, |a, b| a >= b);
4030
let epsilon = rating(input, |a, b| a < b);
4131
gamma * epsilon
4232
}
4333

44-
fn sum(numbers: &[&[u8]], i: usize) -> usize {
45-
let total: usize = numbers.iter().map(|b| b[i] as usize).sum();
46-
total - 48 * numbers.len()
34+
fn ones(numbers: &[&[u8]], i: usize) -> usize {
35+
numbers.iter().filter(|b| b[i] == b'1').count()
4736
}
4837

49-
fn fold(numbers: &[u8], width: usize) -> u32 {
50-
numbers.iter().take(width).fold(0, |acc, &n| (acc << 1) | (n & 1) as u32)
51-
}
38+
fn rating(input: &[&[u8]], cmp: fn(usize, usize) -> bool) -> u32 {
39+
let mut numbers = input.to_vec();
40+
let mut column = 0;
5241

53-
fn rating(input: &Input<'_>, cmp: impl Fn(usize, usize) -> bool) -> u32 {
54-
let mut numbers = input.numbers.clone();
42+
while numbers.len() > 1 {
43+
let ones = ones(&numbers, column);
44+
let zeros = numbers.len() - ones;
45+
let keep = if cmp(ones, zeros) { b'1' } else { b'0' };
5546

56-
for i in 0..input.width {
57-
let sum = sum(&numbers, i);
58-
let keep = if cmp(sum, numbers.len() - sum) { b'1' } else { b'0' };
59-
filter(&mut numbers, i, keep);
60-
if numbers.len() == 1 {
61-
return fold(numbers[0], input.width);
62-
}
47+
numbers.retain(|n| n[column] == keep);
48+
column += 1;
6349
}
6450

65-
unreachable!()
66-
}
67-
68-
fn filter(numbers: &mut Vec<&[u8]>, i: usize, keep: u8) {
69-
let mut j = 0;
70-
71-
while j < numbers.len() {
72-
if numbers[j][i] == keep {
73-
j += 1;
74-
} else {
75-
numbers.swap_remove(j);
76-
}
77-
}
51+
numbers[0].iter().fold(0, |acc, &n| (acc << 1) | (n == b'1') as u32)
7852
}

src/year2021/day04.rs

Lines changed: 29 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,60 +10,51 @@
1010
//! these maximum values. This is the turn that the entire board will win.
1111
//!
1212
//! Filtering the board numbers by turn and a reverse lookup from turn to number gives the
13-
//! score for each board. Sort each result by turn and the answers for part 1 and part1 are the
13+
//! score for each board. Sort each result by turn and the answers for part one and two are the
1414
//! first and last values respectively.
1515
use crate::util::parse::*;
1616
use std::array::from_fn;
1717

18-
pub struct Input {
18+
const BOARD_SIZE: usize = 25;
19+
const ROWS_AND_COLS: [(usize, usize); 10] =
20+
[(0, 1), (5, 1), (10, 1), (15, 1), (20, 1), (0, 5), (1, 5), (2, 5), (3, 5), (4, 5)];
21+
22+
pub struct Board {
1923
turn: usize,
2024
score: usize,
2125
}
2226

23-
pub fn parse(input: &str) -> Vec<Input> {
24-
let mut to_turn = [0; 100];
25-
let mut from_turn = [0; 100];
27+
pub fn parse(input: &str) -> Vec<Board> {
28+
let (prefix, suffix) = input.split_once("\n\n").unwrap();
29+
let boards: Vec<_> = suffix.iter_unsigned().collect();
2630

27-
let mut chunks = input.split("\n\n");
31+
let mut number_to_turn = [0; 100];
32+
let mut turn_to_number = [0; 100];
2833

29-
for (i, n) in chunks.next().unwrap().iter_unsigned().enumerate() {
30-
to_turn[n] = i;
31-
from_turn[i] = n;
34+
for (i, n) in prefix.iter_unsigned().enumerate() {
35+
number_to_turn[n] = i;
36+
turn_to_number[i] = n;
3237
}
3338

34-
let score = |chunk: &str| {
35-
let mut iter = chunk.iter_unsigned();
36-
let board: [usize; 25] = from_fn(|_| iter.next().unwrap());
37-
let turns: [usize; 25] = from_fn(|i| to_turn[board[i]]);
38-
39-
let row_and_cols = [
40-
turns[0..5].iter().max().unwrap(),
41-
turns[5..10].iter().max().unwrap(),
42-
turns[10..15].iter().max().unwrap(),
43-
turns[15..20].iter().max().unwrap(),
44-
turns[20..25].iter().max().unwrap(),
45-
turns.iter().step_by(5).max().unwrap(),
46-
turns.iter().skip(1).step_by(5).max().unwrap(),
47-
turns.iter().skip(2).step_by(5).max().unwrap(),
48-
turns.iter().skip(3).step_by(5).max().unwrap(),
49-
turns.iter().skip(4).step_by(5).max().unwrap(),
50-
];
51-
let winning_turn = **row_and_cols.iter().min().unwrap();
52-
let unmarked: usize = board.iter().filter(|&&n| to_turn[n] > winning_turn).sum();
53-
let just_called = from_turn[winning_turn];
39+
boards
40+
.chunks_exact(BOARD_SIZE)
41+
.map(|board| {
42+
let turns: [usize; BOARD_SIZE] = from_fn(|i| number_to_turn[board[i]]);
43+
let max = |&(skip, step)| *turns.iter().skip(skip).step_by(step).take(5).max().unwrap();
5444

55-
Input { turn: winning_turn, score: unmarked * just_called }
56-
};
45+
let winning_turn = ROWS_AND_COLS.iter().map(max).min().unwrap();
46+
let unmarked: usize = board.iter().filter(|&&n| number_to_turn[n] > winning_turn).sum();
47+
let just_called = turn_to_number[winning_turn];
5748

58-
let mut scores: Vec<_> = chunks.map(score).collect();
59-
scores.sort_unstable_by_key(|s| s.turn);
60-
scores
49+
Board { turn: winning_turn, score: unmarked * just_called }
50+
})
51+
.collect()
6152
}
6253

63-
pub fn part1(input: &[Input]) -> usize {
64-
input.first().unwrap().score
54+
pub fn part1(input: &[Board]) -> usize {
55+
input.iter().min_by_key(|b| b.turn).unwrap().score
6556
}
6657

67-
pub fn part2(input: &[Input]) -> usize {
68-
input.last().unwrap().score
58+
pub fn part2(input: &[Board]) -> usize {
59+
input.iter().max_by_key(|b| b.turn).unwrap().score
6960
}

src/year2021/day05.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,32 @@
1313
use crate::util::iter::*;
1414
use crate::util::parse::*;
1515

16-
type Vent = [u32; 4];
16+
type Input = (usize, usize);
1717

18-
pub fn parse(input: &str) -> [usize; 2] {
18+
pub fn parse(input: &str) -> Input {
1919
let all: Vec<_> = input.iter_unsigned().chunk::<4>().collect();
2020
let (orthogonal, diagonal): (Vec<_>, Vec<_>) =
2121
all.iter().partition(|[x1, y1, x2, y2]| x1 == x2 || y1 == y2);
2222

2323
let mut grid = vec![0_u8; 1_000_000];
24-
let first = vents(&orthogonal, &mut grid);
25-
let second = vents(&diagonal, &mut grid);
24+
let first = vents(orthogonal, &mut grid);
25+
let second = vents(diagonal, &mut grid);
2626

27-
[first, second]
27+
(first, first + second)
2828
}
2929

30-
pub fn part1(input: &[usize]) -> usize {
31-
input[0]
30+
pub fn part1(input: &Input) -> usize {
31+
input.0
3232
}
3333

34-
pub fn part2(input: &[usize]) -> usize {
35-
input[0] + input[1]
34+
pub fn part2(input: &Input) -> usize {
35+
input.1
3636
}
3737

38-
fn vents(input: &[Vent], grid: &mut [u8]) -> usize {
38+
fn vents(input: Vec<[usize; 4]>, grid: &mut [u8]) -> usize {
3939
let mut result = 0;
4040

41-
for &[x1, y1, x2, y2] in input {
41+
for [x1, y1, x2, y2] in input {
4242
let (x1, y1, x2, y2) = (x1 as i32, y1 as i32, x2 as i32, y2 as i32);
4343
let count = (y2 - y1).abs().max((x2 - x1).abs());
4444
let delta = (y2 - y1).signum() * 1000 + (x2 - x1).signum();

src/year2021/day10.rs

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,41 @@
88
//! For part 2 the completion score is the remaining items on the stack, reversed and converted from
99
//! corresponding closing delimiters. For example the completion string `])}>` would have a stack
1010
//! that looks like `<{([`, where the right hand side is the top of the stack.
11-
pub fn parse(input: &str) -> Vec<&[u8]> {
12-
input.lines().map(str::as_bytes).collect()
13-
}
14-
15-
pub fn part1(input: &[&[u8]]) -> u64 {
16-
let mut stack = Vec::new();
17-
let mut score = 0;
18-
19-
for line in input {
20-
score += syntax_score(line, &mut stack);
21-
stack.clear();
22-
}
23-
24-
score
25-
}
11+
type Input = (u64, u64);
2612

27-
pub fn part2(input: &[&[u8]]) -> u64 {
13+
pub fn parse(input: &str) -> Input {
2814
let mut stack = Vec::new();
2915
let mut scores = Vec::new();
16+
let mut part_one = 0;
3017

31-
for line in input {
32-
if syntax_score(line, &mut stack) == 0 {
18+
for line in input.lines() {
19+
let score = syntax_score(line, &mut stack);
20+
21+
if score == 0 {
3322
scores.push(autocomplete_score(&stack));
23+
} else {
24+
part_one += score;
3425
}
26+
3527
stack.clear();
3628
}
3729

3830
scores.sort_unstable();
39-
scores[scores.len() / 2]
31+
let part_two = scores[scores.len() / 2];
32+
33+
(part_one, part_two)
34+
}
35+
36+
pub fn part1(input: &Input) -> u64 {
37+
input.0
38+
}
39+
40+
pub fn part2(input: &Input) -> u64 {
41+
input.1
4042
}
4143

42-
fn syntax_score(line: &[u8], stack: &mut Vec<u8>) -> u64 {
43-
for &b in line {
44+
fn syntax_score(line: &str, stack: &mut Vec<u8>) -> u64 {
45+
for b in line.bytes() {
4446
match b {
4547
b'(' | b'[' | b'{' | b'<' => stack.push(b),
4648
b')' => {

src/year2021/day11.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub fn part2(input: &Input) -> usize {
3636
steps
3737
}
3838

39-
fn simulate(input: &Input, predicate: impl Fn(usize, usize) -> bool) -> (usize, usize) {
39+
fn simulate(input: &Input, predicate: fn(usize, usize) -> bool) -> (usize, usize) {
4040
let mut grid = *input;
4141
let mut flashed = [true; 144];
4242
let mut todo = Vec::with_capacity(100);

0 commit comments

Comments
 (0)