mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-05-19 03:23:27 +02:00
333 lines
8.2 KiB
Go
333 lines
8.2 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)
|
|
|
|
ans1, ans2 := part1(input)
|
|
if part == 1 {
|
|
util.CopyToClipboard(fmt.Sprintf("%v", ans1))
|
|
fmt.Println("Output:", ans1)
|
|
} else {
|
|
util.CopyToClipboard(fmt.Sprintf("%v", ans2))
|
|
fmt.Println("Output:", ans2)
|
|
}
|
|
}
|
|
|
|
func part1(input string) (part1, part2 int) {
|
|
scanners := parseInput(input)
|
|
|
|
// determine scanner locations by finding scanners that see 12 of the same beacons
|
|
// everything will be relative from scanner 0, so it is "settled" and its abs & rel coords are the same
|
|
settled := []scanner{scanners[0]}
|
|
settled[0].absoluteCoords = settled[0].relativeCoords
|
|
settled[0].fillAbsoluteCoordsMap()
|
|
|
|
// create helper functions that can create all the rotated versions of the seen beacons
|
|
// scanner 0 will have KNOWN coordinates (0,0,0)
|
|
// maintain a list of all UNKNOWN scanners (all but 0 at the start)
|
|
undetermined := scanners[1:]
|
|
// iterate while it has a non zero length
|
|
for len(undetermined) > 0 {
|
|
fmt.Printf("progress: %d/%d\n", len(settled), len(scanners))
|
|
|
|
for i, undet := range undetermined {
|
|
maybeUpdated, ok := findAbsoluteCoordsForScanner(undet, settled)
|
|
if ok {
|
|
settled = append(settled, maybeUpdated)
|
|
// remove the determined scanner from undetermined list
|
|
copy(undetermined[i:], undetermined[i+1:])
|
|
// undetermined[i] = undetermined[len(undetermined)-1]
|
|
undetermined = undetermined[:len(undetermined)-1]
|
|
// restart checks from start of undetermined list
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
allBeacons := map[[3]int]bool{}
|
|
for _, s := range settled {
|
|
for c := range s.absoluteCoordsMap {
|
|
allBeacons[c] = true
|
|
}
|
|
}
|
|
|
|
var furthest int
|
|
for i, s1 := range settled {
|
|
for j, s2 := range settled {
|
|
if i == j {
|
|
continue
|
|
}
|
|
manhattanDist := mathy.AbsInt(s1.x-s2.x) + mathy.AbsInt(s1.y-s2.y) + mathy.AbsInt(s1.z-s2.z)
|
|
if manhattanDist > furthest {
|
|
furthest = manhattanDist
|
|
}
|
|
}
|
|
}
|
|
return len(allBeacons), furthest
|
|
}
|
|
|
|
type scanner struct {
|
|
number int
|
|
x, y, z int
|
|
relativeCoords [][3]int
|
|
rotations [][][3]int
|
|
absoluteCoords [][3]int
|
|
absoluteCoordsMap map[[3]int]bool
|
|
}
|
|
|
|
func (s *scanner) fillAbsoluteCoordsMap() {
|
|
s.absoluteCoordsMap = map[[3]int]bool{}
|
|
if len(s.absoluteCoords) == 0 {
|
|
panic(fmt.Sprintf("absolute coords not set for scanner %d", s.number))
|
|
}
|
|
for _, ac := range s.absoluteCoords {
|
|
s.absoluteCoordsMap[ac] = true
|
|
}
|
|
}
|
|
|
|
// create the 24 rotations given a slice of coords (3-length arrays)
|
|
func (s *scanner) fillRotations() {
|
|
// facing negative x
|
|
posX := s.relativeCoords
|
|
var dir2, dir3, dir4, dir5, dir6 [][3]int
|
|
for _, c := range posX {
|
|
x, y, z := c[0], c[1], c[2]
|
|
dir2 = append(dir2, [3]int{x, -y, -z})
|
|
dir3 = append(dir3, [3]int{x, -z, y})
|
|
dir4 = append(dir4, [3]int{-y, -z, x})
|
|
dir5 = append(dir5, [3]int{-x, -z, -y})
|
|
dir6 = append(dir6, [3]int{y, -z, -x})
|
|
}
|
|
sixRotations := [][][3]int{
|
|
posX, dir2,
|
|
dir3, dir4,
|
|
dir5, dir6,
|
|
}
|
|
|
|
// apply 4 rotations around the axis that the scanner is "staring down"
|
|
var finalRotations [][][3]int
|
|
for _, rotation := range sixRotations {
|
|
var r2, r3, r4 [][3]int // r1 is rotation itself
|
|
for _, c := range rotation {
|
|
x, y, z := c[0], c[1], c[2]
|
|
r2 = append(r2, [3]int{-y, x, z})
|
|
r3 = append(r3, [3]int{-x, -y, z})
|
|
r4 = append(r4, [3]int{y, -x, z})
|
|
}
|
|
finalRotations = append(finalRotations, rotation, r2, r3, r4)
|
|
}
|
|
s.rotations = finalRotations
|
|
}
|
|
|
|
func findAbsoluteCoordsForScanner(undet scanner, settled []scanner) (maybeUpdated scanner, didUpdate bool) {
|
|
// for all orientations of the unknown's beacon list
|
|
for _, rotatedCoords := range undet.rotations {
|
|
// for each beacon in known list
|
|
for _, set := range settled {
|
|
for _, absCoord := range set.absoluteCoords {
|
|
// for each beacon in unknown list
|
|
for _, relativeCoord := range rotatedCoords {
|
|
// assume the known and unknown beacon are the same, calculate the absolute coords of the unknown's scanner coords
|
|
// convert all of unknown list to their absolute coords, check against known list
|
|
unsettledAbsoluteCoords := makeAbsoluteCoordsList(absCoord, relativeCoord, rotatedCoords)
|
|
|
|
var matchingCount int
|
|
// var matched [][3]int // !
|
|
for _, ac := range unsettledAbsoluteCoords {
|
|
if set.absoluteCoordsMap[ac] {
|
|
// matched = append(matched, ac) // !
|
|
matchingCount++
|
|
}
|
|
}
|
|
|
|
// if true return a true or something, modify the scanner param pointer
|
|
if matchingCount >= 12 {
|
|
undet.relativeCoords = rotatedCoords
|
|
undet.absoluteCoords = unsettledAbsoluteCoords
|
|
undet.fillAbsoluteCoordsMap()
|
|
undet.x = absCoord[0] - relativeCoord[0]
|
|
undet.y = absCoord[1] - relativeCoord[1]
|
|
undet.z = absCoord[2] - relativeCoord[2]
|
|
return undet, true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// did not update
|
|
return undet, false
|
|
}
|
|
|
|
func makeAbsoluteCoordsList(absolute, relative [3]int, relativeCoords [][3]int) [][3]int {
|
|
// assuming absolute and relative are pointing to the same coord
|
|
// generate the all of the abolute coords
|
|
|
|
// diff to the scanner's coordinates, then calculate all beacons from scanner's coords
|
|
diff := [3]int{
|
|
absolute[0] - relative[0],
|
|
absolute[1] - relative[1],
|
|
absolute[2] - relative[2],
|
|
}
|
|
|
|
var absCoords [][3]int
|
|
for _, c := range relativeCoords {
|
|
absCoords = append(absCoords, [3]int{
|
|
diff[0] + c[0],
|
|
diff[1] + c[1],
|
|
diff[2] + c[2],
|
|
})
|
|
}
|
|
|
|
return absCoords
|
|
}
|
|
|
|
func parseInput(input string) (ans []scanner) {
|
|
for _, rawScanner := range strings.Split(input, "\n\n") {
|
|
var number int
|
|
lines := strings.Split(rawScanner, "\n")
|
|
_, err := fmt.Sscanf(lines[0], "--- scanner %d ---", &number)
|
|
if err != nil {
|
|
panic("parsing error " + err.Error())
|
|
}
|
|
|
|
var coords [][3]int
|
|
for _, line := range lines[1:] {
|
|
var x, y, z int
|
|
_, err := fmt.Sscanf(line, "%d,%d,%d", &x, &y, &z)
|
|
if err != nil {
|
|
panic("parsing error " + err.Error())
|
|
}
|
|
coords = append(coords, [3]int{x, y, z})
|
|
}
|
|
|
|
sc := scanner{
|
|
number: number,
|
|
x: 0,
|
|
y: 0,
|
|
z: 0,
|
|
relativeCoords: coords,
|
|
absoluteCoords: nil,
|
|
absoluteCoordsMap: map[[3]int]bool{},
|
|
}
|
|
sc.fillRotations()
|
|
ans = append(ans, sc)
|
|
}
|
|
|
|
return ans
|
|
}
|
|
|
|
// var exampleBeaconsList [][3]int = func() [][3]int {
|
|
// var coords [][3]int
|
|
// for _, l := range strings.Split(rawBeaconsList, "\n") {
|
|
// var x, y, z int
|
|
// fmt.Sscanf(l, "%d,%d,%d", &x, &y, &z)
|
|
// coords = append(coords, [3]int{x, y, z})
|
|
// }
|
|
// return coords
|
|
// }()
|
|
|
|
// var rawBeaconsList = `-892,524,684
|
|
// -876,649,763
|
|
// -838,591,734
|
|
// -789,900,-551
|
|
// -739,-1745,668
|
|
// -706,-3180,-659
|
|
// -697,-3072,-689
|
|
// -689,845,-530
|
|
// -687,-1600,576
|
|
// -661,-816,-575
|
|
// -654,-3158,-753
|
|
// -635,-1737,486
|
|
// -631,-672,1502
|
|
// -624,-1620,1868
|
|
// -620,-3212,371
|
|
// -618,-824,-621
|
|
// -612,-1695,1788
|
|
// -601,-1648,-643
|
|
// -584,868,-557
|
|
// -537,-823,-458
|
|
// -532,-1715,1894
|
|
// -518,-1681,-600
|
|
// -499,-1607,-770
|
|
// -485,-357,347
|
|
// -470,-3283,303
|
|
// -456,-621,1527
|
|
// -447,-329,318
|
|
// -430,-3130,366
|
|
// -413,-627,1469
|
|
// -345,-311,381
|
|
// -36,-1284,1171
|
|
// -27,-1108,-65
|
|
// 7,-33,-71
|
|
// 12,-2351,-103
|
|
// 26,-1119,1091
|
|
// 346,-2985,342
|
|
// 366,-3059,397
|
|
// 377,-2827,367
|
|
// 390,-675,-793
|
|
// 396,-1931,-563
|
|
// 404,-588,-901
|
|
// 408,-1815,803
|
|
// 423,-701,434
|
|
// 432,-2009,850
|
|
// 443,580,662
|
|
// 455,729,728
|
|
// 456,-540,1869
|
|
// 459,-707,401
|
|
// 465,-695,1988
|
|
// 474,580,667
|
|
// 496,-1584,1900
|
|
// 497,-1838,-617
|
|
// 527,-524,1933
|
|
// 528,-643,409
|
|
// 534,-1912,768
|
|
// 544,-627,-890
|
|
// 553,345,-567
|
|
// 564,392,-477
|
|
// 568,-2007,-577
|
|
// 605,-1665,1952
|
|
// 612,-1593,1893
|
|
// 630,319,-379
|
|
// 686,-3108,-505
|
|
// 776,-3184,-501
|
|
// 846,-3110,-434
|
|
// 1135,-1161,1235
|
|
// 1243,-1093,1063
|
|
// 1660,-552,429
|
|
// 1693,-557,386
|
|
// 1735,-437,1738
|
|
// 1749,-1800,1813
|
|
// 1772,-405,1572
|
|
// 1776,-675,371
|
|
// 1779,-442,1789
|
|
// 1780,-1548,337
|
|
// 1786,-1538,337
|
|
// 1847,-1591,415
|
|
// 1889,-1729,1762
|
|
// 1994,-1805,1792`
|