194 lines
3.9 KiB
Go
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]
|
|
}
|