Advent-of-Code/year25/day10/day.go
2025-12-23 19:27:19 +02:00

270 lines
6.2 KiB
Go

package day10
import (
"cmp"
"fmt"
"math"
"slices"
"strconv"
"strings"
"git.bizdoc.ro/gabi-public/Advent-of-Code.git/aoc"
"git.bizdoc.ro/private/devkit.git/collections/dsa"
"git.bizdoc.ro/private/devkit.git/gg"
)
func Part1(ctx aoc.Context) (int, error) {
inputLines, err := ParseInput(ctx)
if err != nil {
return 0, fmt.Errorf("parse input: %w", err)
}
sum := 0
for _, line := range inputLines {
if line.IndicatorLight == 0 {
continue // zero by default
}
cache2 := make([]int, 1<<line.indicatorSize)
type Frame struct {
indicator uint64
step int
//history *dsa.ImmutableList[uint64]
}
stack := dsa.NewList[Frame]()
stack.Push(Frame{indicator: line.IndicatorLight, step: 0})
solution := math.MaxInt
for !stack.IsEmpty() {
item := stack.PopFront()
//if item.step != item.history.Len() {
// panic("stack overflow")
//}
if item.indicator == 0 {
solution = min(solution, item.step)
}
if steps := cache2[item.indicator]; steps > 0 {
if steps <= item.step {
continue
}
}
cache2[item.indicator] = item.step
for _, button := range line.ButtonsNum {
if item.step <= solution {
stack.Push(Frame{
indicator: item.indicator ^ button,
step: item.step + 1,
//history: dsa.NewImmutableList(button, item.history),
})
}
}
}
sum += solution
}
return sum, nil
}
func compare[S ~[]E, E cmp.Ordered](s1, s2 S) (bool, bool) {
isSolution := true
for i := range len(s1) {
canContinue := s1[i] >= s2[i]
if !canContinue {
return false, false
}
isSolution = isSolution && (s1[i] == s2[i])
}
return true, isSolution
}
func Part2(ctx aoc.Context) (int, error) {
// does not work
inputLines, err := ParseInput(ctx)
if err != nil {
return 0, fmt.Errorf("parse input: %w", err)
}
var sum int
for _, line := range inputLines {
type Frame struct {
state []byte
step int
history [][]int
}
stack := dsa.NewStack[Frame]()
startFrameJoltage := make([]byte, len(line.Joltages))
for i, value := range line.Joltages {
startFrameJoltage[i] = byte(value)
if value > int(startFrameJoltage[i]) {
panic("byte out of range")
}
}
stack.Push(Frame{
state: make([]byte, len(line.Joltages)),
step: 0,
})
solution := math.MaxInt
cache := make(map[string]int)
for !stack.IsEmpty() {
item := stack.Pop()
if item.step > solution {
continue
}
key := gg.UnsafeString(item.state)
if steps, ok := cache[key]; ok {
if steps < item.step {
continue
}
}
cache[key] = item.step
canContinue, isSolution := compare(startFrameJoltage, item.state)
if !canContinue {
continue
}
if isSolution {
//fmt.Println(item.state, item.step)
solution = min(solution, item.step)
//break
}
for _, button := range line.Buttons {
state := slices.Clone(item.state)
for _, val := range button {
state[val] += 1
}
stack.Push(Frame{
state: state,
step: item.step + 1,
//history: append(slices.Clone(item.history), state),
})
}
}
ctx.Println(solution)
sum += solution
}
return sum, nil
}
func ParseInput(ctx aoc.Context) ([]InputLine, error) {
var inputLines []InputLine
scanner := ctx.Scanner()
for scanner.Scan() {
line := scanner.Text()
line = strings.TrimSpace(line)
input, err := ParseInputLine([]rune(line))
if err != nil {
return nil, fmt.Errorf("parse input: %w", err)
}
inputLines = append(inputLines, input)
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("scanner error: %w", err)
}
return inputLines, nil
}
type InputLine struct {
indicatorSize int
IndicatorLight uint64
IndicatorLightRaw string
Buttons [][]int
ButtonsNum []uint64
Joltages []int
RawLine string
}
func ParseInputLine(line []rune) (input InputLine, err error) {
// [...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2}
for i, ch := range line {
input.RawLine = string(line)
switch ch {
case '[': // indicator light
if input.IndicatorLight != 0 {
return input, fmt.Errorf("duplicate Indicator Light in line: %s", string(line))
}
size := i
parseIndicatorLight:
for i = i + 1; i < len(line); i++ {
switch line[i] {
case '#':
input.IndicatorLight <<= 1
input.IndicatorLight |= 1
case ']':
input.IndicatorLightRaw = string(line[size+1 : i])
size = i - size - 1
input.indicatorSize = size
break parseIndicatorLight
// done
case '.':
input.IndicatorLight <<= 1
default:
return input, fmt.Errorf("failed to parse Indicator Light. Unexpected character in IndicatorLight: %s", string(line))
}
}
case '(':
i += 1
j := strings.IndexByte(string(line[i:]), ')')
if j == -1 {
return input, fmt.Errorf("failed to parse button in line: %s", string(line))
}
button := make([]int, 0)
parts := strings.Split(string(line[i:i+j]), ",")
for _, part := range parts {
num, err := strconv.Atoi(part)
if err != nil {
return input, fmt.Errorf("failed to parse button in line: %s", string(line))
}
if num < 0 {
return input, fmt.Errorf("negative button value in line: %s", string(line))
}
if num > input.indicatorSize {
return input, fmt.Errorf("too many buttons in line: %s", string(line))
}
button = append(button, num)
}
input.Buttons = append(input.Buttons, button)
i = j
case '{':
i += 1
j := strings.IndexByte(string(line[i:]), '}')
if j == -1 {
return input, fmt.Errorf("failed to parse joltage in line: %s", string(line))
}
joltage := make([]int, 0)
parts := strings.Split(string(line[i:i+j]), ",")
for _, part := range parts {
num, err := strconv.Atoi(part)
if err != nil {
return input, fmt.Errorf("failed to parse joltage in line: %s", string(line))
}
if num < 0 {
return input, fmt.Errorf("negative joltage value in line: %s", string(line))
}
joltage = append(joltage, num)
}
input.Joltages = joltage
i = j
}
}
input.ButtonsNum = make([]uint64, len(input.Buttons))
for i, button := range input.Buttons {
for _, v := range button {
input.ButtonsNum[i] |= 1 << uint64(input.indicatorSize-v-1)
}
}
return input, nil
}