diff --git a/day15/common/beacon.go b/day15/common/beacon.go new file mode 100644 index 0000000..9545b4d --- /dev/null +++ b/day15/common/beacon.go @@ -0,0 +1,229 @@ +package common + +import ( + "bufio" + "fmt" + "strconv" + "strings" +) + + +type Tile byte + +const ( + Sensor Tile = iota + Beacon + Covered + Free +) + + +type Map struct { + sensors []SensorPosition + minX int + maxX int + minY int + maxY int +} + +func NewMap() *Map { + tileMap := Map{} + tileMap.sensors = []SensorPosition{} + tileMap.minX = 0 + tileMap.maxX = 0 + tileMap.minY = 0 + tileMap.maxY = 0 + return &tileMap +} + +func (tileMap *Map) GetTile(x, y int) Tile { + for _, sensor := range tileMap.sensors { + if sensor.x == x && sensor.y == y { + return Sensor + } else if sensor.xBeacon == x && sensor.yBeacon == y { + return Beacon + } + } + + for _, sensor := range tileMap.sensors { + coverageX := sensor.x - sensor.xBeacon + if coverageX < 0 { + coverageX *= -1 + } + coverageY := sensor.y - sensor.yBeacon + if coverageY < 0 { + coverageY *= -1 + } + coverage := coverageX + coverageY + + distanceX := sensor.x - x + if distanceX < 0 { + distanceX *= -1 + } + distanceY := sensor.y - y + if distanceY < 0 { + distanceY *= -1 + } + distance := distanceX + distanceY + + if distance <= coverage { + return Covered + } + } + + return Free +} + +func (tileMap *Map) SetSensor(sensor SensorPosition) { + tileMap.sensors = append(tileMap.sensors, sensor) + + coverageX := sensor.x - sensor.xBeacon + if coverageX < 0 { + coverageX *= -1 + } + coverageY := sensor.y - sensor.yBeacon + if coverageY < 0 { + coverageY *= -1 + } + coverage := coverageX + coverageY + + if sensor.x - coverage < tileMap.minX { + tileMap.minX = sensor.x - coverage + } + if sensor.x + coverage > tileMap.maxX { + tileMap.maxX = sensor.x + coverage + } + if sensor.y - coverage < tileMap.minY { + tileMap.minY = sensor.y - coverage + } + if sensor.y + coverage > tileMap.maxY { + tileMap.maxY = sensor.y + coverage + } +} + +func (tileMap *Map) GetCoverageY(y int) int { + return tileMap.GetCoverageYInXInterval(tileMap.minX, tileMap.maxX, y) +} + +func (tileMap *Map) GetCoverageYInXInterval(xMin, xMax, y int) int { + coverages := []struct{min int; max int}{} + nCovered := 0 + foundBeacons := make(map[int]struct{}) + + for _, sensor := range tileMap.sensors { + if _, ok := foundBeacons[sensor.xBeacon]; !ok && sensor.yBeacon == y && sensor.xBeacon >= xMin && sensor.xBeacon <= xMax { + foundBeacons[sensor.xBeacon] = struct{}{} + nCovered-- + } + + coverageX := sensor.x - sensor.xBeacon + if coverageX < 0 { + coverageX *= -1 + } + coverageY := sensor.y - sensor.yBeacon + if coverageY < 0 { + coverageY *= -1 + } + coverage := coverageX + coverageY + + if y >= sensor.y - coverage && y <= sensor.y + coverage { + distanceY := sensor.y - y + if distanceY < 0 { + distanceY *= -1 + } + + distanceX := coverage - distanceY + coverageXMin := sensor.x - distanceX + if coverageXMin < xMin { + coverageXMin = xMin + } + coverageXMax := sensor.x + distanceX + if coverageXMax > xMax { + coverageXMax = xMax + } + + coverages = append(coverages, struct{min int; max int}{coverageXMin, coverageXMax}) + } + } + + for i := 0; i < len(coverages); i++ { + for j := 0; j < len(coverages); j++ { + if j != i { + found := false + if coverages[j].min >= coverages[i].min && coverages[j].min <= coverages[i].max && coverages[j].max >= coverages[i].max { + coverages[i].max = coverages[j].max + found = true + } else if coverages[j].min <= coverages[i].min && coverages[j].max >= coverages[i].min && coverages[j].max <= coverages[i].max { + coverages[i].min = coverages[j].min + found = true + } else if coverages[j].min < coverages[i].min && coverages[j].max > coverages[i].max { + coverages[i].min = coverages[j].min + coverages[i].max = coverages[j].max + found = true + } else if coverages[j].min >= coverages[i].min && coverages[j].max <= coverages[i].max { + found = true + } + if found { + if j == 0 { + coverages = coverages[1:] + } else if j == len(coverages) - 1 { + coverages = coverages[:len(coverages)-1] + } else { + coverages = append(coverages[0:j], coverages[j+1:]...) + } + j = 0 + } + } + } + } + + for _, coverage := range(coverages) { + nCovered += coverage.max - coverage.min + 1 + } + + return nCovered +} + +func (tileMap *Map) Print(xMin, xMax, yMin, yMax int) { + for i := yMin; i <= yMax; i++ { + for j := xMin; j <= xMax; j++ { + switch tileMap.GetTile(j, i) { + case Beacon: + fmt.Print("B") + case Sensor: + fmt.Print("S") + case Covered: + fmt.Print("#") + case Free: + fmt.Print(".") + } + } + fmt.Println() + } +} + + +type SensorPosition struct { + x int + y int + xBeacon int + yBeacon int +} + + +func Parse(scanner bufio.Scanner) []SensorPosition { + sensors := []SensorPosition{} + + for scanner.Scan() { + line := scanner.Text() + splittedLine := strings.Split(line, " ") + x, _ := strconv.Atoi(splittedLine[2][2:len(splittedLine[2])-1]) + y, _ := strconv.Atoi(splittedLine[3][2:len(splittedLine[3])-1]) + xBeacon, _ := strconv.Atoi(splittedLine[8][2:len(splittedLine[8])-1]) + yBeacon, _ := strconv.Atoi(splittedLine[9][2:]) + + sensors = append(sensors, SensorPosition{x, y, xBeacon, yBeacon}) + } + + return sensors +} diff --git a/day15/ex1/main.go b/day15/ex1/main.go new file mode 100644 index 0000000..60bafb1 --- /dev/null +++ b/day15/ex1/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "aoc2022/day15/common" + "bufio" + "fmt" + "os" +) + +func main() { + sensors := common.Parse(*bufio.NewScanner(os.Stdin)) + tileMap := common.NewMap() + + for _, sensor := range sensors { + tileMap.SetSensor(sensor) + } + + fmt.Println(tileMap.GetCoverageY(2000000)) +} diff --git a/day15/ex2/main.go b/day15/ex2/main.go new file mode 100644 index 0000000..b9e9055 --- /dev/null +++ b/day15/ex2/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "aoc2022/day15/common" + "bufio" + "fmt" + "os" +) + +func main() { + sensors := common.Parse(*bufio.NewScanner(os.Stdin)) + tileMap := common.NewMap() + + for _, sensor := range sensors { + tileMap.SetSensor(sensor) + } + + var frequency int + found := false + for i := 0; i <= 4000000 && !found; i++ { + coverage := tileMap.GetCoverageYInXInterval(0, 4000000, i) + if coverage <= 4000000 { + for j := 0; j < 4000000 && !found; j++ { + if tileMap.GetTile(j, i) == common.Free { + frequency = j * 4000000 + i + found = true + } + } + } + } + + fmt.Println(frequency) +}