Files
advent-of-code-go/2022/day15/main.go
T
2022-12-25 12:21:02 -05:00

175 lines
4.3 KiB
Go

package main
import (
_ "embed"
"flag"
"fmt"
"strings"
"github.com/alexchao26/advent-of-code-go/mathy"
"github.com/alexchao26/advent-of-code-go/util"
)
//go:embed input.txt
var input string
func init() {
// do this in init (not main) so test file has same input
input = strings.TrimRight(input, "\n")
if len(input) == 0 {
panic("empty input.txt file")
}
}
func main() {
var part int
flag.IntVar(&part, "part", 1, "part 1 or 2")
flag.Parse()
fmt.Println("Running part", part)
if part == 1 {
ans := part1(input, 2000000)
util.CopyToClipboard(fmt.Sprintf("%v", ans))
fmt.Println("Output:", ans)
} else {
ans := part2(input, 4000000)
util.CopyToClipboard(fmt.Sprintf("%v", ans))
fmt.Println("Output:", ans)
}
}
// x is col, y is row
// unbounded grid...
// In the row where y=2000000, how many positions canNOT contain a beacon?
//
// very naive approach of marking each coord that is visible from some sensor
// then remove all beacons that are on the target row
// then just return the length of the map containing "seen" cells on target row
//
// this is brutally slow, no way this approach works for part 2
func part1(input string, targetRow int) int {
pairs := parseInput(input)
blockedCoords := map[[2]int]bool{}
for _, p := range pairs {
manhattanDist := mathy.ManhattanDistance(p.beaconRow, p.beaconCol,
p.sensorRow, p.sensorCol)
// if target row is reachable, block coords on it...
blockable := manhattanDist - mathy.AbsInt(p.sensorRow-targetRow)
if blockable > 0 {
for i := 0; i <= blockable; i++ {
// add blocks to map in both left and right directions
blockedCoords[[2]int{
targetRow, p.sensorCol - i,
}] = true
blockedCoords[[2]int{
targetRow, p.sensorCol + i,
}] = true
}
}
}
// remove any beacons that are present in the input?
for _, p := range pairs {
delete(blockedCoords, [2]int{p.beaconRow, p.beaconCol})
}
return len(blockedCoords)
}
func part2(input string, coordLimit int) int {
pairs := parseInput(input)
sensors := []parsedSensor{}
for _, p := range pairs {
sensors = append(sensors, parsedSensor{
sensorRow: p.sensorRow,
sensorCol: p.sensorCol,
manhattanDist: mathy.ManhattanDistance(p.sensorCol, p.sensorRow,
p.beaconCol, p.beaconRow),
})
}
// search space is too large to iterate over the entire thing and check if
// SOME sensor can see that location...
//
// we can assume that the final resting point will be 1 cell away from the
// border of a (actually multiple) sensor. this runs under the assumption
// that there is only one answer
for _, sensor := range sensors {
distPlusOne := sensor.manhattanDist + 1
// checking in this pattern w/ manhattan distance of 1
// 1
// 2 3
// 4 S 5
// 6B7
// 8
for r := -distPlusOne; r <= distPlusOne; r++ {
targetRow := sensor.sensorRow + r
if targetRow < 0 {
continue
}
if targetRow > coordLimit {
break
}
// check left and right on the target row
// zero for first and last r's... then subtract or add it from the
// sensor's col
colOffset := distPlusOne - mathy.AbsInt(r)
colLeft := sensor.sensorCol - colOffset
colRight := sensor.sensorCol + colOffset
if colLeft >= 0 && colLeft <= coordLimit &&
!isReachable(sensors, colLeft, targetRow) {
return colLeft*4000000 + targetRow
}
if colRight >= 0 && colRight <= coordLimit &&
!isReachable(sensors, colRight, targetRow) {
return colRight*4000000 + targetRow
}
}
}
panic("unreachable")
}
type pair struct {
sensorRow, sensorCol int
beaconRow, beaconCol int
}
func parseInput(input string) (ans []pair) {
// Sensor at x=2150774, y=3136587: closest beacon is at x=2561642, y=2914773
for _, line := range strings.Split(input, "\n") {
p := pair{}
_, err := fmt.Sscanf(line,
"Sensor at x=%d, y=%d: closest beacon is at x=%d, y=%d",
&p.sensorCol, &p.sensorRow, &p.beaconCol, &p.beaconRow)
if err != nil {
panic("parsing: " + err.Error())
}
ans = append(ans, p)
}
return ans
}
type parsedSensor struct {
sensorRow, sensorCol int
manhattanDist int
}
func isReachable(sensors []parsedSensor, c, r int) bool {
for _, sensor := range sensors {
// if reachable, break
if sensor.manhattanDist >= mathy.ManhattanDistance(c, r,
sensor.sensorCol, sensor.sensorRow) {
return true
}
}
return false
}