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
 | |
| }
 |