186 lines
3.5 KiB
Go
186 lines
3.5 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"math"
|
||
|
"os"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
const MaxInt = int(^uint(0) >> 1)
|
||
|
|
||
|
func main() {
|
||
|
inp := StdinToStringSlice()
|
||
|
start(inp)
|
||
|
part1()
|
||
|
part2()
|
||
|
}
|
||
|
|
||
|
var points []RegionPos
|
||
|
var boundaries []RegionPos
|
||
|
var allPoints []RegionPos
|
||
|
var maxX, maxY, minX, minY int
|
||
|
|
||
|
func start(inp []string) {
|
||
|
maxX, maxY = 0, 0
|
||
|
minX, minY = MaxInt, MaxInt
|
||
|
|
||
|
for _, v := range inp {
|
||
|
pts := strings.Split(v, ", ")
|
||
|
x, y := Atoi(pts[0]), Atoi(pts[1])
|
||
|
p := RegionPos{x: x, y: y}
|
||
|
points = append(points, p)
|
||
|
if x < minX {
|
||
|
minX = x
|
||
|
}
|
||
|
if x > maxX {
|
||
|
maxX = x
|
||
|
}
|
||
|
if y < minY {
|
||
|
minY = y
|
||
|
}
|
||
|
if y > maxY {
|
||
|
maxY = y
|
||
|
}
|
||
|
}
|
||
|
// Find the points that are the closest to the boundaries
|
||
|
for x := minX; x <= maxX; x++ {
|
||
|
boundaries = append(boundaries, *NewRegionPos(x, minY))
|
||
|
boundaries = append(boundaries, *NewRegionPos(x, maxY))
|
||
|
}
|
||
|
for y := minY; y <= maxY; y++ {
|
||
|
boundaries = append(boundaries, *NewRegionPos(minX, y))
|
||
|
boundaries = append(boundaries, *NewRegionPos(maxX, y))
|
||
|
}
|
||
|
for i := range boundaries {
|
||
|
closest := boundaries[i].findClosest(points)
|
||
|
if len(closest) == 1 {
|
||
|
for j := range points {
|
||
|
if points[j].x == closest[0].x && points[j].y == closest[0].y {
|
||
|
points[j].ignored = true
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
boundaries[i].ignored = true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func part1() {
|
||
|
for x := minX; x <= maxX; x++ {
|
||
|
for y := minY; y <= maxY; y++ {
|
||
|
p := NewRegionPos(x, y)
|
||
|
closest := p.findClosest(points)
|
||
|
if len(closest) == 1 {
|
||
|
c := closest[0]
|
||
|
for i := range points {
|
||
|
if points[i].ignored {
|
||
|
continue
|
||
|
}
|
||
|
if points[i].x == c.x && points[i].y == c.y {
|
||
|
points[i].regionSize++
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
var largestSize int
|
||
|
for _, v := range points {
|
||
|
if !v.ignored {
|
||
|
if v.regionSize > largestSize {
|
||
|
largestSize = v.regionSize
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
fmt.Println("= Part 1 =")
|
||
|
fmt.Println(largestSize)
|
||
|
}
|
||
|
|
||
|
func part2() {
|
||
|
minDistance := 10000
|
||
|
|
||
|
var regionCount int
|
||
|
for y := minY - minDistance; y <= maxY+minDistance; y++ {
|
||
|
for x := minX - minDistance; x <= maxX+minDistance; x++ {
|
||
|
p := NewRegionPos(x, y)
|
||
|
if p.sumOfDistances(minDistance, points) < minDistance {
|
||
|
regionCount++
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
fmt.Println("= Part 2 =")
|
||
|
fmt.Println(regionCount)
|
||
|
}
|
||
|
|
||
|
func manhattanDistance(x1, y1, x2, y2 int) int {
|
||
|
return int(math.Abs(float64(x1)-float64(x2)) + math.Abs(float64(y1)-float64(y2)))
|
||
|
}
|
||
|
|
||
|
type RegionPos struct {
|
||
|
x, y int
|
||
|
regionSize int
|
||
|
closestPos *RegionPos
|
||
|
ignored bool
|
||
|
}
|
||
|
|
||
|
func NewRegionPos(x, y int) *RegionPos {
|
||
|
b := &RegionPos{}
|
||
|
b.x = x
|
||
|
b.y = y
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
func (p *RegionPos) distanceToPos(o *RegionPos) int {
|
||
|
return manhattanDistance(p.x, p.y, o.x, o.y)
|
||
|
}
|
||
|
|
||
|
func (b *RegionPos) sumOfDistances(d int, list []RegionPos) int {
|
||
|
var ret int
|
||
|
for _, p := range list {
|
||
|
ret += b.distanceToPos(&p)
|
||
|
}
|
||
|
return ret
|
||
|
}
|
||
|
|
||
|
func (b *RegionPos) findClosest(list []RegionPos) []RegionPos {
|
||
|
var ret []RegionPos
|
||
|
minD := MaxInt
|
||
|
for _, p := range list {
|
||
|
test := b.distanceToPos(&p)
|
||
|
if test < minD {
|
||
|
minD = test
|
||
|
ret = ret[:0]
|
||
|
ret = append(ret, p)
|
||
|
} else if test == minD {
|
||
|
ret = append(ret, p)
|
||
|
}
|
||
|
}
|
||
|
return ret
|
||
|
}
|
||
|
|
||
|
func (b *RegionPos) string() string {
|
||
|
return fmt.Sprintf("%d;%d", b.x, b.y)
|
||
|
}
|
||
|
|
||
|
func StdinToStringSlice() []string {
|
||
|
var input []string
|
||
|
scanner := bufio.NewScanner(os.Stdin)
|
||
|
for scanner.Scan() {
|
||
|
input = append(input, scanner.Text())
|
||
|
}
|
||
|
return input
|
||
|
}
|
||
|
|
||
|
func Atoi(i string) int {
|
||
|
var ret int
|
||
|
var err error
|
||
|
if ret, err = strconv.Atoi(i); err != nil {
|
||
|
log.Fatal("Invalid Atoi")
|
||
|
}
|
||
|
return ret
|
||
|
}
|