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
 | |
| }
 |