diff --git a/2018/day06-1.go b/2018/day06-1.go new file mode 100644 index 0000000..b10e70a --- /dev/null +++ b/2018/day06-1.go @@ -0,0 +1,23 @@ +package main + +import ( + "fmt" + + "internal/coords" + "internal/util" +) + +func main() { + data := util.ReadInput() + points := coords.ParsePoints(data) + + coords.ComputeRectilinearAreas(points) + + biggest := points[0] + for _, point := range points { + if point.RectArea > biggest.RectArea { + biggest = point + } + } + fmt.Println(biggest.RectArea) +} diff --git a/2018/internal/coords/coords.go b/2018/internal/coords/coords.go new file mode 100644 index 0000000..af77623 --- /dev/null +++ b/2018/internal/coords/coords.go @@ -0,0 +1,111 @@ +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 +}