package main import ( "bufio" "fmt" "log" "os" "strconv" "strings" ) func die(err error) { log.Fatal(err) } func check(err error) { if err != nil { die(err) } } func main() { solve("sample") solve("input") } // Brilliant insight from a redditor: // suppose we only want to match the parity of the required joltages. // we can use our solution for part 1 to find the minimum number of button presses // to make that happen. now, consider what happens if we press a button twice: // it won't change the parity at all, only add 2 to some joltages. ok. // take the 2nd bit of each joltage. what's the minimum number of double-presses // required to match that pattern? and so on. func solve(filename string) { input, err := os.Open(filename) check(err) scanner := bufio.NewScanner(input) scanner.Split(bufio.ScanLines) total := 0 for scanner.Scan() { line := scanner.Text() parts := strings.Fields(line) parts = parts[1:] var buts []uint32 var target Jolts for _, p := range parts { j, err := parseJolt(p) check(err) if p[0] == '{' { target = j } else { buts = append(buts, j.uint32()) } } fmt.Printf("%v %b %v\n", target, buts, parts) n := solveJolts(target, buts) fmt.Printf("%s = %v\n", line, n) total += n //fmt.Println(n) } check(scanner.Err()) fmt.Println(total) } type Jolts [10]int16 func parseJolt(s string) (Jolts, error) { if s[0] == '(' { return parseJoltByIndex(s) } else if s[0] == '{' { return parseJoltByValue(s) } return Jolts{}, fmt.Errorf("invalid jolts: %q", s) } func parseJoltByIndex(s string) (Jolts, error) { var j Jolts s = strings.Trim(s, "()") for _, part := range strings.Split(s, ",") { idx, err := strconv.ParseInt(part, 10, 16) if err != nil { return j, err } j[idx] = 1 } return j, nil } func parseJoltByValue(s string) (Jolts, error) { var j Jolts s = strings.Trim(s, "{}") for i, part := range strings.Split(s, ",") { val, err := strconv.ParseInt(part, 10, 16) if err != nil { return j, err } j[i] = int16(val) } return j, nil } func (j *Jolts) uint32() (u uint32) { for i, v := range j { if v != 0 { u |= uint32(1) << i } } return u } func (j Jolts) Sub(m uint32, value int) Jolts { for i := range j { if m>>i&1 != 0 { j[i] -= int16(value) } } return j } func (j Jolts) Valid() bool { for i := range j { if j[i] < 0 { return false } } return true } // TODO: this isn't quite enough. we need to know not only how many button presses // a mask can be solved in, but also *the specific buttons* -- because even though // each button is pressed at most once, two buttons can be connected to the same // output so the joltages can be between like 0-10. this needs to be subtracted // from the target, and two different button patterns can have different effects // on the target. // // the good news is that we can cache and reuse the cost map computed in best(), // since it depends only on the button wiring. all that's left after that is a // graph search through the state space, using masks to prune each level. func solveJolts(target Jolts, pool []uint32) int { var bits uint32 for _, x := range target { bits |= uint32(x) } var answer int for bit := uint(0); bits>>bit > 0; bit++ { var mask uint32 for i := range target { mask |= uint32(target[i]) >> bit << i } if mask == 0 { continue } n := best(mask, pool) answer += n << bit //for i, p := range pool { // if mask>>i&1 != 0 { // target.Sub(p, 1) // } //} } return answer } func best(target uint32, pool []uint32) int { var cost = make(map[uint32]int) var states = []uint32{0} cost[0] = 0 for _, p := range pool { // enumerate all the states that can be reached by toggling button p for _, s := range states { t := s ^ p c := cost[t] + 1 if cost_t, ok := cost[t]; ok { if c < cost_t { cost[t] = c } } else { cost[t] = c states = append(states, t) } } } return cost[target] }