mirror of
https://github.com/onyx-and-iris/aoc2023.git
synced 2024-11-15 15:10:49 +00:00
114 lines
2.1 KiB
Go
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
|
||
|
}
|