183 lines
3.8 KiB
Go
183 lines
3.8 KiB
Go
package day15
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
)
|
|
|
|
type Point struct {
|
|
x int
|
|
y int
|
|
}
|
|
|
|
func pointDistance(a Point, b Point) int {
|
|
return diff(a.x, b.x) + diff(a.y, b.y)
|
|
}
|
|
|
|
func diff(a int, b int) int {
|
|
if a > b {
|
|
return a - b
|
|
} else {
|
|
return b - a
|
|
}
|
|
}
|
|
|
|
type Sensor struct {
|
|
location Point
|
|
distance int
|
|
}
|
|
|
|
func parseLine(line string) (sensor Sensor, beacon Point) {
|
|
var sensorX int
|
|
var sensorY int
|
|
var beaconX int
|
|
var beaconY int
|
|
|
|
fmt.Sscanf(line, "Sensor at x=%d, y=%d: closest beacon is at x=%d, y=%d", &sensorX, &sensorY, &beaconX, &beaconY)
|
|
sensorLoc := Point{sensorX, sensorY}
|
|
beacon = Point{beaconX, beaconY}
|
|
distance := pointDistance(sensorLoc, beacon)
|
|
sensor = Sensor{sensorLoc, distance}
|
|
return
|
|
}
|
|
|
|
func isBeacon(x int, y int, beacons []Point) bool {
|
|
for _, beacon := range beacons {
|
|
if beacon.x == x && beacon.y == y {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func inSensorRange(x int, y int, sensors []Sensor) *Sensor {
|
|
point := Point{x, y}
|
|
for _, sensor := range sensors {
|
|
distance := pointDistance(point, sensor.location)
|
|
if distance <= sensor.distance {
|
|
return &sensor
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func leftmostPoint(sensors []Sensor) int {
|
|
leftmost := 0
|
|
|
|
for _, sensor := range sensors {
|
|
candidate := sensor.location.x - sensor.distance
|
|
if candidate < leftmost {
|
|
leftmost = candidate
|
|
}
|
|
}
|
|
|
|
return leftmost
|
|
}
|
|
|
|
func rightmostPoint(sensors []Sensor) int {
|
|
rightmost := 0
|
|
|
|
for _, sensor := range sensors {
|
|
candidate := sensor.location.x + sensor.distance
|
|
if candidate > rightmost {
|
|
rightmost = candidate
|
|
}
|
|
}
|
|
|
|
return rightmost
|
|
}
|
|
|
|
func addLinePoints(pointSet *[]Point, start Point, end Point) {
|
|
offsetX := 1
|
|
offsetY := 1
|
|
|
|
if end.x < start.x {
|
|
offsetX = -1
|
|
}
|
|
if end.y < start.y {
|
|
offsetY = -1
|
|
}
|
|
|
|
for x, y := start.x, start.y; y != end.y; x, y = x+offsetX, y+offsetY {
|
|
*pointSet = append(*pointSet, Point{x, y})
|
|
}
|
|
}
|
|
|
|
func addEdgePoints(pointSet *[]Point, sensor Sensor) {
|
|
distance := sensor.distance + 1
|
|
top := Point{sensor.location.x, sensor.location.y - distance}
|
|
right := Point{sensor.location.x + distance, sensor.location.y}
|
|
bottom := Point{sensor.location.x, sensor.location.y + distance}
|
|
left := Point{sensor.location.x - distance, sensor.location.y}
|
|
|
|
addLinePoints(pointSet, top, right)
|
|
addLinePoints(pointSet, right, bottom)
|
|
addLinePoints(pointSet, bottom, left)
|
|
addLinePoints(pointSet, left, top)
|
|
}
|
|
|
|
func Run(filename string, yStr string, maxCoordStr string) {
|
|
y, err := strconv.Atoi(yStr)
|
|
if err != nil {
|
|
fmt.Println("PANIC: bad y argument", yStr)
|
|
return
|
|
}
|
|
|
|
maxCoord, err := strconv.Atoi(maxCoordStr)
|
|
if err != nil {
|
|
fmt.Println("PANIC: bad y argument", maxCoordStr)
|
|
return
|
|
}
|
|
|
|
file, err := os.Open(filename)
|
|
|
|
if err != nil {
|
|
fmt.Println("Error opening file:", err)
|
|
return
|
|
}
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
scanner.Split(bufio.ScanLines)
|
|
sensors := []Sensor{}
|
|
beacons := []Point{}
|
|
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
sensor, beacon := parseLine(line)
|
|
sensors = append(sensors, sensor)
|
|
beacons = append(beacons, beacon)
|
|
}
|
|
|
|
file.Close()
|
|
|
|
fmt.Println(sensors)
|
|
|
|
count := 0
|
|
for x := leftmostPoint(sensors); x <= rightmostPoint(sensors); x++ {
|
|
if isBeacon(x, y, beacons) {
|
|
continue
|
|
}
|
|
|
|
if inSensorRange(x, y, sensors) != nil {
|
|
count += 1
|
|
}
|
|
}
|
|
fmt.Println("Beacons couldn't be in", count, "places on line", y)
|
|
|
|
candidates := []Point{}
|
|
for _, sensor := range sensors {
|
|
addEdgePoints(&candidates, sensor)
|
|
}
|
|
|
|
for _, candidate := range candidates {
|
|
if candidate.x >= 0 && candidate.y >= 0 && candidate.x <= maxCoord && candidate.y <= maxCoord {
|
|
closestSensor := inSensorRange(candidate.x, candidate.y, sensors)
|
|
if closestSensor == nil {
|
|
fmt.Printf("Found possible beacon location at (%d, %d). Frequency value is %d\n", candidate.x, candidate.y, candidate.x*4000000+candidate.y)
|
|
}
|
|
}
|
|
}
|
|
}
|