Skip to content

Commit cbfba0d

Browse files
committed
19: 10x speedup by reducing possible states
1 parent e4272e0 commit cbfba0d

File tree

3 files changed

+66
-63
lines changed

3 files changed

+66
-63
lines changed

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# AdventOfCode2022 :christmas_tree:
2-
Solutions to all 25 AoC 2022 in Rust :crab: Using less than 100 lines per day, and with a total runtime of `0.9` second.
2+
Solutions to all 25 AoC 2022 in Rust :crab: Using less than 100 lines per day, and with a total runtime of `0.82` seconds.
33

44
I really liked 2022, it was a good AoC year. It was the year I finally reached my goal of hitting the top 100, 25th place on [day 19](./src/bin/19.rs)!
55

Diff for: src/bin/19.rs

+63-60
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,89 @@
1-
use std::collections::VecDeque;
21
use itertools::Itertools;
32
use hashbrown::HashSet;
43
use rayon::prelude::*;
54

65
#[derive(Default, Clone, Copy, Hash, PartialEq, Eq)]
76
struct State {
8-
ore: u32,
9-
ore_robots: u32,
10-
clay: u32,
11-
clay_robots: u32,
12-
obsidian: u32,
13-
obsidian_robots: u32,
14-
geode: u32,
15-
geode_robots: u32,
7+
ore: u16,
8+
ore_robots: u16,
9+
clay: u16,
10+
clay_robots: u16,
11+
obsidian: u16,
12+
obsidian_robots: u16,
13+
geode: u16,
14+
geode_robots: u16,
1615
}
1716

