13

Day 10: Factory

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
[-] Camille@lemmy.ml 5 points 1 month ago

Go

Linear programming all over again! I solved part 1 with a BFS like many. But for part 2 a proper solver was needed. I'm terrible at LP so I tried to use a simplex implementation, but it was not enough to find a solution, Z3 was needed but I couldn't bother to install anything so... screw it.

Here is my proposal part 2 doesn't work but the modelisation is interesting enough that it is worth posting:

day10.go

package main

import (
	"aoc/utils"
	"fmt"
	"math"
	"regexp"
	"slices"
	"strconv"
	"strings"
	"sync"

	"gonum.org/v1/gonum/mat"
	"gonum.org/v1/gonum/optimize/convex/lp"
)

type button []int8

type problem struct {
	want                int
	buttons             []button
	joltageRequirements []int8
}

func parseLights(lights string) (want int) {
	want = 0
	for idx, ch := range lights {
		if ch == '#' {
			want += (1 << idx)
		}
	}
	return want
}

func parseProblem(line string) problem {
	reg := regexp.MustCompile(`\[([.#]+)\] ((?:(?:\([^)]+\))\s*)+) {([^}]+)}`)
	matches := reg.FindStringSubmatch(line)

	lights := matches[1]
	buttonString := matches[2]
	joltage := matches[3]

	buttonString = string(slices.DeleteFunc(
		[]rune(buttonString),
		func(ch rune) bool {
			return ch == '(' || ch == ')'
		}))

	buttons := []button{}
	for switchString := range strings.SplitSeq(buttonString, " ") {
		switches := []int8{}
		for sw := range strings.SplitSeq(switchString, ",") {
			val, _ := strconv.Atoi(sw)
			switches = append(switches, int8(val))
		}
		buttons = append(buttons, switches)
	}

	joltageRequirements := []int8{}
	for req := range strings.SplitSeq(joltage, ",") {
		val, _ := strconv.Atoi(req)
		joltageRequirements = append(joltageRequirements, int8(val))
	}

	return problem{
		want:                parseLights(lights),
		buttons:             buttons,
		joltageRequirements: joltageRequirements,
	}
}

func getProblemChannel(input chan string) chan problem {
	ch := make(chan problem, cap(input))
	go func() {
		for line := range input {
			ch <- parseProblem(line)
		}
		close(ch)
	}()
	return ch
}

func (b button) value() (val int) {
	val = 0
	for _, sw := range b {
		val += 1 << sw
	}
	return val
}

func bfs(goal int, buttons []button) int {
	type bfsState struct {
		epoch int8
		value int
	}

	visited := []int{0}
	queue := []bfsState{}

	for _, bt := range buttons {
		value := bt.value()
		queue = append(queue, bfsState{epoch: 1, value: value})
		visited = append(visited, value)
	}

	for len(queue) > 0 {
		state := queue[0]
		queue = queue[1:]

		if state.value == goal {
			return int(state.epoch)
		}

		for _, bt := range buttons {
			nextValue := state.value ^ bt.value()
			if !slices.Contains(visited, nextValue) {
				visited = append(visited, nextValue)
				queue = append(queue, bfsState{
					epoch: state.epoch + 1,
					value: nextValue,
				})
			}
		}
	}

	return math.MaxInt
}

func (p problem) solve() int {
	depth := bfs(p.want, p.buttons)
	return int(depth)
}

func parallel(threadCount int, f func(thrId int)) {
	var wg sync.WaitGroup
	for id := range threadCount {
		wg.Go(func() {
			f(id)
		})
	}
	wg.Wait()
}

func stepOne(input chan string) (int, error) {
	sum := 0
	pbChan := getProblemChannel(input)
	for pb := range pbChan {
		depth := pb.solve()
		fmt.Println(depth)
		sum += depth
	}
	return sum, nil
}

type linearEquation struct {
	solution int
	coeffs   []int
}

func (p problem) toLinearEquations() linearProblem {
	equations := make([]linearEquation, len(p.joltageRequirements))
	varCount := len(p.buttons)

	for equIdx, solution := range p.joltageRequirements {
		equ := linearEquation{
			solution: int(solution),
			coeffs:   make([]int, varCount),
		}

		for btIdx, bt := range p.buttons {
			if slices.Contains(bt, int8(equIdx)) {
				equ.coeffs[btIdx] = 1
			}
		}
		equations[equIdx] = equ
	}
	return equations
}

type linearProblem []linearEquation

func (lp linearProblem) A() mat.Matrix {
	rows := len(lp)
	cols := len(lp[0].coeffs)
	data := make([]float64, rows*cols)

	for r := range rows {
		for c := range cols {
			data[r*cols+c] = float64(lp[r].coeffs[c])
		}
		fmt.Println(data[r*cols : (r+1)*cols])
	}

	return mat.NewDense(rows, cols, data)
}

func (lp linearProblem) c() []float64 {
	count := len(lp[0].coeffs)
	consts := make([]float64, count)
	for idx := range count {
		consts[idx] = 1.0
	}
	return consts
}

func (lp linearProblem) b() []float64 {
	bees := make([]float64, len(lp))
	for idx := range bees {
		bees[idx] = float64(lp[idx].solution)
	}
	return bees
}

func stepTwo(input chan string) (int, error) {
	sum := 0
	pbChan := getProblemChannel(input)
	for pb := range pbChan {
		lpb := pb.toLinearEquations()
		c := lpb.c()
		A := lpb.A()
		b := lpb.b()

		optF, _, err := lp.Simplex(c, A, b, 1.0, nil)
		if err != nil {
			fmt.Errorf("simplex error: %v\n", err)
		}
		fmt.Println(optF)
		sum += int(optF)
	}

	return sum, nil
}

func main() {
	input := utils.FilePath("day10.txt")
	utils.RunStep(utils.ONE, input, stepOne)
	utils.RunStep(utils.TWO, input, stepTwo)
}

this post was submitted on 10 Dec 2025
13 points (100.0% liked)

Advent Of Code

1217 readers
1 users here now

An unofficial home for the advent of code community on programming.dev! Other challenges are also welcome!

Advent of Code is an annual Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language you like.

Everybody Codes is another collection of programming puzzles with seasonal events.

EC 2025

AoC 2025

Solution Threads

M T W T F S S
1 2 3 4 5 6 7
8 9 10 11 12

Visualisations Megathread

Rules/Guidelines

Relevant Communities

Relevant Links

Credits

Icon base by Lorc under CC BY 3.0 with modifications to add a gradient

console.log('Hello World')

founded 2 years ago
MODERATORS