Rename shared packages by day instead of vaguely by purpose.
This commit is contained in:
parent
42cb05ba7f
commit
31f60eca0e
22 changed files with 49 additions and 50 deletions
149
2018/internal/day06/coords.go
Normal file
149
2018/internal/day06/coords.go
Normal file
|
@ -0,0 +1,149 @@
|
|||
package day06
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Point struct {
|
||||
X int
|
||||
Y int
|
||||
RectArea int
|
||||
}
|
||||
|
||||
// Turn input data into useable structured point objects.
|
||||
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, maxY := findUpperBounds(data)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// ComputeNearnessRegion takes a list of points, returns the number of points
|
||||
// whose rectilinear distance to *all* the listed points sums to less than the
|
||||
// target value.
|
||||
func ComputeNearnessRegion(points []*Point, target int) int {
|
||||
maxX, maxY := findUpperBounds(points)
|
||||
|
||||
// this is a 'brute-force' solution that just picks a suitably wide
|
||||
// area to search.
|
||||
area := 0
|
||||
for x := -target; x < maxX+target; x++ {
|
||||
for y := -target; y < maxY+target; y++ {
|
||||
if sumDistances(points, &Point{X: x, Y: y}) < target {
|
||||
area++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return area
|
||||
}
|
||||
|
||||
// sumDistances calculates the distance from each point in `points` to `target`, and
|
||||
// returns the sum of all of those distances
|
||||
func sumDistances(points []*Point, target *Point) int {
|
||||
area := 0
|
||||
for _, point := range points {
|
||||
area += Distance(point, target)
|
||||
}
|
||||
return area
|
||||
}
|
||||
|
||||
// Find the nearest point in `data` to `point`, using rectilinear distance.
|
||||
func findClosest(data []*Point, point *Point) *Point {
|
||||
closest := data[0]
|
||||
distance := Distance(data[0], point)
|
||||
|
||||
for i := 1; i < len(data); i++ {
|
||||
candidate := data[i]
|
||||
newDistance := Distance(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 findUpperBounds(points []*Point) (int, int) {
|
||||
maxX := 0
|
||||
maxY := 0
|
||||
for _, point := range points {
|
||||
if point.X > maxX {
|
||||
maxX = point.X
|
||||
}
|
||||
if point.Y > maxY {
|
||||
maxY = point.Y
|
||||
}
|
||||
}
|
||||
|
||||
return maxX, maxY
|
||||
}
|
||||
|
||||
func Distance(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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue