adventofcode/2018/internal/coords/coords.go

112 lines
2.6 KiB
Go
Raw Normal View History

2018-12-06 07:23:45 +00:00
package coords
import (
"strconv"
"strings"
)
type Point struct {
X int
Y int
RectArea int
}
func ParsePoints(data []string) []*Point {
points := []*Point{}
for _, pointInfo := range data {
components := strings.Split(pointInfo, ", ")
x, _ := strconv.Atoi(components[0])
y, _ := strconv.Atoi(components[1])
points = append(points, &Point{
X: x,
Y: y,
})
}
return points
}
// Mutates the passed-in data in-place, computing the rectilinear
// area for all points within the space bounded by (0,0) and the points themselves.
// Sets the area of the 4 corner-most points (which definitionally must have
// unbounded areas) to -1
func ComputeRectilinearAreas(data []*Point) {
// first we find the bounds
maxX := 0
maxY := 0
for _, point := range data {
if point.X > maxX {
maxX = point.X
}
if point.Y > maxY {
maxY = point.Y
}
}
// now for each integer point in our range, we compute the distance to each
// point. We save the closest point and its distance, then:
// 1. If there are two or more identically closest points, we do nothing.
// 2. Otherwise, we increment RectArea for the closest point.
for x := -maxX; x <= maxX*2; x++ {
for y := -maxY; y <= maxY*2; y++ {
closest := FindClosest(data, &Point{X: x, Y: y})
if closest == nil {
continue
}
closest.RectArea++
}
}
// now we find any unbounded points and set their areas to the sentinel
// value
DetectUnboundedPoints(data)
}
func FindClosest(data []*Point, point *Point) *Point {
closest := data[0]
distance := computeDistance(data[0], point)
for i := 1; i < len(data); i++ {
candidate := data[i]
newDistance := computeDistance(candidate, point)
if newDistance < distance {
closest = candidate
distance = newDistance
} else if newDistance == distance {
closest = nil
}
}
return closest
}
func DetectUnboundedPoints(data []*Point) {
for _, candidate := range data {
// look in each direction
up := FindClosest(data, &Point{X: candidate.X, Y: candidate.Y + 1000000})
down := FindClosest(data, &Point{X: candidate.X, Y: candidate.Y - 1000000})
left := FindClosest(data, &Point{X: candidate.X + 1000000, Y: candidate.Y})
right := FindClosest(data, &Point{X: candidate.X - 1000000, Y: candidate.Y})
// if any of those points are closest to our point, we're unbounded
if up == candidate || down == candidate ||
left == candidate || right == candidate {
candidate.RectArea = -1
}
}
}
func computeDistance(p1 *Point, p2 *Point) int {
return abs(p2.Y-p1.Y) + abs(p2.X-p1.X)
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}