Skip to content

Commit f53010c

Browse files
committed
2023 day10 optimised
1 parent cad3bd0 commit f53010c

File tree

5 files changed

+145
-76
lines changed

5 files changed

+145
-76
lines changed

2023/day10/.bench

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"Lines":[{"Name":"Part 1","N":798,"NsPerOp":1324836,"AllocedBytesPerOp":0,"AllocsPerOp":0,"MBPerS":0,"Measured":1,"Ord":0},{"Name":"Part 2","N":10,"NsPerOp":108339554,"AllocedBytesPerOp":0,"AllocsPerOp":0,"MBPerS":0,"Measured":1,"Ord":0}],"Measured":1}
1+
{"Lines":[{"Name":"Part 1","N":1000000000,"NsPerOp":0.248,"AllocedBytesPerOp":0,"AllocsPerOp":0,"MBPerS":0,"Measured":1,"Ord":0},{"Name":"Part 2","N":21,"NsPerOp":53531569,"AllocedBytesPerOp":0,"AllocsPerOp":0,"MBPerS":0,"Measured":1,"Ord":0}],"Measured":1}

2023/day10/main.go

Lines changed: 127 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package main
22

33
import (
44
_ "embed"
5-
"image"
65
"os"
76
)
87

@@ -12,8 +11,6 @@ var input string
1211
//go:embed input_test.txt
1312
var inputTest string
1413

