Day 6, part 2. A bit brute-force, but it gets the job done.
This commit is contained in:
parent
0c96d7dded
commit
0388b57112
16
2018/day06-2.go
Normal file
16
2018/day06-2.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"internal/coords"
|
||||||
|
"internal/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
data := util.ReadInput()
|
||||||
|
points := coords.ParsePoints(data)
|
||||||
|
|
||||||
|
area := coords.ComputeNearnessRegion(points, 10000)
|
||||||
|
fmt.Println(area)
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ type Point struct {
|
||||||
RectArea int
|
RectArea int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Turn input data into useable structured point objects.
|
||||||
func ParsePoints(data []string) []*Point {
|
func ParsePoints(data []string) []*Point {
|
||||||
points := []*Point{}
|
points := []*Point{}
|
||||||
|
|
||||||
|
@ -33,16 +34,7 @@ func ParsePoints(data []string) []*Point {
|
||||||
// unbounded areas) to -1
|
// unbounded areas) to -1
|
||||||
func ComputeRectilinearAreas(data []*Point) {
|
func ComputeRectilinearAreas(data []*Point) {
|
||||||
// first we find the bounds
|
// first we find the bounds
|
||||||
maxX := 0
|
maxX, maxY := findUpperBounds(data)
|
||||||
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
|
// now for each integer point in our range, we compute the distance to each
|
||||||
// point. We save the closest point and its distance, then:
|
// point. We save the closest point and its distance, then:
|
||||||
|
@ -50,7 +42,7 @@ func ComputeRectilinearAreas(data []*Point) {
|
||||||
// 2. Otherwise, we increment RectArea for the closest point.
|
// 2. Otherwise, we increment RectArea for the closest point.
|
||||||
for x := -maxX; x <= maxX*2; x++ {
|
for x := -maxX; x <= maxX*2; x++ {
|
||||||
for y := -maxY; y <= maxY*2; y++ {
|
for y := -maxY; y <= maxY*2; y++ {
|
||||||
closest := FindClosest(data, &Point{X: x, Y: y})
|
closest := findClosest(data, &Point{X: x, Y: y})
|
||||||
if closest == nil {
|
if closest == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -61,16 +53,47 @@ func ComputeRectilinearAreas(data []*Point) {
|
||||||
|
|
||||||
// now we find any unbounded points and set their areas to the sentinel
|
// now we find any unbounded points and set their areas to the sentinel
|
||||||
// value
|
// value
|
||||||
DetectUnboundedPoints(data)
|
detectUnboundedPoints(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindClosest(data []*Point, point *Point) *Point {
|
// 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]
|
closest := data[0]
|
||||||
distance := computeDistance(data[0], point)
|
distance := Distance(data[0], point)
|
||||||
|
|
||||||
for i := 1; i < len(data); i++ {
|
for i := 1; i < len(data); i++ {
|
||||||
candidate := data[i]
|
candidate := data[i]
|
||||||
newDistance := computeDistance(candidate, point)
|
newDistance := Distance(candidate, point)
|
||||||
|
|
||||||
if newDistance < distance {
|
if newDistance < distance {
|
||||||
closest = candidate
|
closest = candidate
|
||||||
|
@ -83,13 +106,13 @@ func FindClosest(data []*Point, point *Point) *Point {
|
||||||
return closest
|
return closest
|
||||||
}
|
}
|
||||||
|
|
||||||
func DetectUnboundedPoints(data []*Point) {
|
func detectUnboundedPoints(data []*Point) {
|
||||||
for _, candidate := range data {
|
for _, candidate := range data {
|
||||||
// look in each direction
|
// look in each direction
|
||||||
up := FindClosest(data, &Point{X: candidate.X, Y: candidate.Y + 1000000})
|
up := findClosest(data, &Point{X: candidate.X, Y: candidate.Y + 1000000})
|
||||||
down := 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})
|
left := findClosest(data, &Point{X: candidate.X + 1000000, Y: candidate.Y})
|
||||||
right := 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 any of those points are closest to our point, we're unbounded
|
||||||
if up == candidate || down == candidate ||
|
if up == candidate || down == candidate ||
|
||||||
|
@ -99,7 +122,22 @@ func DetectUnboundedPoints(data []*Point) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func computeDistance(p1 *Point, p2 *Point) int {
|
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)
|
return abs(p2.Y-p1.Y) + abs(p2.X-p1.X)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user