Skip to content

Commit 2439fa7

Browse files
committed
Year 2022 speed and code quality improvements
1 parent a8f4b25 commit 2439fa7

File tree

5 files changed

+101
-101
lines changed

5 files changed

+101
-101
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,11 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
152152
| 11 | [Monkey in the Middle](https://adventofcode.com/2022/day/11) | [Source](src/year2022/day11.rs) | 1173 |
153153
| 12 | [Hill Climbing Algorithm](https://adventofcode.com/2022/day/12) | [Source](src/year2022/day12.rs) | 57 |
154154
| 13 | [Distress Signal](https://adventofcode.com/2022/day/13) | [Source](src/year2022/day13.rs) | 15 |
155-
| 14 | [Regolith Reservoir](https://adventofcode.com/2022/day/14) | [Source](src/year2022/day14.rs) | 205 |
155+
| 14 | [Regolith Reservoir](https://adventofcode.com/2022/day/14) | [Source](src/year2022/day14.rs) | 146 |
156156
| 15 | [Beacon Exclusion Zone](https://adventofcode.com/2022/day/15) | [Source](src/year2022/day15.rs) | 2 |
157157
| 16 | [Proboscidea Volcanium](https://adventofcode.com/2022/day/16) | [Source](src/year2022/day16.rs) | 59 |
158158
| 17 | [Pyroclastic Flow](https://adventofcode.com/2022/day/17) | [Source](src/year2022/day17.rs) | 71 |
159-
| 18 | [Boiling Boulders](https://adventofcode.com/2022/day/18) | [Source](src/year2022/day18.rs) | 121 |
159+
| 18 | [Boiling Boulders](https://adventofcode.com/2022/day/18) | [Source](src/year2022/day18.rs) | 52 |
160160
| 19 | [Not Enough Minerals](https://adventofcode.com/2022/day/19) | [Source](src/year2022/day19.rs) | 74 |
161161
| 20 | [Grove Positioning System](https://adventofcode.com/2022/day/20) | [Source](src/year2022/day20.rs) | 3785 |
162162
| 21 | [Monkey Math](https://adventofcode.com/2022/day/21) | [Source](src/year2022/day21.rs) | 64 |

src/year2022/day09.rs

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,26 @@ use crate::util::parse::*;
77
use crate::util::point::*;
88

99
type Pair = (Point, i32);
10-
type Input = ([i32; 4], Vec<Pair>);
10+
type Input = (i32, i32, i32, i32, Vec<Pair>);
1111

1212
/// Converts input lines into a pair of [`Point`] and integer amount, to indicate direction and
1313
/// magnitude respectively. Then determines the maximum extent of the head so that we can allocate
1414
/// a two dimensional grid.
1515
pub fn parse(input: &str) -> Input {
1616
let first = input.bytes().filter(u8::is_ascii_alphabetic).map(Point::from);
1717
let second = input.iter_signed::<i32>();
18-
let pairs = first.zip(second).collect();
18+
let pairs: Vec<_> = first.zip(second).collect();
1919

2020
// Determine maximum extents
21-
let mut x1 = i32::MAX;
22-
let mut y1 = i32::MAX;
23-
let mut x2 = i32::MIN;
24-
let mut y2 = i32::MIN;
25-
let mut point = ORIGIN;
21+
let (x1, y1, x2, y2, _) = pairs.iter().fold(
22+
(i32::MAX, i32::MAX, i32::MIN, i32::MIN, ORIGIN),
23+
|(x1, y1, x2, y2, point), &(step, amount)| {
24+
let next = point + step * amount;
25+
(x1.min(next.x), y1.min(next.y), x2.max(next.x), y2.max(next.y), next)
26+
},
27+
);
2628

27-
for &(step, amount) in &pairs {
28-
point += step * amount;
29-
x1 = x1.min(point.x);
30-
y1 = y1.min(point.y);
31-
x2 = x2.max(point.x);
32-
y2 = y2.max(point.y);
33-
}
34-
35-
([x1, y1, x2, y2], pairs)
29+
(x1, y1, x2, y2, pairs)
3630
}
3731

3832
/// Simulate a rope length of 2
@@ -54,7 +48,7 @@ pub fn part2(input: &Input) -> u32 {
5448
/// Using const generics for the rope length allows the compiler to optimize the loop and speeds
5549
/// things up by about 40%.
5650
fn simulate<const N: usize>(input: &Input) -> u32 {
57-
let ([x1, y1, x2, y2], pairs) = input;
51+
let (x1, y1, x2, y2, pairs) = input;
5852
let width = x2 - x1 + 1;
5953
let height = y2 - y1 + 1;
6054
let start = Point::new(-x1, -y1);

src/year2022/day10.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ pub fn part1(input: &[i32]) -> i32 {
2626

2727
/// Returns pixels as a multi-line [`String`] so that the entire function can be integration tested.
2828
pub fn part2(input: &[i32]) -> String {
29-
let to_char = |(i, c): (usize, &i32)| {
30-
if ((i as i32) - c).abs() <= 1 { '#' } else { '.' }
31-
};
32-
let mut result = input
33-
.chunks_exact(40)
34-
.map(|row| row.iter().enumerate().map(to_char).collect())
35-
.collect::<Vec<String>>()
36-
.join("\n");
37-
result.insert(0, '\n');
29+
let mut result = String::new();
30+
31+
for row in input.chunks_exact(40) {
32+
result.push('\n');
33+
for (i, &c) in row.iter().enumerate() {
34+
result.push(if ((i as i32) - c).abs() <= 1 { '#' } else { '.' });
35+
}
36+
}
37+
3838
result
3939
}

src/year2022/day14.rs

Lines changed: 48 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
//! the whole pile.
1616
//!
1717
//! We instead simulate in `O(n)` complexity by recursively check each grain's underneath
18-
//! neighbors until we have a conclusive result then propagating that back up the call stack,
18+
//! neighbors until we have a conclusive result then propagating that back up the stack,
1919
//! for example:
2020
//!
2121
//! ```none
@@ -31,6 +31,7 @@
3131
//! * `Falling` Grains of sand that will continue to fall continously forever.
3232
//! * `Stopped` Both original rock walls and any grains of sand that have come to rest.
3333
use crate::util::parse::*;
34+
use Kind::*;
3435

3536
#[derive(Clone, Copy, PartialEq, Eq)]
3637
enum Kind {
@@ -43,89 +44,86 @@ enum Kind {
4344
pub struct Cave {
4445
width: usize,
4546
height: usize,
46-
size: usize,
4747
kind: Vec<Kind>,
48-
floor: Kind,
49-
count: u32,
50-
}
51-
52-
impl Cave {
53-
fn fall(&mut self, index: usize) -> Kind {
54-
// Check in order: center, left then right
55-
let result = self.check(index + self.width)
56-
&& self.check(index + self.width - 1)
57-
&& self.check(index + self.width + 1);
58-
59-
// If all 3 bottom neighbors are stopped then so are we.
60-
// Cache the result into the grid then propagate result back up the call stack.
61-
if result {
62-
self.count += 1;
63-
self.kind[index] = Kind::Stopped;
64-
Kind::Stopped
65-
} else {
66-
self.kind[index] = Kind::Falling;
67-
Kind::Falling
68-
}
69-
}
70-
71-
// Returns `true` if cell is stopped.
72-
fn check(&mut self, index: usize) -> bool {
73-
let kind = if index >= self.size {
74-
// If we've reached the "floor" then return that.
75-
self.floor
76-
} else if self.kind[index] == Kind::Air {
77-
// If we're unknown then recursively check our own underneath neighbors
78-
self.fall(index)
79-
} else {
80-
// Otherwise use the cached value.
81-
self.kind[index]
82-
};
83-
kind == Kind::Stopped
84-
}
8548
}
8649

8750
/// Creates a 2D grid cave exactly the maximum possible size.
8851
pub fn parse(input: &str) -> Cave {
8952
let unsigned = |line: &str| line.iter_unsigned().collect();
9053
let points: Vec<Vec<usize>> = input.lines().map(unsigned).collect();
9154
let max_y = points.iter().flat_map(|row| row.iter().skip(1).step_by(2)).max().unwrap();
55+
9256
// Floor is 2 below the bottommost wall.
9357
let height = max_y + 2;
9458
// Allow enough horizontal room to spread out.
9559
let width = 2 * height + 1;
96-
let size = width * height;
97-
let mut kind = vec![Kind::Air; size];
9860

9961
// Draw each of the walls.
62+
let mut kind = vec![Air; width * height];
63+
10064
for row in points {
10165
for window in row.windows(4).step_by(2) {
10266
if let &[x1, y1, x2, y2] = window {
103-
for x in x1.min(x2)..=x1.max(x2) {
67+
if x1 == x2 {
10468
for y in y1.min(y2)..=y1.max(y2) {
105-
kind[(width * y) + (x + height - 500)] = Kind::Stopped;
69+
kind[width * y + x1 + height - 500] = Stopped;
70+
}
71+
} else {
72+
for x in x1.min(x2)..=x1.max(x2) {
73+
kind[width * y1 + x + height - 500] = Stopped;
10674
}
10775
}
10876
}
10977
}
11078
}
11179

112-
Cave { width, height, size, kind, floor: Kind::Air, count: 0 }
80+
Cave { width, height, kind }
11381
}
11482

11583
/// If a grain of sand reaches the floor it will fall forever.
11684
pub fn part1(input: &Cave) -> u32 {
117-
simulate(input, Kind::Falling)
85+
simulate(input, Falling)
11886
}
11987

12088
/// The floor is solid rock.
12189
pub fn part2(input: &Cave) -> u32 {
122-
simulate(input, Kind::Stopped)
90+
simulate(input, Stopped)
12391
}
12492

12593
fn simulate(input: &Cave, floor: Kind) -> u32 {
126-
let mut cave = input.clone();
127-
cave.floor = floor;
94+
let Cave { width, height, mut kind } = input.clone();
95+
let mut count = 0;
96+
12897
// Height is also the x coordinate of the central starting location for grains.
129-
cave.fall(cave.height);
130-
cave.count
98+
let mut todo = Vec::with_capacity(1_000);
99+
todo.push(height);
100+
101+
'outer: while let Some(index) = todo.pop() {
102+
// Check in order: center, left then right
103+
for next in [index + width, index + width - 1, index + width + 1] {
104+
// If we've reached the "floor" then return that.
105+
let tile = if next >= kind.len() { floor } else { kind[next] };
106+
107+
match tile {
108+
// If we're unknown then check underneath neighbors first then re-check this tile.
109+
Air => {
110+
todo.push(index);
111+
todo.push(next);
112+
continue 'outer;
113+
}
114+
// Any falling tile underneath means that this tile is also falling.
115+
Falling => {
116+
kind[index] = Falling;
117+
continue 'outer;
118+
}
119+
Stopped => (),
120+
}
121+
}
122+
123+
// If all 3 tiles underneath are stopped then this tile is also stopped.
124+
kind[index] = Stopped;
125+
count += 1;
126+
}
127+
128+
count
131129
}

src/year2022/day18.rs

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
use crate::util::iter::*;
1010
use crate::util::parse::*;
1111

12-
const SIZE: usize = 22;
12+
const SIZE: usize = 24;
1313

14-
pub fn parse(input: &str) -> Vec<u32> {
14+
pub fn parse(input: &str) -> Vec<u8> {
1515
let mut cube = vec![0; SIZE * SIZE * SIZE];
1616
// Leave a 1 layer boundary around the outside for the part two flood fill
1717
// and also so that we don't have to use boundary checks when checking neighbors.
@@ -21,49 +21,57 @@ pub fn parse(input: &str) -> Vec<u32> {
2121
cube
2222
}
2323

24-
pub fn part1(input: &[u32]) -> u32 {
24+
pub fn part1(input: &[u8]) -> u32 {
2525
// The exposed surface area is the 6 faces of the cubes minus any neighbors.
2626
count(input, |x| 6 - x)
2727
}
2828

29-
pub fn part2(input: &[u32]) -> u32 {
30-
let mut cube = input.to_vec();
29+
pub fn part2(input: &[u8]) -> u32 {
3130
// "Paint" the outside of the cube with water drops.
32-
flood_fill(&mut cube, 0);
31+
// Use 8 as the nearest power of two greater than 6.
32+
let mut cube = input.to_vec();
33+
cube[0] = 8;
34+
35+
let mut todo = Vec::new();
36+
todo.push(0);
37+
38+
while let Some(index) = todo.pop() {
39+
let mut flood_fill = |next| {
40+
if next < input.len() && cube[next] == 0 {
41+
cube[next] = 8;
42+
todo.push(next);
43+
}
44+
};
45+
46+
// We may wrap around but that index will be out of bounds.
47+
flood_fill(index.wrapping_sub(1));
48+
flood_fill(index + 1);
49+
flood_fill(index.wrapping_sub(SIZE));
50+
flood_fill(index + SIZE);
51+
flood_fill(index.wrapping_sub(SIZE * SIZE));
52+
flood_fill(index + SIZE * SIZE);
53+
}
54+
3355
// Divide by 8 so that we only count water cubes.
3456
count(&cube, |x| x >> 3)
3557
}
3658

37-
fn count(cube: &[u32], adjust: fn(u32) -> u32) -> u32 {
59+
fn count(cube: &[u8], adjust: fn(u32) -> u32) -> u32 {
3860
let mut total = 0;
3961

4062
for i in 0..cube.len() {
4163
if cube[i] == 1 {
4264
// No need for boundary checks as all cubes are at least 1 away from the edge.
4365
total += adjust(
44-
cube[i - 1]
66+
(cube[i - 1]
4567
+ cube[i + 1]
4668
+ cube[i - SIZE]
4769
+ cube[i + SIZE]
4870
+ cube[i - SIZE * SIZE]
49-
+ cube[i + SIZE * SIZE],
71+
+ cube[i + SIZE * SIZE]) as u32,
5072
);
5173
}
5274
}
5375

5476
total
5577
}
56-
57-
fn flood_fill(cube: &mut [u32], i: usize) {
58-
if cube.get(i) == Some(&0) {
59-
// Use 8 as the nearest power of two greater than 6.
60-
cube[i] = 8;
61-
// We may wrap around to an opposite edge but that will also be water.
62-
flood_fill(cube, i.saturating_sub(1));
63-
flood_fill(cube, i + 1);
64-
flood_fill(cube, i.saturating_sub(SIZE));
65-
flood_fill(cube, i + SIZE);
66-
flood_fill(cube, i.saturating_sub(SIZE * SIZE));
67-
flood_fill(cube, i + SIZE * SIZE);
68-
}
69-
}

0 commit comments

Comments
 (0)