218 lines
4.9 KiB
Go
218 lines
4.9 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
|
)
|
|
|
|
func main() {
|
|
inp := h.StdinToStringSlice()
|
|
|
|
ans1, ans2 := solve(inp)
|
|
fmt.Println("# Part 1")
|
|
fmt.Println(ans1)
|
|
fmt.Println()
|
|
fmt.Println("# Part 2")
|
|
fmt.Println(ans2)
|
|
}
|
|
|
|
func solve(input []string) (int, int) {
|
|
scanners := parseScanners(input)
|
|
|
|
solved := []Scanner{scanners[0]}
|
|
solved[0].absoluteCoords = solved[0].relativeCoords
|
|
solved[0].fillAbsMap()
|
|
|
|
// Scanners with a position we don't know yet
|
|
unk := scanners[1:]
|
|
// loop until we know them all
|
|
for len(unk) > 0 {
|
|
fmt.Print(h.CLEAR_SCREEN, "Processing...\n")
|
|
c, t := len(solved), len(scanners)
|
|
h.PrintProgress(c, t)
|
|
fmt.Printf(" %d/%d %d%%\n", c, t, int(float64(c)/float64(t)*100))
|
|
|
|
for i, undet := range unk {
|
|
ok := undet.findCoords(solved)
|
|
if ok {
|
|
solved = append(solved, undet)
|
|
// remove the determined scanner from the unknown list
|
|
copy(unk[i:], unk[i+1:])
|
|
unk = unk[:len(unk)-1]
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
allBeacons := map[[3]int]bool{}
|
|
for _, s := range solved {
|
|
for c := range s.absoluteCoordsMap {
|
|
allBeacons[c] = true
|
|
}
|
|
}
|
|
|
|
var furthest int
|
|
for i, s1 := range solved {
|
|
for j, s2 := range solved {
|
|
if i == j {
|
|
continue
|
|
}
|
|
dist := h.Abs(s1.x-s2.x) + h.Abs(s1.y-s2.y) + h.Abs(s1.z-s2.z)
|
|
if dist > furthest {
|
|
furthest = dist
|
|
}
|
|
}
|
|
}
|
|
return len(allBeacons), furthest
|
|
}
|
|
|
|
func parseScanners(inp []string) []Scanner {
|
|
var scanners []Scanner
|
|
for i := range inp {
|
|
if strings.HasPrefix(inp[i], "---") {
|
|
end := i
|
|
for ; end < len(inp); end++ {
|
|
if inp[end] == "" {
|
|
break
|
|
}
|
|
}
|
|
wrk, err := NewScannerFromInput(inp[i:end])
|
|
if err == nil {
|
|
scanners = append(scanners, wrk)
|
|
} else {
|
|
fmt.Println("Error parsing scanners")
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
}
|
|
return scanners
|
|
}
|
|
|
|
type Scanner struct {
|
|
number int
|
|
x, y, z int
|
|
relativeCoords [][3]int
|
|
rotations [][][3]int
|
|
absoluteCoords [][3]int
|
|
absoluteCoordsMap map[[3]int]bool
|
|
}
|
|
|
|
func NewScannerFromInput(inp []string) (Scanner, error) {
|
|
s := Scanner{
|
|
absoluteCoordsMap: make(map[[3]int]bool),
|
|
}
|
|
r := strings.NewReader(inp[0])
|
|
_, err := fmt.Fscanf(r, "--- scanner %d ---", &s.number)
|
|
if err != nil {
|
|
return s, errors.New("No Scanner ID")
|
|
}
|
|
for _, v := range inp[1:] {
|
|
var x, y, z int
|
|
r = strings.NewReader(v)
|
|
_, err := fmt.Fscanf(r, "%d,%d,%d", &x, &y, &z)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.relativeCoords = append(s.relativeCoords, [3]int{x, y, z})
|
|
}
|
|
s.fillRotations()
|
|
return s, nil
|
|
}
|
|
|
|
func (s *Scanner) fillAbsMap() {
|
|
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
|
|
}
|
|
}
|
|
|
|
// Fill in all coordinates for all beacon rotations
|
|
func (s *Scanner) fillRotations() {
|
|
dir1 := s.relativeCoords
|
|
var dir2, dir3, dir4, dir5, dir6 [][3]int
|
|
for _, c := range dir1 {
|
|
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{
|
|
dir1, dir2,
|
|
dir3, dir4,
|
|
dir5, dir6,
|
|
}
|
|
|
|
var finalRotations [][][3]int
|
|
for _, rotation := range sixRotations {
|
|
var r2, r3, r4 [][3]int
|
|
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
|
|
}
|
|
|
|
// Find the absolute coordinates for this scanner, if we can.
|
|
func (s *Scanner) findCoords(solved []Scanner) bool {
|
|
for _, rotatedCoords := range s.rotations {
|
|
for _, set := range solved {
|
|
for _, absCoord := range set.absoluteCoords {
|
|
for _, relativeCoord := range rotatedCoords {
|
|
unsolvedAbsoluteCoords := fillAbsCoords(absCoord, relativeCoord, rotatedCoords)
|
|
|
|
var matchingCount int
|
|
for _, ac := range unsolvedAbsoluteCoords {
|
|
if set.absoluteCoordsMap[ac] {
|
|
matchingCount++
|
|
}
|
|
}
|
|
|
|
if matchingCount >= 12 {
|
|
s.relativeCoords = rotatedCoords
|
|
s.absoluteCoords = unsolvedAbsoluteCoords
|
|
s.fillAbsMap()
|
|
s.x = absCoord[0] - relativeCoord[0]
|
|
s.y = absCoord[1] - relativeCoord[1]
|
|
s.z = absCoord[2] - relativeCoord[2]
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Assume that absolute and relative are actually the same coordinate, calc the diff of all relative Coords
|
|
func fillAbsCoords(absolute, relative [3]int, relativeCoords [][3]int) [][3]int {
|
|
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
|
|
}
|