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 }