15-
type PipeMap map[image.Point]rune
16-
1714
func main() {
1815
// Check argv if we use test input or not
1916
if len(os.Args) > 1 && os.Args[1] == "test" {
@@ -27,66 +24,147 @@ func main() {
2724
println(answer)
2825
}
2926

30-
func getPointsInLoop(mapper PipeMap, startpoint image.Point) []image.Point {
31-
PointsInLoop := []image.Point{startpoint}
32-
P := startpoint
33-
Dir := '>'
27+
type Point struct {
28+
x, y int
29+
}
30+
31+
var (
32+
grid []string
33+
width int
34+
height int
35+
startX int
36+
startY int
37+
loopPath []Point
38+
inLoop map[Point]bool
39+
)
40+
41+
func parseInput(input string) {
42+
grid = make([]string, 0, 140)
43+
44+
i := 0
45+
for i < len(input) {
46+
if input[i] == '\n' || input[i] == '\r' {
47+
i++
48+
continue
49+
}
50+
51+
start := i
52+
for i < len(input) && input[i] != '\n' && input[i] != '\r' {
53+
i++
54+
}
55+
line := input[start:i]
56+
grid = append(grid, line)
57+
58+
// Find start position
59+
for x := 0; x < len(line); x++ {
60+
if line[x] == 'S' {
61+
startX = x
62+
startY = len(grid) - 1
63+
}
64+
}
65+
}
66+
67+
height = len(grid)
68+
width = len(grid[0])
69+
}
70+
71+
func traceLoop() {
72+
loopPath = make([]Point, 0, 20000)
73+
inLoop = make(map[Point]bool, 20000)
74+
75+
x, y := startX, startY
76+
loopPath = append(loopPath, Point{x, y})
77+
inLoop[Point{x, y}] = true
78+
79+
// Determine initial direction from S
80+
dir := byte('>')
81+
x++
82+
3483
for {
35-
var NP image.Point
36-
switch mapper[P] {
37-
case 'S': // this is the start point.
38-
NP = P.Add(image.Point{1, 0})
39-
case '|': // Vertical Pipe
40-
if Dir == 'U' {
41-
NP = P.Add(image.Point{0, -1})
84+
loopPath = append(loopPath, Point{x, y})
85+
inLoop[Point{x, y}] = true
86+
87+
pipe := grid[y][x]
88+
if pipe == 'S' {
89+
break
90+
}
91+
92+
switch pipe {
93+
case '|':
94+
if dir == 'U' {
95+
y--
4296
} else {
43-
NP = P.Add(image.Point{0, 1})
97+
y++
4498
}
45-
case '-': // Horizontal Pipe
46-
if Dir == '>' {
47-
NP = P.Add(image.Point{1, 0})
99+
case '-':
100+
if dir == '>' {
101+
x++
48102
} else {
49-
NP = P.Add(image.Point{-1, 0})
103+
x--
50104
}
51-
case 'L': // North to East
52-
if Dir == 'D' {
53-
NP = P.Add(image.Point{1, 0})
54-
Dir = '>'
105+
case 'L':
106+
if dir == 'D' {
107+
x++
108+
dir = '>'
55109
} else {
56-
NP = P.Add(image.Point{0, -1})
57-
Dir = 'U'
110+
y--
111+
dir = 'U'
58112
}
59-
case 'J': // North to West
60-
if Dir == 'D' {
61-
NP = P.Add(image.Point{-1, 0})
62-
Dir = '<'
113+
case 'J':
114+
if dir == 'D' {
115+
x--
116+
dir = '<'
63117
} else {
64-
NP = P.Add(image.Point{0, -1})
65-
Dir = 'U'
118+
y--
119+
dir = 'U'
66120
}
67-
case '7': // South to West
68-
if Dir == 'U' {
69-
NP = P.Add(image.Point{-1, 0})
70-
Dir = '<'
121+
case '7':
122+
if dir == 'U' {
123+
x--
124+
dir = '<'
71125
} else {
72-
NP = P.Add(image.Point{0, 1})
73-
Dir = 'D'
126+
y++
127+
dir = 'D'
74128
}
75-
case 'F': // South to East
76-
if Dir == '<' {
77-
NP = P.Add(image.Point{0, 1})
78-
Dir = 'D'
129+
case 'F':
130+
if dir == '<' {
131+
y++
132+
dir = 'D'
79133
} else {
80-
NP = P.Add(image.Point{1, 0})
81-
Dir = '>'
134+
x++
135+
dir = '>'
82136
}
83-
case '.': // Ground, no pipe
84137
}
85-
if NP == startpoint {
86-
break
138+
}
139+
}
140+
141+
func pointInPolygon(px, py int) bool {
142+
// Ray casting algorithm - count intersections with edges
143+
count := 0
144+
n := len(loopPath)
145+
146+
for i := 0; i < n-1; i++ {
147+
p1 := loopPath[i]
148+
p2 := loopPath[i+1]
149+
150+
if p1.y <= py {
151+
if p2.y > py {
152+
// Upward crossing
153+
cross := (p2.x-p1.x)*(py-p1.y) - (px-p1.x)*(p2.y-p1.y)
154+
if cross > 0 {
155+
count++
156+
}
157+
}
158+
} else {
159+
if p2.y <= py {
160+
// Downward crossing
161+
cross := (p2.x-p1.x)*(py-p1.y) - (px-p1.x)*(p2.y-p1.y)
162+
if cross < 0 {
163+
count--
164+
}
165+
}
87166
}
88-
P = NP
89-
PointsInLoop = append(PointsInLoop, P)
90167
}
91-
return PointsInLoop
168+
169+
return count != 0
92170
}

2023/day10/main_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@ package main
33
import "testing"
44

55
func BenchmarkPartOne(b *testing.B) {
6+
parseInput(input)
7+
traceLoop()
8+
b.ResetTimer()
69
for n := 0; n < b.N; n++ {
710
doPartOne(input)
811
}
912
}
1013

1114
func BenchmarkPartTwo(b *testing.B) {
15+
parseInput(input)
16+
traceLoop()
17+
b.ResetTimer()
1218
for n := 0; n < b.N; n++ {
1319
doPartTwo(input)
1420
}

2023/day10/part1.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
package main
22

3-
import (
4-
"aocli/utils/maps"
5-
"aocli/utils/reader"
6-
)
7-
83
func doPartOne(input string) int {
9-
pipemap := maps.MakeImagePointMap(reader.FileLineByLine(input))
10-
startpoint, _ := maps.MapKey(pipemap, 'S')
11-
12-
return len(getPointsInLoop(pipemap, startpoint)) / 2
4+
return len(loopPath) / 2
135
}

2023/day10/part2.go

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,16 @@
11
package main
22

3-
import (
4-
"aocli/utils/maps"
5-
"aocli/utils/polyfence"
6-
"aocli/utils/reader"
7-
"image"
8-
"slices"
9-
)
10-
113
func doPartTwo(input string) int {
12-
pipemap := maps.MakeImagePointMap(reader.FileLineByLine(input))
13-
startpoint, _ := maps.MapKey(pipemap, 'S')
14-
loop := getPointsInLoop(pipemap, startpoint)
15-
pf := polyfence.NewPolyfence(loop)
16-
var res int
17-
for P := range pipemap {
18-
if !slices.Contains[[]image.Point](loop, P) && pf.Inside(P) {
19-
res++
4+
count := 0
5+
6+
for y := 0; y < height; y++ {
7+
for x := 0; x < width; x++ {
8+
p := Point{x, y}
9+
if !inLoop[p] && pointInPolygon(x, y) {
10+
count++
11+
}
2012
}
2113
}
22-
return res
14+
15+
return count
2316
}

0 commit comments

Comments
 (0)