mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-05-18 19:13:27 +02:00
175 lines
4.3 KiB
Go
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
|
|
}
|