270 lines
6.2 KiB
Go
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
|
|
}
|