advent-of-code/day18/main.go
xuu bcd90a57f3
All checks were successful
Go Test / build (pull_request) Successful in 57s
chore: use shoelace and picks theorem
2023-12-27 13:46:42 -07:00

130 lines
2.4 KiB
Go

package main
import (
"bufio"
_ "embed"
"fmt"
"strconv"
"strings"
aoc "go.sour.is/advent-of-code"
"golang.org/x/exp/maps"
)
// var log = aoc.Log
func main() { aoc.MustResult(aoc.Runner(run)) }
type result struct {
valuePT1 int
valuePT2 int
}
func (r result) String() string { return fmt.Sprintf("%#v", r) }
func run(scan *bufio.Scanner) (*result, error) {
var vecsPT1 []vector
var vecsPT2 []vector
for scan.Scan() {
text := scan.Text()
if len(text) == 0 {
continue
}
v, color := fromLine(text)
vecsPT1 = append(vecsPT1, v)
vecsPT2 = append(vecsPT2, fromColor(color))
}
return &result{
valuePT1: findArea(vecsPT1),
valuePT2: findArea(vecsPT2),
}, nil
}
type vector struct {
offset point
scale int
}
type point [2]int
func (p point) add(a point) point {
return point{p[0] + a[0], p[1] + a[1]}
}
func (p point) scale(m int) point {
return point{p[0] * m, p[1] * m}
}
// numPoints the number of the points inside an outline plus the number of points in the outline
func numPoints(outline []point, borderLength int) int {
// shoelace - find the float area in a shape
sum := 0
for _, p := range pairwise(outline) {
row1, col1 := p[0][0], p[0][1]
row2, col2 := p[1][0], p[1][1]
sum += row1*col2 - row2*col1
}
area := sum / 2
// pick's theorem - find the number of points in a shape given its area
return (aoc.ABS(area) - borderLength/2 + 1) + borderLength
}
func pairwise[T any](arr []T) [][2]T {
var pairs [][2]T
for i := range arr[:len(arr)-1] {
pairs = append(pairs, [2]T{arr[i], arr[i+1]})
}
return pairs
}
var OFFSET = map[string]point{
"R": {0, 1},
"D": {1, 0},
"L": {0, -1},
"U": {-1, 0},
}
var OFFSET_INDEXES = maps.Values(OFFSET)
func fromLine(text string) (vector, string) {
v := vector{}
s, text, _ := strings.Cut(text, " ")
v.offset = OFFSET[s]
s, text, _ = strings.Cut(text, " ")
v.scale = aoc.Atoi(s)
_, text, _ = strings.Cut(text, "(#")
s, _, _ = strings.Cut(text, ")")
return v, s
}
func fromColor(c string) vector {
scale, _ := strconv.ParseInt(c[:5], 16, 64)
offset := OFFSET_INDEXES[c[5]-'0']
return vector{
offset: offset,
scale: int(scale),
}
}
func findArea(vecs []vector) int {
shoelace := []point{{0,0}}
borderLength := 0
for _, vec := range vecs {
scaled_offset := vec.offset.scale(vec.scale)
shoelace = append(shoelace, shoelace[len(shoelace)-1].add(scaled_offset))
borderLength += vec.scale
}
return numPoints(shoelace, borderLength)
}