Skip to content

Commit e63275d

Browse files
committed
Merge branch 'day2022/19'
2 parents 9b273e8 + 4ae802c commit e63275d

File tree

1 file changed

+46
-33
lines changed

1 file changed

+46
-33
lines changed

2022/Day19/Solution.cs

Lines changed: 46 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,19 @@ public object PartTwo(string input) {
2828

2929
// Priority queue based maximum search with LOTS OF PRUNING
3030
private int MaxGeodes(Blueprint blueprint, int timeLimit) {
31-
var q = new PriorityQueue<(State state, Robot[] ignore), int>();
31+
var q = new PriorityQueue<State, int>();
3232
var seen = new HashSet<State>();
3333

34-
enqueue(new State(remainingTime: timeLimit, available: Nothing, producing: Ore));
34+
enqueue(new State(
35+
remainingTime: timeLimit,
36+
available: Nothing,
37+
producing: Ore,
38+
dontBuild: 0
39+
));
3540

3641
var max = 0;
3742
while (q.Count > 0) {
38-
var (state, ignore) = q.Dequeue();
43+
var state = q.Dequeue();
3944

4045
// Queue is ordered by potentialGeodeCount, there is
4146
// no point in investigating the remaining items.
@@ -50,34 +55,34 @@ private int MaxGeodes(Blueprint blueprint, int timeLimit) {
5055
// time is off, just update max
5156
max = Math.Max(max, state.available.geode);
5257
} else {
53-
54-
// what robots can be created from our available materials?
58+
// What robots can be created from the available materials?
5559
var buildableRobots = blueprint.robots
5660
.Where(robot => state.available >= robot.cost)
5761
.ToArray();
5862

59-
// 1) wait until next round for potentialy more robot types
60-
enqueue(
61-
state with {
62-
remainingTime = state.remainingTime - 1,
63-
available = state.available + state.producing,
64-
},
65-
// if we have materials for some robot, there is no point
66-
// in building it only in the next round let's ignore these
67-
ignore: buildableRobots
68-
);
69-
70-
// 2) or build some robots right away
63+
// 1) build one of them right away
7164
foreach (var robot in buildableRobots) {
72-
73-
if (!ignore.Contains(robot) && worthBuilding(state, robot)) {
65+
if (worthBuilding(state, robot)) {
7466
enqueue(state with {
7567
remainingTime = state.remainingTime - 1,
7668
available = state.available + state.producing - robot.cost,
7769
producing = state.producing + robot.producing,
70+
dontBuild = 0
7871
});
7972
}
8073
}
74+
75+
// 2) or wait until next round for more robot types. Don't postpone
76+
// building of robots which are already available. This is a very
77+
// very important prunning step. It's about 25 times faster if we
78+
// do it this way.
79+
enqueue(
80+
state with {
81+
remainingTime = state.remainingTime - 1,
82+
available = state.available + state.producing,
83+
dontBuild = buildableRobots.Select(robot => robot.id).Sum(),
84+
}
85+
);
8186
}
8287
}
8388
}
@@ -86,35 +91,43 @@ state with {
8691

8792
// -------
8893

89-
// Upper limit for the maximum geodes we can mine starting from this state.
90-
// Let's be optimistic and suppose that in each and every step we will be able to build a new geode robot...
94+
// Upper limit for the maximum geodes we reach when starting from this state.
95+
// Let's be optimistic and suppose that in each step we will be able to build
96+
// a new geode robot...
9197
int potentialGeodeCount(State state) {
92-
var future = (state.producing.geode + state.producing.geode + state.remainingTime) * state.remainingTime / 2;
98+
// sum of [state.producing.geode .. state.producing.geode + state.remainingTime - 1]
99+
var future = (2 * state.producing.geode + state.remainingTime - 1) * state.remainingTime / 2;
93100
return state.available.geode + future;
94101
}
95102

96-
// We can build just a single robot in a round. This gives as a prunning condition.
97-
// Producing more material in a round that we can spend on building a new robot is worthless.
98103
bool worthBuilding(State state, Robot robot) {
104+
// We can explicitly ignore building some robots.
105+
// Robot ids are powers of 2 used as flags in the dontBuild integer.
106+
if ((state.dontBuild & robot.id) != 0) {
107+
return false;
108+
}
109+
110+
// Our factory can build just a single robot in a round. This gives as
111+
// a prunning condition. Producing more material in a round that we can
112+
// spend on building a new robot is worthless.
99113
return state.producing + robot.producing <= blueprint.maxCost;
100114
}
101115

102116
// Just add an item to the search queue, use -potentialGeodeCount as priority
103-
void enqueue(State state, Robot[] ignore = null) {
104-
q.Enqueue((state, ignore ?? new Robot[0]), -potentialGeodeCount(state));
117+
void enqueue(State state) {
118+
q.Enqueue(state, -potentialGeodeCount(state));
105119
}
106-
107120
}
108121

109122
IEnumerable<Blueprint> Parse(string input) {
110123
foreach (var line in input.Split("\n")) {
111124
var numbers = Regex.Matches(line, @"(\d+)").Select(x => int.Parse(x.Value)).ToArray();
112125
yield return new Blueprint(
113126
id: numbers[0],
114-
new Robot(producing: Ore, cost: numbers[1] * Ore),
115-
new Robot(producing: Clay, cost: numbers[2] * Ore),
116-
new Robot(producing: Obsidian, cost: numbers[3] * Ore + numbers[4] * Clay),
117-
new Robot(producing: Geode, cost: numbers[5] * Ore + numbers[6] * Obsidian)
127+
new Robot(id: 1, producing: Ore, cost: numbers[1] * Ore),
128+
new Robot(id: 2, producing: Clay, cost: numbers[2] * Ore),
129+
new Robot(id: 4, producing: Obsidian, cost: numbers[3] * Ore + numbers[4] * Clay),
130+
new Robot(id: 8, producing: Geode, cost: numbers[5] * Ore + numbers[6] * Obsidian)
118131
);
119132
}
120133
}
@@ -164,8 +177,8 @@ record Material(int ore, int clay, int obsidian, int geode) {
164177
}
165178
}
166179

167-
record Robot(Material cost, Material producing);
168-
record State(int remainingTime, Material available, Material producing);
180+
record Robot(int id, Material cost, Material producing);
181+
record State(int remainingTime, Material available, Material producing, int dontBuild);
169182
record Blueprint(int id, params Robot[] robots) {
170183
public Material maxCost = new Material(
171184
ore: robots.Select(robot => robot.cost.ore).Max(),

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy