194 lines
3.9 KiB
Go

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