Skip to content

Commit 7ffce2a

Browse files
committed
19th day
1 parent 6158384 commit 7ffce2a

File tree

3 files changed

+263
-0
lines changed

3 files changed

+263
-0
lines changed

day19/day19.go

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
"strings"
7+
8+
. "majcn.si/advent-of-code-2022/util"
9+
)
10+
11+
type Blueprint struct {
12+
ID int
13+
OreRobotCostOre int
14+
ClayRobotCostOre int
15+
ObsidianRobotCostOre int
16+
ObsidianRobotCostClay int
17+
GeodeRobotCostOre int
18+
GeodeRobotCostObsidian int
19+
MaxOreCost int
20+
MaxClayCost int
21+
MaxObsidianCost int
22+
}
23+
24+
type DataType []Blueprint
25+
26+
func parseData(data string) DataType {
27+
dataSplit := strings.Split(data, "\n")
28+
29+
regexString := `^Blueprint (\d+): `
30+
regexString += `Each ore robot costs (\d+) ore. `
31+
regexString += `Each clay robot costs (\d+) ore. `
32+
regexString += `Each obsidian robot costs (\d+) ore and (\d+) clay. `
33+
regexString += `Each geode robot costs (\d+) ore and (\d+) obsidian.$`
34+
r := regexp.MustCompile(regexString)
35+
36+
result := make([]Blueprint, len(dataSplit))
37+
for i, line := range dataSplit {
38+
match := r.FindStringSubmatch(line)
39+
result[i] = Blueprint{
40+
ID: ParseInt(match[1]),
41+
OreRobotCostOre: ParseInt(match[2]),
42+
ClayRobotCostOre: ParseInt(match[3]),
43+
ObsidianRobotCostOre: ParseInt(match[4]),
44+
ObsidianRobotCostClay: ParseInt(match[5]),
45+
GeodeRobotCostOre: ParseInt(match[6]),
46+
GeodeRobotCostObsidian: ParseInt(match[7]),
47+
}
48+
result[i].MaxOreCost = Max(
49+
result[i].OreRobotCostOre,
50+
result[i].ClayRobotCostOre,
51+
result[i].ObsidianRobotCostOre,
52+
result[i].GeodeRobotCostOre,
53+
)
54+
result[i].MaxClayCost = result[i].ObsidianRobotCostClay
55+
result[i].MaxObsidianCost = result[i].GeodeRobotCostObsidian
56+
}
57+
58+
return result
59+
}
60+
61+
type State struct {
62+
Time int
63+
Ore int
64+
Clay int
65+
Obsidian int
66+
Geode int
67+
OreRobots int
68+
ClayRobots int
69+
ObsidianRobots int
70+
GeodeRobots int
71+
}
72+
73+
func getNextStates(blueprint Blueprint, state State) []State {
74+
if state.Ore >= blueprint.GeodeRobotCostOre && state.Obsidian >= blueprint.GeodeRobotCostObsidian {
75+
return []State{{
76+
Time: state.Time + 1,
77+
Ore: state.Ore + state.OreRobots - blueprint.GeodeRobotCostOre,
78+
Clay: state.Clay + state.ClayRobots,
79+
Obsidian: state.Obsidian + state.ObsidianRobots - blueprint.GeodeRobotCostObsidian,
80+
Geode: state.Geode + state.GeodeRobots,
81+
OreRobots: state.OreRobots,
82+
ClayRobots: state.ClayRobots,
83+
ObsidianRobots: state.ObsidianRobots,
84+
GeodeRobots: state.GeodeRobots + 1,
85+
}}
86+
}
87+
88+
if state.ObsidianRobots < blueprint.MaxObsidianCost && state.Ore >= blueprint.ObsidianRobotCostOre && state.Clay >= blueprint.ObsidianRobotCostClay {
89+
return []State{{
90+
Time: state.Time + 1,
91+
Ore: state.Ore + state.OreRobots - blueprint.ObsidianRobotCostOre,
92+
Clay: state.Clay + state.ClayRobots - blueprint.ObsidianRobotCostClay,
93+
Obsidian: state.Obsidian + state.ObsidianRobots,
94+
Geode: state.Geode + state.GeodeRobots,
95+
OreRobots: state.OreRobots,
96+
ClayRobots: state.ClayRobots,
97+
ObsidianRobots: state.ObsidianRobots + 1,
98+
GeodeRobots: state.GeodeRobots,
99+
}}
100+
}
101+
102+
result := make([]State, 0, 3)
103+
104+
if state.ClayRobots < blueprint.MaxClayCost && state.Ore >= blueprint.ClayRobotCostOre {
105+
result = append(result, State{
106+
Time: state.Time + 1,
107+
Ore: state.Ore + state.OreRobots - blueprint.ClayRobotCostOre,
108+
Clay: state.Clay + state.ClayRobots,
109+
Obsidian: state.Obsidian + state.ObsidianRobots,
110+
Geode: state.Geode + state.GeodeRobots,
111+
OreRobots: state.OreRobots,
112+
ClayRobots: state.ClayRobots + 1,
113+
ObsidianRobots: state.ObsidianRobots,
114+
GeodeRobots: state.GeodeRobots,
115+
})
116+
}
117+
118+
if state.OreRobots < blueprint.MaxOreCost && state.Ore >= blueprint.OreRobotCostOre {
119+
result = append(result, State{
120+
Time: state.Time + 1,
121+
Ore: state.Ore + state.OreRobots - blueprint.OreRobotCostOre,
122+
Clay: state.Clay + state.ClayRobots,
123+
Obsidian: state.Obsidian + state.ObsidianRobots,
124+
Geode: state.Geode + state.GeodeRobots,
125+
OreRobots: state.OreRobots + 1,
126+
ClayRobots: state.ClayRobots,
127+
ObsidianRobots: state.ObsidianRobots,
128+
GeodeRobots: state.GeodeRobots,
129+
})
130+
}
131+
132+
result = append(result, State{
133+
Time: state.Time + 1,
134+
Ore: state.Ore + state.OreRobots,
135+
Clay: state.Clay + state.ClayRobots,
136+
Obsidian: state.Obsidian + state.ObsidianRobots,
137+
Geode: state.Geode + state.GeodeRobots,
138+
OreRobots: state.OreRobots,
139+
ClayRobots: state.ClayRobots,
140+
ObsidianRobots: state.ObsidianRobots,
141+
GeodeRobots: state.GeodeRobots,
142+
})
143+
144+
return result
145+
}
146+
147+
func canBeDiscarded(state State, bestResult State, blueprint Blueprint, maxTime int) bool {
148+
for i := state.Time + 1; i <= maxTime; i++ {
149+
state.Clay += state.ClayRobots
150+
state.ClayRobots++
151+
}
152+
153+
for i := state.Time + 1; i <= maxTime; i++ {
154+
state.Obsidian += state.ObsidianRobots
155+
state.Clay -= blueprint.ObsidianRobotCostClay
156+
if state.Clay >= 0 {
157+
state.ObsidianRobots++
158+
}
159+
}
160+
161+
for i := state.Time + 1; i <= maxTime; i++ {
162+
state.Geode += state.GeodeRobots
163+
state.Obsidian -= blueprint.GeodeRobotCostObsidian
164+
if state.Obsidian >= 0 {
165+
state.GeodeRobots++
166+
}
167+
}
168+
169+
return state.Geode <= bestResult.Geode
170+
}
171+
172+
func dfs(initState State, blueprint Blueprint, maxTime int, nextStatesF func(blueprint Blueprint, state State) []State) State {
173+
bestResult := State{}
174+
discovered := make(Set[State])
175+
176+
var dfsInner func(State)
177+
dfsInner = func(state State) {
178+
discovered.Add(state)
179+
180+
for _, n := range nextStatesF(blueprint, state) {
181+
if discovered.Contains(n) {
182+
continue
183+
}
184+
185+
if n.Time == maxTime {
186+
if bestResult.Geode < n.Geode {
187+
bestResult = n
188+
}
189+
continue
190+
}
191+
192+
if canBeDiscarded(n, bestResult, blueprint, maxTime) {
193+
continue
194+
}
195+
196+
dfsInner(n)
197+
}
198+
}
199+
200+
dfsInner(initState)
201+
202+
return bestResult
203+
}
204+
205+
func findMaxGeodes(blueprint Blueprint, maxTime int) int {
206+
return dfs(State{OreRobots: 1}, blueprint, maxTime, getNextStates).Geode
207+
}
208+
209+
func solvePart1(data DataType) (rc int) {
210+
findMaxGeodes(data[3], 24)
211+
for _, blueprint := range data {
212+
rc += blueprint.ID * findMaxGeodes(blueprint, 24)
213+
}
214+
return
215+
}
216+
217+
func solvePart2(data DataType) (rc int) {
218+
rc = 1
219+
for _, blueprint := range data[:3] {
220+
rc *= findMaxGeodes(blueprint, 32)
221+
}
222+
return
223+
}
224+
225+
func main() {
226+
data := parseData(FetchInputData(19))
227+
fmt.Println(solvePart1(data))
228+
fmt.Println(solvePart2(data))
229+
}

day19/day19_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
. "majcn.si/advent-of-code-2022/util"
8+
)
9+
10+
func ExpectedPart1() int {
11+
return 23
12+
}
13+
14+
func ExpectedPart2() int {
15+
return 29348
16+
}
17+
18+
var data DataType
19+
20+
func init() {
21+
input, _ := os.ReadFile("../examples/19.txt")
22+
data = parseData(string(input))
23+
}
24+
25+
func TestPart1(t *testing.T) {
26+
AssertTestPartX(t, ExpectedPart1(), solvePart1(data))
27+
}
28+
29+
func TestPart2(t *testing.T) {
30+
AssertTestPartX(t, ExpectedPart2(), solvePart2(data))
31+
}

examples/19.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Blueprint 1: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 19 clay. Each geode robot costs 2 ore and 12 obsidian.
2+
Blueprint 2: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 19 clay. Each geode robot costs 2 ore and 9 obsidian.
3+
Blueprint 3: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 17 clay. Each geode robot costs 3 ore and 10 obsidian.

0 commit comments

Comments
 (0)