Day 8: Playground

Megathread guidelines

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL

FAQ

you are viewing a single comment's thread
view the rest of the comments
[–] 3 points 9 months ago

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

  • source