chore: use shoelace and picks theorem
All checks were successful
Go Test / build (pull_request) Successful in 57s

This commit is contained in:
xuu 2023-12-27 13:46:42 -07:00
parent b6501e80c9
commit bcd90a57f3
Signed by: xuu
GPG Key ID: 8B3B0604F164E04F
2 changed files with 76 additions and 184 deletions

View File

@ -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)
} }

View File

@ -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)
} }