diff --git a/2018/day06-2.go b/2018/day06-2.go new file mode 100644 index 0000000..8ed1908 --- /dev/null +++ b/2018/day06-2.go @@ -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) +} diff --git a/2018/internal/coords/coords.go b/2018/internal/coords/coords.go index af77623..3fa8723 100644 --- a/2018/internal/coords/coords.go +++ b/2018/internal/coords/coords.go @@ -11,6 +11,7 @@ type Point struct { RectArea int } +// Turn input data into useable structured point objects. func ParsePoints(data []string) []*Point { points := []*Point{} @@ -33,16 +34,7 @@ func ParsePoints(data []string) []*Point { // 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 - } - } + 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: @@ -50,7 +42,7 @@ func ComputeRectilinearAreas(data []*Point) { // 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}) + closest := findClosest(data, &Point{X: x, Y: y}) if closest == nil { continue } @@ -61,16 +53,47 @@ func ComputeRectilinearAreas(data []*Point) { // now we find any unbounded points and set their areas to the sentinel // 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] - distance := computeDistance(data[0], point) + distance := Distance(data[0], point) for i := 1; i < len(data); i++ { candidate := data[i] - newDistance := computeDistance(candidate, point) + newDistance := Distance(candidate, point) if newDistance < distance { closest = candidate @@ -83,13 +106,13 @@ func FindClosest(data []*Point, point *Point) *Point { return closest } -func DetectUnboundedPoints(data []*Point) { +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}) + 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 || @@ -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) }