-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
125 lines (97 loc) · 2.1 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package main
import (
"github.com/danvolchek/AdventOfCode/lib"
"strings"
)
type Coord struct {
x, y int
}
func parse(line string) []Coord {
parts := strings.Split(line, " -> ")
coords := lib.Map(parts, func(part string) Coord {
ints := lib.Ints(part)
return Coord{
x: ints[0],
y: ints[1],
}
})
return coords
}
type Cave struct {
blocked lib.Set[Coord]
minX, maxX, maxY int
sandPoured int
}
func (c *Cave) Construct(paths [][]Coord) {
for i, path := range paths {
curr := path[0]
for j, next := range path[1:] {
// record blocked positions
minX, maxX := lib.Min(curr.x, next.x), lib.Max(curr.x, next.x)
minY, maxY := lib.Min(curr.y, next.y), lib.Max(curr.y, next.y)
for x := minX; x <= maxX; x++ {
for y := minY; y <= maxY; y++ {
c.blocked.Add(Coord{x: x, y: y})
}
}
curr = next
// store cave bounds to determine if fallen into abyss
if i == 0 && j == 0 {
c.minX = minX
}
c.minX = lib.Min(c.minX, minX)
c.maxX = lib.Max(c.maxX, maxX)
c.maxY = lib.Max(c.maxY, maxY)
}
}
}
// PourUnitOfSand drops one sand into the cave and returns whether
// the sand falls into the abyss.
func (c *Cave) PourUnitOfSand() bool {
pos := Coord{x: 500, y: 0}
canMove := func(newPos Coord) bool {
if !c.blocked.Contains(newPos) {
pos = newPos
return true
}
return false
}
for {
// fallen into abyss
if pos.x < c.minX || pos.x > c.maxX || pos.y > c.maxY {
return true
}
// down
if canMove(Coord{x: pos.x, y: pos.y + 1}) {
continue
}
// down left
if canMove(Coord{x: pos.x - 1, y: pos.y + 1}) {
continue
}
// down right
if canMove(Coord{x: pos.x + 1, y: pos.y + 1}) {
continue
}
break
}
// landed on a spot
c.blocked.Add(pos)
c.sandPoured += 1
return false
}
func solve(paths [][]Coord) int {
cave := &Cave{}
cave.Construct(paths)
for !cave.PourUnitOfSand() {
}
return cave.sandPoured
}
func main() {
solver := lib.Solver[[][]Coord, int]{
ParseF: lib.ParseLine(parse),
SolveF: solve,
}
solver.Expect("498,4 -> 498,6 -> 496,6\n503,4 -> 502,4 -> 502,9 -> 494,9\n", 24)
solver.Verify(1001)
}