day 10 part 2 progress!

almost there, just 2 stubborn instances that we can't solve and i think i
know why.
This commit is contained in:
magical 2025-12-17 22:10:28 +00:00
parent 5fd80e56d8
commit 17ce57943c

View File

@ -2,8 +2,10 @@ package main
import ( import (
"bufio" "bufio"
"container/heap"
"fmt" "fmt"
"log" "log"
"math/bits"
"os" "os"
"strconv" "strconv"
"strings" "strings"
@ -39,6 +41,7 @@ func solve(filename string) {
scanner := bufio.NewScanner(input) scanner := bufio.NewScanner(input)
scanner.Split(bufio.ScanLines) scanner.Split(bufio.ScanLines)
total := 0 total := 0
errors := 0
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
parts := strings.Fields(line) parts := strings.Fields(line)
@ -57,11 +60,19 @@ func solve(filename string) {
fmt.Printf("%v %b %v\n", target, buts, parts) fmt.Printf("%v %b %v\n", target, buts, parts)
n := solveJolts(target, buts) n := solveJolts(target, buts)
fmt.Printf("%s = %v\n", line, n) fmt.Printf("%s = %v\n", line, n)
total += n fmt.Println()
if n >= 0 {
total += n
} else {
errors += 1
}
//fmt.Println(n) //fmt.Println(n)
} }
check(scanner.Err()) check(scanner.Err())
fmt.Println(total) fmt.Println(total)
if errors > 0 {
fmt.Printf("error! failed to solve %d instances\n", errors)
}
} }
@ -111,15 +122,19 @@ func (j *Jolts) uint32() (u uint32) {
return u return u
} }
func (j Jolts) Sub(m uint32, value int) Jolts { func (j Jolts) Add(m uint32, value int) Jolts {
for i := range j { for i := range j {
if m>>i&1 != 0 { if m>>i&1 != 0 {
j[i] -= int16(value) j[i] += int16(value)
} }
} }
return j return j
} }
func (j Jolts) Sub(m uint32, value int) Jolts {
return j.Add(m, -value)
}
func (j Jolts) Valid() bool { func (j Jolts) Valid() bool {
for i := range j { for i := range j {
if j[i] < 0 { if j[i] < 0 {
@ -129,6 +144,31 @@ func (j Jolts) Valid() bool {
return true return true
} }
func (j Jolts) Less(k Jolts) bool {
for i := range j {
if j[i] != k[i] {
return j[i] < k[i]
}
}
return false // equal
}
func (j Jolts) BitLength() int {
var total uint32
for _, x := range j {
total |= uint32(x)
}
return bits.Len32(total)
}
func (j Jolts) BitSlice(bit int) uint32 {
var mask uint32
for i, x := range j {
mask |= (uint32(x) >> uint(bit)) & 1 << uint(i)
}
return mask
}
// TODO: this isn't quite enough. we need to know not only how many button presses // 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 // 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 // each button is pressed at most once, two buttons can be connected to the same
@ -140,24 +180,68 @@ func (j Jolts) Valid() bool {
// since it depends only on the button wiring. all that's left after that is a // 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. // graph search through the state space, using masks to prune each level.
// suppose we have four buttons whose bitmasks are 110, 101, 011, and 100.
// suppose our target jolts are [2 1 1], corresponding to bitmasks 100x2 and 011x1.
// the shortest solution for the low bits (011) is to press button 3 once. but then we need
// to press button 4 twice to get the 2nd bits. that's 3 total button presses.
// a more efficient solution is to press button 1 once and button 2 once (110+101 = 211),
// for 2 total button presses.
// this is a potential issue whenever we have a set of buttons which aren't linearly independent
// (e.g. button 1 ^ 2 ^ 3 = button 4). we know this to be the case in some of the inputs.
func solveJolts(target Jolts, pool []uint32) int { func solveJolts(target Jolts, pool []uint32) int {
var bits uint32 var bitcost = new(state)
for _, x := range target { bitcost.init(pool)
bits |= uint32(x) for mask, vals := range bitcost.nodes {
fmt.Printf("%b: %v\n", mask, vals)
} }
var answer int
for bit := uint(0); bits>>bit > 0; bit++ { bits := target.BitLength()
var mask uint32
for i := range target { qu := newHeap(target)
mask |= uint32(target[i]) >> bit << i var zero Jolts
addstates := func(t, n Node, bit int) Node {
t.cost += n.cost << bit
t.mask += 1
for i := range t.jolts {
t.jolts[i] -= n.jolts[i] << bit
} }
return t
}
ntargets := len(target)
for ntargets > 1 && target[ntargets-1] == 0 {
ntargets--
}
seen := make(map[Jolts]bool)
for qu.Len() > 0 {
this := heap.Pop(qu).(Node)
log.Printf("sj: %v, len(q) = %v", this, qu.Len()+1)
if this.jolts == zero {
return int(this.cost)
}
seen[this.jolts] = true
bit := int(this.mask)
if bit >= int(bits) {
continue
}
//mask := masks[this.mask]
mask := this.jolts.BitSlice(bit)
log.Printf("jolts = %v, bit = %d, mask = %0*b", this.jolts, bit, ntargets, mask)
if mask == 0 { if mask == 0 {
seen[this.jolts] = false
this.mask += 1
qu.AddNode(this)
continue continue
} }
n := best(mask, pool) for _, n := range bitcost.best(mask) {
answer += n << bit t := addstates(this, n, int(bit))
if t.jolts.Valid() && !seen[t.jolts] {
qu.AddNode(t)
}
}
//for i, p := range pool { //for i, p := range pool {
// if mask>>i&1 != 0 { // if mask>>i&1 != 0 {
@ -165,29 +249,120 @@ func solveJolts(target Jolts, pool []uint32) int {
// } // }
//} //}
} }
return answer log.Printf("%v: no solution found\n", target)
return -1
} }
func best(target uint32, pool []uint32) int { type Node struct {
var cost = make(map[uint32]int) mask uint32
var states = []uint32{0} buttons uint32
cost[0] = 0 cost int32
for _, p := range pool { jolts Jolts
// 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 { func (n Node) Add(mask uint32) Node {
if c < cost_t { n.mask ^= mask
cost[t] = c n.cost += 1
n.jolts = n.jolts.Add(mask, 1)
return n
}
//func (n *Node) cost() int { return bits.OnesCount(n.buttons) }
type Heap struct {
heap []Node
idx map[Jolts]int
}
// these functions are for the heap.Interface interface
// start
func (h *Heap) Len() int { return len(h.heap) }
func (h *Heap) Less(i, j int) bool {
if h.heap[i].cost != h.heap[j].cost {
return h.heap[i].cost < h.heap[j].cost
}
return h.heap[i].jolts.Less(h.heap[j].jolts)
}
func (h *Heap) Swap(i, j int) {
h.heap[i], h.heap[j] = h.heap[j], h.heap[i]
h.idx[h.heap[i].jolts] = i
h.idx[h.heap[j].jolts] = j
}
func (h *Heap) Push(x any) {
n := x.(Node)
h.heap = append(h.heap, n)
h.idx[n.jolts] = len(h.heap) - 1
}
func (h *Heap) Pop() any {
old := h.heap
n := len(old)
x := old[n-1]
h.heap = old[0 : n-1]
delete(h.idx, x.jolts)
return x
}
// end
func (h *Heap) AddNode(t Node) {
defer func() {
if e := recover(); e != nil {
log.Println(h.heap)
log.Println(h.idx)
panic(e)
}
}()
if i, ok := h.idx[t.jolts]; ok {
old_cost := h.heap[i].cost
if t.cost < old_cost {
h.heap[i].cost = t.cost
heap.Fix(h, i)
} else {
// nothing; don't add node with higher cost to the queue
}
} else {
heap.Push(h, t)
}
}
func newHeap(start Jolts) *Heap {
var idx = make(map[Jolts]int)
idx[start] = 0
return &Heap{[]Node{{jolts: start}}, idx}
}
type state struct {
nodes map[uint32][]Node
}
func (s *state) init(pool []uint32) {
qu := newHeap(Jolts{})
best := make(map[uint32][]Node)
seen := make(map[uint32]bool)
for qu.Len() > 0 {
s := heap.Pop(qu).(Node)
best[s.mask] = append(best[s.mask], s)
// enumerate all the states that can be reached by toggling button p
for b, p := range pool {
if s.buttons>>b&1 == 0 {
t := s.Add(p)
t.buttons |= uint32(1) << b
if !seen[t.buttons] {
heap.Push(qu, t)
seen[t.buttons] = true
} }
} else { //qu.AddNode(t)
cost[t] = c
states = append(states, t)
} }
} }
} }
s.nodes = best
return cost[target] }
func (s *state) best(target uint32) []Node {
return s.nodes[target]
} }