chore: use shoelace and picks theorem
All checks were successful
Go Test / build (pull_request) Successful in 57s
All checks were successful
Go Test / build (pull_request) Successful in 57s
This commit is contained in:
parent
b6501e80c9
commit
bcd90a57f3
237
day18/main.go
237
day18/main.go
|
@ -3,9 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -26,8 +24,8 @@ func (r result) String() string { return fmt.Sprintf("%#v", r) }
|
||||||
|
|
||||||
func run(scan *bufio.Scanner) (*result, error) {
|
func run(scan *bufio.Scanner) (*result, error) {
|
||||||
|
|
||||||
var vecs []vec
|
var vecsPT1 []vector
|
||||||
var vecs2 []vec
|
var vecsPT2 []vector
|
||||||
|
|
||||||
for scan.Scan() {
|
for scan.Scan() {
|
||||||
text := scan.Text()
|
text := scan.Text()
|
||||||
|
@ -36,199 +34,96 @@ func run(scan *bufio.Scanner) (*result, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var s string
|
v, color := fromLine(text)
|
||||||
v := vec{}
|
|
||||||
s, text, _ = strings.Cut(text, " ")
|
vecsPT1 = append(vecsPT1, v)
|
||||||
v.Direction = direction(s[0])
|
vecsPT2 = append(vecsPT2, fromColor(color))
|
||||||
s, text, _ = strings.Cut(text, " ")
|
|
||||||
v.Steps = aoc.Atoi(s)
|
|
||||||
_, text, _ = strings.Cut(text, "#")
|
|
||||||
s, _, _ = strings.Cut(text, ")")
|
|
||||||
b, _ := hex.DecodeString(s)
|
|
||||||
copy(v.Color[:], b)
|
|
||||||
vecs = append(vecs, v)
|
|
||||||
vecs2 = append(vecs2, fromColor(s))
|
|
||||||
}
|
}
|
||||||
return &result{
|
return &result{
|
||||||
valuePT1: findArea(vecs),
|
valuePT1: findArea(vecsPT1),
|
||||||
valuePT2: skip("AOC_DAY18P2", func() int { return findArea(vecs2) }),
|
valuePT2: findArea(vecsPT2),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func findArea(vecs []vec) int {
|
type vector struct {
|
||||||
var points []point
|
offset point
|
||||||
|
scale int
|
||||||
var x, y int
|
|
||||||
var minX, minY int
|
|
||||||
last := direction('S')
|
|
||||||
for _, v := range vecs {
|
|
||||||
// fmt.Println("pt ", i, v)
|
|
||||||
for i := 0; i < v.Steps; i++ {
|
|
||||||
switch v.Direction {
|
|
||||||
case 'U':
|
|
||||||
y++
|
|
||||||
case 'D':
|
|
||||||
y--
|
|
||||||
case 'R':
|
|
||||||
x++
|
|
||||||
case 'L':
|
|
||||||
x--
|
|
||||||
}
|
|
||||||
// fmt.Println("pt ", i, y, x)
|
|
||||||
|
|
||||||
minX = min(minX, x)
|
|
||||||
minY = min(minY, y)
|
|
||||||
if len(points) > 0 {
|
|
||||||
points[len(points)-1].d = v.Direction
|
|
||||||
}
|
|
||||||
points = append(points, point{d: v.Direction, w: opposite(v.Direction), x: x, y: y, color: v.Color})
|
|
||||||
last = v.Direction
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ = last
|
|
||||||
// points[0].w = last
|
|
||||||
points[len(points)-1].d = points[0].w
|
|
||||||
|
|
||||||
adjX := aoc.ABS(min(0, minX))
|
|
||||||
adjY := aoc.ABS(min(0, minY))
|
|
||||||
fmt.Println("minX", minX, "minY", minY)
|
|
||||||
fmt.Println("adjX", adjX, "adjY", adjY)
|
|
||||||
|
|
||||||
trace := make(map[int]map[int]point)
|
|
||||||
|
|
||||||
minX, minY = 0, 0
|
|
||||||
maxX, maxY := 0, 0
|
|
||||||
|
|
||||||
for i, p := range points {
|
|
||||||
|
|
||||||
p.x += adjX
|
|
||||||
p.y += adjY
|
|
||||||
// fmt.Println("raw", i, p.x, p.y, string(p.w), string(p.d))
|
|
||||||
|
|
||||||
maxX = max(maxX, p.x)
|
|
||||||
maxY = max(maxY, p.y)
|
|
||||||
|
|
||||||
points[i] = p
|
|
||||||
|
|
||||||
if row, ok := trace[p.y]; true {
|
|
||||||
if !ok {
|
|
||||||
row = make(map[int]point)
|
|
||||||
}
|
|
||||||
row[p.x] = p
|
|
||||||
trace[p.y] = row
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println("maxX", maxX, "maxY", maxY)
|
|
||||||
|
|
||||||
area := 0
|
|
||||||
|
|
||||||
for y := maxY; y >= 0; y-- {
|
|
||||||
row, ok := trace[y]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var last direction
|
type point [2]int
|
||||||
p := row[0]
|
|
||||||
last = p.d
|
|
||||||
inLoop := false
|
|
||||||
|
|
||||||
for x := 0; x <= maxX; x++ {
|
func (p point) add(a point) point {
|
||||||
if p, ok := row[x]; ok {
|
return point{p[0] + a[0], p[1] + a[1]}
|
||||||
// fmt.Println("vec", string(p.w), string(p.d))
|
|
||||||
switch string([]rune{rune(p.w), rune(p.d)}) {
|
|
||||||
case "LD", "DL", "UD", "DU", "RD", "UU", "DR":
|
|
||||||
inLoop = !inLoop
|
|
||||||
}
|
}
|
||||||
if last != p.d {
|
func (p point) scale(m int) point {
|
||||||
last = p.d
|
return point{p[0] * m, p[1] * m}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fmt.Print(string(p.w)+string(p.d))
|
// 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]
|
||||||
|
|
||||||
// On loop
|
sum += row1*col2 - row2*col1
|
||||||
area++
|
|
||||||
|
|
||||||
continue // 203338
|
|
||||||
}
|
}
|
||||||
last = direction('0')
|
area := sum / 2
|
||||||
|
|
||||||
if inLoop {
|
// pick's theorem - find the number of points in a shape given its area
|
||||||
area++
|
return (aoc.ABS(area) - borderLength/2 + 1) + borderLength
|
||||||
// fmt.Print("XX")
|
|
||||||
// } else {
|
|
||||||
// fmt.Print("..")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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]})
|
||||||
}
|
}
|
||||||
// fmt.Println("")
|
return pairs
|
||||||
fmt.Println(y, area)
|
|
||||||
}
|
|
||||||
return area
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type direction rune
|
var OFFSET = map[string]point{
|
||||||
|
"R": {0, 1},
|
||||||
const (
|
"D": {1, 0},
|
||||||
U direction = 'U'
|
"L": {0, -1},
|
||||||
D direction = 'D'
|
"U": {-1, 0},
|
||||||
L direction = 'L'
|
|
||||||
R direction = 'R'
|
|
||||||
)
|
|
||||||
|
|
||||||
func opposite(d direction) direction {
|
|
||||||
switch d {
|
|
||||||
case U:
|
|
||||||
return D
|
|
||||||
case D:
|
|
||||||
return U
|
|
||||||
case L:
|
|
||||||
return R
|
|
||||||
case R:
|
|
||||||
return L
|
|
||||||
}
|
}
|
||||||
return '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
|
||||||
}
|
}
|
||||||
|
|
||||||
type vec struct {
|
func fromColor(c string) vector {
|
||||||
Direction direction
|
scale, _ := strconv.ParseInt(c[:5], 16, 64)
|
||||||
Steps int
|
offset := OFFSET_INDEXES[c[5]-'0']
|
||||||
Color [3]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type point struct {
|
return vector{
|
||||||
w direction
|
offset: offset,
|
||||||
d direction
|
scale: int(scale),
|
||||||
x, y int
|
|
||||||
color [3]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func fromColor(c string) vec {
|
|
||||||
steps, _ := strconv.ParseInt(c[:5], 16, 64)
|
|
||||||
|
|
||||||
d := '_'
|
|
||||||
switch c[5] {
|
|
||||||
case '0':
|
|
||||||
d = 'R'
|
|
||||||
case '1':
|
|
||||||
d = 'D'
|
|
||||||
case '2':
|
|
||||||
d = 'L'
|
|
||||||
case '3':
|
|
||||||
d = 'U'
|
|
||||||
}
|
|
||||||
|
|
||||||
return vec{
|
|
||||||
Direction: direction(d),
|
|
||||||
Steps: int(steps),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func skip[T any](env string, fn func() T) T {
|
func findArea(vecs []vector) int {
|
||||||
var zero T
|
shoelace := []point{{0,0}}
|
||||||
if e, err := strconv.ParseBool(os.Getenv(env)); err == nil && e {
|
borderLength := 0
|
||||||
return zero
|
|
||||||
|
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 fn()
|
return numPoints(shoelace, borderLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
@ -33,13 +32,11 @@ func TestSolution(t *testing.T) {
|
||||||
is := is.New(t)
|
is := is.New(t)
|
||||||
scan := bufio.NewScanner(bytes.NewReader(input))
|
scan := bufio.NewScanner(bytes.NewReader(input))
|
||||||
|
|
||||||
os.Setenv("AOC_DAY18P2", "1")
|
|
||||||
|
|
||||||
result, err := run(scan)
|
result, err := run(scan)
|
||||||
is.NoErr(err)
|
is.NoErr(err)
|
||||||
|
|
||||||
t.Log(result)
|
t.Log(result)
|
||||||
is.True(result.valuePT1 < 68834) // first attempt too high.
|
is.True(result.valuePT1 < 68834) // first attempt too high.
|
||||||
is.Equal(result.valuePT1, 46334)
|
is.Equal(result.valuePT1, 46334)
|
||||||
is.Equal(result.valuePT2, 0)
|
is.Equal(result.valuePT2, 102000662718092)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user