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 (
"bufio"
"container/heap"
"fmt"
"log"
"math/bits"
"os"
"strconv"
"strings"
@ -39,6 +41,7 @@ func solve(filename string) {
scanner := bufio.NewScanner(input)
scanner.Split(bufio.ScanLines)
total := 0
errors := 0
for scanner.Scan() {
line := scanner.Text()
parts := strings.Fields(line)
@ -57,11 +60,19 @@ func solve(filename string) {
fmt.Printf("%v %b %v\n", target, buts, parts)
n := solveJolts(target, buts)
fmt.Printf("%s = %v\n", line, n)
total += n
fmt.Println()
if n >= 0 {
total += n
} else {
errors += 1
}
//fmt.Println(n)
}
check(scanner.Err())
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
}
func (j Jolts) Sub(m uint32, value int) Jolts {
func (j Jolts) Add(m uint32, value int) Jolts {
for i := range j {
if m>>i&1 != 0 {
j[i] -= int16(value)
j[i] += int16(value)
}
}
return j
}
func (j Jolts) Sub(m uint32, value int) Jolts {
return j.Add(m, -value)
}
func (j Jolts) Valid() bool {
for i := range j {
if j[i] < 0 {
@ -129,6 +144,31 @@ func (j Jolts) Valid() bool {
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
// 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
@ -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
// 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 {
var bits uint32
for _, x := range target {
bits |= uint32(x)
var bitcost = new(state)
bitcost.init(pool)
for mask, vals := range bitcost.nodes {
fmt.Printf("%b: %v\n", mask, vals)
}
var answer int
for bit := uint(0); bits>>bit > 0; bit++ {
var mask uint32
for i := range target {
mask |= uint32(target[i]) >> bit << i
bits := target.BitLength()
qu := newHeap(target)
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 {
seen[this.jolts] = false
this.mask += 1
qu.AddNode(this)
continue
}
n := best(mask, pool)
answer += n << bit
for _, n := range bitcost.best(mask) {
t := addstates(this, n, int(bit))
if t.jolts.Valid() && !seen[t.jolts] {
qu.AddNode(t)
}
}
//for i, p := range pool {
// 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 {
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
type Node struct {
mask uint32
buttons uint32
cost int32
jolts Jolts
}
if cost_t, ok := cost[t]; ok {
if c < cost_t {
cost[t] = c
func (n Node) Add(mask uint32) Node {
n.mask ^= mask
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 {
cost[t] = c
states = append(states, t)
//qu.AddNode(t)
}
}
}
return cost[target]
s.nodes = best
}
func (s *state) best(target uint32) []Node {
return s.nodes[target]
}