back home

Day - 15

Part 1 - Code

      
package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
)

var year = 2022
var day = 15
var path = os.Getenv("ADVENT_HOME")

func AbsInt(x int) int {
	if x < 0 {
		return -x
	}
	return x
}

type Tuple struct {
	x, y int
}

func (t Tuple) String() string {
	return fmt.Sprintf("(%d,%d)", t.x, t.y)
}

type Sensor struct {
	source Tuple
	beacon Tuple
}

func TaxiCabDist(a Tuple, b Tuple) int {
	return AbsInt(a.x-b.x) + AbsInt(a.y-b.y)
}

func (s Sensor) String() string {
	return fmt.Sprintf(" ")
}

func (s Sensor) TaxiCabDist() int {
	return TaxiCabDist(s.source, s.beacon)
}

func (s Sensor) LeftBound() int {
	return s.source.x - s.TaxiCabDist()
}

func (s Sensor) RightBound() int {
	return s.source.x + s.TaxiCabDist()
}

func StringToTuple(str string) Tuple {
	tokens := strings.Split(str, ",")
	x := strings.TrimSpace(tokens[0])
	y := strings.TrimSpace(tokens[1])

	x = strings.TrimPrefix(x, "x=")
	y = strings.TrimPrefix(y, "y=")

	xVal, _ := strconv.Atoi(x)
	yVal, _ := strconv.Atoi(y)

	return Tuple{xVal, yVal}
}

func StringToSensor(dataLine string) Sensor {
	tokens := strings.Split(dataLine, ":")
	left := tokens[0]
	right := tokens[1]

	left = strings.TrimPrefix(left, "Sensor at ")
	right = strings.TrimPrefix(right, " closest beacon is at ")
	l := StringToTuple(left)
	r := StringToTuple(right)

	return Sensor{l, r}
}

func GetScannedSpots(sensors []Sensor, minX, maxX, atRow int) int {
	count := 0

	occupied := make(map[Tuple]bool)
	for _, sensor := range sensors {
		occupied[sensor.source] = true
		occupied[sensor.beacon] = true
	}

	for i := minX; i <= maxX; i++ {
		inRange := false
		spot := Tuple{i, atRow}
		if _, ok := occupied[spot]; ok {
			continue
		}
		for _, sensor := range sensors {
			if TaxiCabDist(spot, sensor.source) <= sensor.TaxiCabDist() {
				inRange = true
			}
			if inRange {
				break
			}
		}
		if inRange {
			count += 1
		}
	}

	return count
}

func NotInRange(sensors []Sensor, at Tuple) bool {
	for _, sensor := range sensors {
		if TaxiCabDist(at, sensor.source) <= sensor.TaxiCabDist() {
			return false
		}
	}
	return true
}

func CalculateTuningFrequency(t Tuple) int {
	return (t.x * 4000000) + t.y
}

func InBoundsChecker(maxX int, maxY int) func(Tuple) bool {
	return func(t Tuple) bool {
		return (0 <= t.x && t.x <= maxX) && (0 <= t.y && t.y <= maxY)
	}
}

func FindBeaconSpot(sensors []Sensor, maxX, maxY, atRow int) int {
	// find range with biggest area
	// navigate circumference of area to find spot
	for _, sensor := range sensors {
		sensorMax := sensor

		// fmt.Println(sensorMax.source)

		check := InBoundsChecker(maxX, maxY)

		dist := sensorMax.TaxiCabDist() + 1
		x, y := sensorMax.source.x+dist, sensorMax.source.y
		// up left

		for i := 0; i <= dist; i++ {
			at := Tuple{x - i, y + i}
			// fmt.Println(at)
			if check(at) && NotInRange(sensors, at) {
				return CalculateTuningFrequency(at)
			}
		}

		// down left
		x, y = sensorMax.source.x, sensorMax.source.y+dist

		for i := 0; i <= dist; i++ {
			at := Tuple{x - i, y - i}
			// fmt.Println(at)

			if check(at) && NotInRange(sensors, at) {
				return CalculateTuningFrequency(at)
			}
		}

		x, y = sensorMax.source.x-dist, sensorMax.source.y
		// down right
		for i := 0; i <= dist; i++ {
			at := Tuple{x + i, y - i}
			// fmt.Println(at)

			if check(at) && NotInRange(sensors, at) {
				return CalculateTuningFrequency(at)
			}
		}

		x, y = sensorMax.source.x, sensorMax.source.y-dist
		// up right
		for i := 0; i <= dist; i++ {
			at := Tuple{x + i, y + i}
			// fmt.Println(at)

			if check(at) && NotInRange(sensors, at) {
				return CalculateTuningFrequency(at)
			}
		}
	}

	return 0
}

func main() {

	dataPath := fmt.Sprintf("%s/%d/data/day%d/data.txt", path, year, day)
	fmt.Println(dataPath)

	file, err := os.Open(dataPath)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)

	sensors := []Sensor{}
	for scanner.Scan() {
		line := scanner.Text()
		sensor := StringToSensor(line)
		sensors = append(sensors, sensor)
	}

	maxX := 0

	for _, sensor := range sensors {
		rightBound := sensor.RightBound()
		if maxX < rightBound {
			maxX = rightBound
		}
	}

	minX := maxX
	for _, sensor := range sensors {
		leftBound := sensor.LeftBound()
		if minX > leftBound {
			minX = leftBound
		}
	}

	fmt.Printf("l=%d, r=%d\n", minX, maxX)
	atRow := 2000000
	// atRow := 10

	count := 0
	count = GetScannedSpots(sensors, minX, maxX, atRow)
	fmt.Printf("count = %d\n", count)

	sizeX := 4000000
	sizeY := 4000000

	// sizeX := 20
	// sizeY := 20

	count = FindBeaconSpot(sensors, sizeX, sizeY, atRow)
	fmt.Printf("count = %d\n", count)
}