aoc2023/day-12/one.go
2023-12-14 03:44:07 +00:00

114 lines
2.1 KiB
Go

package main
import (
"fmt"
log "github.com/sirupsen/logrus"
)
const (
OPERATIONAL = iota
DAMAGED
UNKNOWN
)
var records []record
type record struct {
format []int
springs []spring
}
func newRecord() record {
return record{}
}
// raw returns the state of all springs for this record as int array
func (r record) raw() []int {
vals := make([]int, len(r.springs))
for j, spring := range r.springs {
vals[j] = spring.state
}
return vals
}
type spring struct {
state int
}
func newSpring(state int) spring {
return spring{state: state}
}
// tracker keeps track of results for function calls
type tracker struct {
checked map[string]int
}
func newTracker() tracker {
return tracker{checked: make(map[string]int)}
}
func (t tracker) add(key string, res int) {
t.checked[key] = res
}
func validate(springs, format []int) bool {
return !contains(springs[:format[0]], OPERATIONAL) && (format[0] == len(springs) || springs[format[0]] != DAMAGED)
}
// count returns the number of arrangements possible for a set of springs
func count(springs, format []int) int {
if len(springs) == 0 {
if len(format) == 0 {
return 1
}
return 0
}
if len(format) == 0 {
if contains(springs, DAMAGED) {
return 0
}
return 1
}
identifier := fmt.Sprintf("%v%v", format, springs)
if res, ok := fnTracker.checked[fmt.Sprintf("%v%v", format, springs)]; ok {
log.Debug("returning cached value for ", identifier)
return res
}
result := 0
if springs[0] == OPERATIONAL || springs[0] == UNKNOWN {
result += count(springs[1:], format)
}
if springs[0] == DAMAGED || springs[0] == UNKNOWN {
if format[0] < len(springs) && validate(springs, format) {
result += count(springs[format[0]+1:], format[1:])
} else if format[0] == len(springs) && validate(springs, format) {
result += count(springs[format[0]:], format[1:])
}
}
fnTracker.add(identifier, result)
return result
}
var fnTracker tracker
// one returns the sum of all arrangement counts
func one(lines []string) int {
parselines(lines)
fnTracker = newTracker()
sum := 0
for _, record := range records {
raw := record.raw()
sum += count(raw, record.format)
}
return sum
}