18-
fn earn(mut state: State) -> State {
19-
state.ore += state.ore_robots;
20-
state.clay += state.clay_robots;
21-
state.obsidian += state.obsidian_robots;
22-
state.geode += state.geode_robots;
23-
state
17+
fn earn(state: State, max_ore_cost: u16, max_clay_cost: u16, max_obs_cost: u16) -> State {
18+
State {
19+
ore: (state.ore + state.ore_robots).min(3 * max_ore_cost),
20+
clay: (state.clay + state.clay_robots).min(3 * max_clay_cost),
21+
obsidian: (state.obsidian + state.obsidian_robots).min(3 * max_obs_cost),
22+
geode: state.geode + state.geode_robots,
23+
..state
24+
}
2425
}
2526

26-
fn max_geodes((ore_ore_cost, clay_ore_cost, obs_ore_cost, obs_clay_cost, geo_ore_cost, geo_obs_cost): (u32,u32,u32,u32,u32,u32), time: u32) -> u32 {
27+
fn max_geodes((ore_ore_cost, clay_ore_cost, obs_ore_cost, obs_clay_cost, geo_ore_cost, geo_obs_cost): (u16,u16,u16,u16,u16,u16), time: u16) -> u16 {
2728
let max_ore_cost = *[ore_ore_cost, clay_ore_cost, obs_ore_cost, geo_ore_cost].iter().max().unwrap();
28-
let (mut seen, mut q) = (HashSet::with_capacity(100000), VecDeque::with_capacity(100000));
29-
q.push_back((0, State { ore_robots: 1, ..State::default() }));
30-
let mut ans = 0;
31-
while let Some((len, state)) = q.pop_front() {
32-
if len >= time {
33-
ans = std::cmp::max(ans, state.geode);
34-
continue;
35-
}
36-
if seen.contains(&state) {
37-
continue;
38-
}
39-
seen.insert(state);
40-
41-
if state.ore >= geo_ore_cost && state.obsidian >= geo_obs_cost {
42-
let mut state = earn(state);
43-
state.ore -= geo_ore_cost;
44-
state.obsidian -= geo_obs_cost;
45-
state.geode_robots += 1;
46-
q.push_back((len+1, state));
47-
continue;
48-
}
49-
if state.obsidian_robots < geo_obs_cost && state.ore >= obs_ore_cost && state.clay >= obs_clay_cost {
50-
let mut state = earn(state);
51-
state.ore -= obs_ore_cost;
52-
state.clay -= obs_clay_cost;
53-
state.obsidian_robots += 1;
54-
q.push_back((len+1, state));
55-
continue;
56-
}
57-
if state.ore_robots < max_ore_cost && state.ore >= ore_ore_cost {
58-
let mut state = earn(state);
59-
state.ore -= ore_ore_cost;
60-
state.ore_robots += 1;
61-
q.push_back((len+1, state));
62-
}
63-
if state.clay_robots < obs_clay_cost && state.ore >= clay_ore_cost {
64-
let mut state = earn(state);
65-
state.ore -= clay_ore_cost;
66-
state.clay_robots += 1;
67-
q.push_back((len+1, state));
29+
let mut seen = HashSet::with_capacity(100000);
30+
let mut states = vec![State { ore_robots: 1, ..State::default() }];
31+
let mut best = 0;
32+
for t in 0..time {
33+
let mut new_states = vec![];
34+
for &state in &states {
35+
if state.geode + state.geode_robots * 2 * (time - 1 - t) < best {
36+
continue;
37+
}
38+
if !seen.insert(state) {
39+
continue;
40+
}
41+
best = best.max(state.geode);
42+
if state.ore >= geo_ore_cost && state.obsidian >= geo_obs_cost {
43+
let mut state = earn(state, max_ore_cost, obs_clay_cost, geo_obs_cost);
44+
state.ore -= geo_ore_cost;
45+
state.obsidian -= geo_obs_cost;
46+
state.geode_robots += 1;
47+
new_states.push(state);
48+
continue;
49+
}
50+
if state.obsidian_robots < geo_obs_cost && state.ore >= obs_ore_cost && state.clay >= obs_clay_cost {
51+
let mut state = earn(state, max_ore_cost, obs_clay_cost, geo_obs_cost);
52+
state.ore -= obs_ore_cost;
53+
state.clay -= obs_clay_cost;
54+
state.obsidian_robots += 1;
55+
new_states.push(state);
56+
continue;
57+
}
58+
if state.ore_robots < max_ore_cost && state.ore >= ore_ore_cost {
59+
let mut state = earn(state, max_ore_cost, obs_clay_cost, geo_obs_cost);
60+
state.ore -= ore_ore_cost;
61+
state.ore_robots += 1;
62+
new_states.push(state);
63+
}
64+
if state.clay_robots < obs_clay_cost && state.ore >= clay_ore_cost {
65+
let mut state = earn(state, max_ore_cost, obs_clay_cost, geo_obs_cost);
66+
state.ore -= clay_ore_cost;
67+
state.clay_robots += 1;
68+
new_states.push(state);
69+
}
70+
new_states.push(earn(state, max_ore_cost, obs_clay_cost, geo_obs_cost));
6871
}
69-
q.push_back((len+1, earn(state)));
72+
states = new_states;
7073
}
71-
ans
74+
states.iter().map(|state| state.geode).max().unwrap()
7275
}
7376

7477
#[aoc::main(19)]
75-
fn main(input: &str) -> (u32, u32) {
78+
fn main(input: &str) -> (u16, u16) {
7679
let blueprints = input.lines().filter_map(|l|
7780
l.split(|c: char| !c.is_ascii_digit())
7881
.filter(|w| !w.is_empty())
7982
.skip(1)
8083
.map(|w| w.parse().unwrap())
8184
.collect_tuple()
8285
).collect::<Vec<_>>();
83-
let p1 = blueprints.par_iter().enumerate().map(|(i,&b)| max_geodes(b, 24) * (i as u32 + 1)).sum();
86+
let p1 = blueprints.par_iter().enumerate().map(|(i,&b)| max_geodes(b, 24) * (i + 1) as u16).sum();
8487
let p2 = blueprints.par_iter().take(3).map(|&b| max_geodes(b, 32)).product();
8588
(p1,p2)
8689
}

Diff for: timings.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ Measured on an `Intel(R) Core(TM) i5-6600K CPU @ 3.50GHz`.
2121
| 16 | 292.030 |
2222
| 17 | 17.897 |
2323
| 18 | 1.446 |
24-
| 19 | 81.065 |
24+
| 19 | 7.933 |
2525
| 20 | 73.857 |
2626
| 21 | 0.570 |
2727
| 22 | 0.297 |
2828
| 23 | 218.210 |
2929
| 24 | 68.402 |
3030
| 25 | 0.017 |
31-
| **Total** | 892.153 |
31+
| **Total** | 819.021 |

0 commit comments

Comments
 (0)