Files
adventofcode/2025/day10/main.go
2025-12-10 11:20:57 -06:00

251 lines
4.5 KiB
Go

package main
import (
"fmt"
"slices"
"strings"
"sync"
"github.com/draffensperger/golp"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
)
func main() {
inp := h.StdinToStringSlice()
part1(inp)
fmt.Println()
part2(inp)
}
func part1(inp []string) {
machines := parseInput(inp)
var ret int
for _, m := range machines {
r := m.Start()
ret += r
}
fmt.Println("# Part 1")
fmt.Println(ret)
}
func part2(inp []string) {
machines := parseInput(inp)
var ret int
var wg sync.WaitGroup
for _, m := range machines {
wg.Go(func() {
sol := m.SolveJoltage()
ret += sol
})
}
wg.Wait()
fmt.Println("# Part 2")
fmt.Println(ret)
}
func parseInput(inp []string) []*Machine {
var ret []*Machine
for i := range inp {
ret = append(ret, NewMachine(i, inp[i]))
}
return ret
}
type Machine struct {
id int
numLights int
lights int
wLights int
buttons [][]int
joltages []int
jCounters []int
}
func NewMachine(id int, inp string) *Machine {
ret := Machine{id: id}
pts := strings.Fields(inp)
for _, p := range pts {
in := p[1 : len(p)-1]
switch p[0] {
case '[':
var l []int
for i := range in {
if in[i] == '#' {
l = append(l, i)
}
}
ret.wLights = intsToBitmask(l)
ret.numLights = len(in)
case '(':
wP := strings.Split(in, ",")
var w []int
for i := range wP {
w = append(w, h.Atoi(wP[i]))
}
ret.buttons = append(ret.buttons, w)
case '{':
wJ := strings.Split(in, ",")
for i := range wJ {
ret.joltages = append(ret.joltages, h.Atoi(wJ[i]))
ret.jCounters = append(ret.jCounters, 0)
}
}
}
return &ret
}
// Start presses buttons until the machine is running
func (m *Machine) Start() int {
bMasks := []int{}
for _, b := range m.buttons {
bMasks = append(bMasks, intsToBitmask(b))
}
// Start at the end
endMask := 0
current := map[int]bool{m.wLights: true}
var ret int
for p := 1; p <= 1000; p++ {
nextSet := make(map[int]bool)
for c := range current {
for _, b := range bMasks {
nextSet[c^b] = true
}
}
current = nextSet
if _, exists := current[endMask]; exists {
return p
}
}
return ret
}
func (m *Machine) SolveJoltage() int {
if slices.Equal(m.jCounters, m.joltages) {
return 0
}
nB := len(m.buttons)
nJ := len(m.joltages)
lp := golp.NewLP(0, nB)
lp.SetVerboseLevel(golp.NEUTRAL)
objCoeffs := make([]float64, nB)
for i := range nB {
objCoeffs[i] = 1.0
}
lp.SetObjFn(objCoeffs)
for i := range nB {
lp.SetInt(i, true)
lp.SetBounds(i, 0.0, float64(1000))
}
for i := 0; i < nJ; i++ {
var entries []golp.Entry
for j, btn := range m.buttons {
if slices.Contains(btn, i) {
entries = append(entries, golp.Entry{Col: j, Val: 1.0})
}
}
targetValue := float64(m.joltages[i])
if err := lp.AddConstraintSparse(entries, golp.EQ, targetValue); err != nil {
panic(err)
}
}
status := lp.Solve()
if status != golp.OPTIMAL {
return 0
}
solution := lp.Variables()
totalPresses := 0
for _, val := range solution {
totalPresses += int(val + 0.5)
}
return totalPresses
}
func (m *Machine) Reset() { m.lights = 0 }
func (m Machine) String() string {
l := bitmaskToBytes(m.lights, m.numLights)
wl := bitmaskToBytes(m.wLights, m.numLights)
return fmt.Sprintf("[%s] -> [%s] %d", string(l), string(wl), m.wLights)
}
func intsToBitmask(i []int) int {
mask := 0
for _, v := range i {
mask |= 1 << v
}
return mask
}
func bitmaskToInts(in int, length int) []int {
var ret []int
for i := 0; i < length; i++ {
if in&1 == 1 {
ret = append(ret, i)
}
in = in >> 1
}
return ret
}
func bitmaskToBytes(in int, length int) []byte {
var ret []byte
for i := 1; i <= length; i++ {
if in&1 == 1 {
ret = append(ret, '#')
} else {
ret = append(ret, '.')
}
in = in >> 1
}
return ret
}
func elim(mtx [][]int) ([]int, [][]int) {
m := len(mtx)
if m == 0 {
return nil, nil
}
n := len(mtx[0]) - 1
pivCols := []int{}
cRow := 0
mat := make([][]int, m)
for i := range mtx {
mat[i] = make([]int, n+1)
copy(mat[i], mtx[i])
}
for col := 0; col < n && cRow < m; col++ {
pivRow := -1
for row := cRow; row < m; row++ {
if mat[row][col] != 0 {
pivRow = row
break
}
}
if pivRow == -1 {
continue
}
mat[cRow], mat[pivRow] = mat[pivRow], mat[cRow]
pivCols = append(pivCols, col)
for row := cRow + 1; row < m; row++ {
if mat[row][col] != 0 {
factor := mat[row][col]
pivVal := mat[cRow][col]
for j := col; j <= n; j++ {
mat[row][j] = mat[row][j]*pivVal - mat[cRow][j]*factor
}
}
}
cRow++
}
return pivCols, mat
}