Go
God damn it, I thought I would never see the end of part 1. I had a hard time finding a good representation and then I failed at not eliminating valid distances etc. The code is quite messy, but I did it!!!
day08.go
package main
import (
"aoc/utils"
"cmp"
"math"
"slices"
"strconv"
"strings"
)
type pos3D struct {
x, y, z int
}
func (p pos3D) Compare(other pos3D) int {
dx := cmp.Compare(p.x, other.x)
if dx != 0 {
return dx
}
dy := cmp.Compare(p.y, other.y)
if dy != 0 {
return dy
}
return cmp.Compare(p.z, other.z)
}
func (p pos3D) distance(other pos3D) float64 {
dx := float64(other.x - p.x)
dy := float64(other.y - p.y)
dz := float64(other.z - p.z)
d2 := math.Pow(dx, 2.0) + math.Pow(dy, 2.0) + math.Pow(dz, 2.0)
return math.Sqrt(d2)
}
func getPosChannel(input chan string) chan pos3D {
ch := make(chan pos3D, cap(input))
go func() {
for line := range input {
parts := strings.Split(line, ",")
x, _ := strconv.Atoi(parts[0])
y, _ := strconv.Atoi(parts[1])
z, _ := strconv.Atoi(parts[2])
ch <- pos3D{x, y, z}
}
close(ch)
}()
return ch
}
type circuits struct {
circuits map[pos3D]int
nextID int
}
func (cc *circuits) newCircuit() (id int) {
id = cc.nextID
cc.nextID++
return id
}
func (cc *circuits) mergeCircuits(id1, id2 int) {
for p, id := range cc.circuits {
if id == id2 {
cc.circuits[p] = id1
}
}
}
func createSingletonCircuits(points []pos3D) (cc circuits) {
cc.circuits = make(map[pos3D]int)
for _, p := range points {
id := cc.newCircuit()
cc.circuits[p] = id
}
return cc
}
func (cc circuits) reverseMap() (m map[int][]pos3D) {
m = make(map[int][]pos3D)
for p, id := range cc.circuits {
if _, ok := m[id]; !ok {
m[id] = []pos3D{}
}
m[id] = append(m[id], p)
}
return m
}
func (cc circuits) sizeMap() map[int]int {
circuitSizeMap := make(map[int]int)
for _, id := range cc.circuits {
circuitSizeMap[id]++
}
return circuitSizeMap
}
type targetedDistance struct {
distance float64
p1, p2 pos3D
}
func (p pos3D) distanceWithAll(
points []pos3D,
alreadyVisited *map[pos3D]any,
) (tds []targetedDistance) {
tds = []targetedDistance{}
for _, op := range points {
if _, ok := (*alreadyVisited)[op]; ok {
continue
}
var td targetedDistance
td.distance = math.MaxFloat64
td.p1 = p
d := p.distance(op)
if d < td.distance {
td.distance = d
td.p2 = op
}
tds = append(tds, td)
}
return tds
}
type pointPair [2]pos3D
func (pp pointPair) equals(other pointPair) bool {
pp1 := pp[0]
pp2 := pp[1]
o1 := other[0]
o2 := other[1]
return (pp1 == o1 && pp2 == o2) || (pp1 == o2 && pp2 == o1)
}
var IterationCount = 1000
func stepOne(input chan string) (int, error) {
ch := getPosChannel(input)
points := []pos3D{}
for p := range ch {
points = append(points, p)
}
slices.SortFunc(points, pos3D.Compare)
cc := createSingletonCircuits(points)
alreadyVisited := make(map[pos3D]any)
tds := []targetedDistance{}
for _, p := range points {
alreadyVisited[p] = nil
dsts := p.distanceWithAll(points, &alreadyVisited)
tds = append(tds, dsts...)
}
slices.SortFunc(tds, func(a, b targetedDistance) int {
return cmp.Compare(a.distance, b.distance)
})
for idx := range IterationCount {
td := tds[idx]
cc.mergeCircuits(cc.circuits[td.p1], cc.circuits[td.p2])
}
circuitSizeMap := cc.sizeMap()
circuitSizes := []int{}
for _, v := range circuitSizeMap {
circuitSizes = append(circuitSizes, v)
}
slices.Sort(circuitSizes)
largestThree := circuitSizes[len(circuitSizes)-3:]
product := 1
for _, v := range largestThree {
product *= v
}
return product, nil
}
func stepTwo(input chan string) (int, error) {
ch := getPosChannel(input)
points := []pos3D{}
for p := range ch {
points = append(points, p)
}
slices.SortFunc(points, pos3D.Compare)
cc := createSingletonCircuits(points)
alreadyVisited := make(map[pos3D]any)
tds := []targetedDistance{}
for _, p := range points {
alreadyVisited[p] = nil
dsts := p.distanceWithAll(points, &alreadyVisited)
tds = append(tds, dsts...)
}
slices.SortFunc(tds, func(a, b targetedDistance) int {
return cmp.Compare(a.distance, b.distance)
})
idx := 0
var lastConnection pointPair
for {
td := tds[idx]
idx++
cc.mergeCircuits(cc.circuits[td.p1], cc.circuits[td.p2])
circuitSizeMap := cc.sizeMap()
if len(circuitSizeMap) == 1 {
lastConnection = pointPair{td.p1, td.p2}
break
}
}
return lastConnection[0].x * lastConnection[1].x, nil
}
func main() {
inputFile := utils.FilePath("day08.txt")
utils.RunStep(utils.ONE, inputFile, stepOne)
utils.RunStep(utils.TWO, inputFile, stepTwo)
}