Files
adventofcode/2023/go/day12/day12.go
2023-12-15 16:07:58 +00:00

136 lines
3.6 KiB
Go

package day12
import (
"fmt"
"strings"
"adventofcode2023/utils"
"adventofcode2023/utils/inputs"
)
var cache = make(map[string]int)
type memoized struct {
f func(int) int
cache map[string]int
}
func Part1(input string) int {
ans := 0
lines := strings.Split(input, "\n")
for _, line := range lines {
slices := strings.Split(line, " ")
record := slices[0]
groups := inputs.ToInts(slices[1], ",")
ans += calc(record, groups)
}
return ans
}
func Part2(input string) int {
ans := 0
lines := strings.Split(input, "\n")
for i, line := range lines {
slices := strings.Split(line, " ")
record := slices[0] + "?" + slices[0] + "?" + slices[0] + "?" + slices[0] + "?" + slices[0]
groups := inputs.ToInts(slices[1] + "," + slices[1] + "," + slices[1] + "," + slices[1] + "," + slices[1] , ",")
ans += calc(record, groups)
fmt.Printf("running total after lines %v = %v\n",i, ans)
}
return ans
}
func calc(record string, groups []int) int {
key := record + fmt.Sprint(groups)
if v, ok := cache[key]; ok {
return v
}
// ADD LOGIC HERE ... Base-case logic will go here
// Did we run out of groups? We might still be valid
if len(groups) == 0 {
// Make sure there aren't any more damaged springs, if so, we're valid
if ! strings.Contains(record, "#") {
cache[key] = 1
return 1
} else {
// Not valid to runout of groups and still have damaged springs "#"
cache[key] = 0
return 0
}
}
if len(record) == 0 {
// Already know there are more groups but no records
cache[key] = 0
return 0
}
// Look at the next element in each record and group
next_character := record[0]
next_group := groups[0]
out := 0
if next_character == '#' {
// Test pound logic
out = pound(record, groups, next_group)
} else if next_character == '.' {
// Test dot logic
out = dot(record, groups)
} else if next_character == '?' {
// This character could be either character, so we'll explore both
//possibilities
out = dot(record, groups) + pound(record, groups, next_group)
} else {
utils.PanicOnErr(fmt.Errorf("RuntimeError"))
}
// Help with debugging
// fmt.Println(record, groups, out)
cache[key] = out
return out
}
// Logic that treats the first character as pound-sign "#"
func pound(record string, groups []int, next_group int) int {
// If the first is a pound, then the first n characters must be
// able to be treated as a pound, where n is the first group number
if next_group > len(record) {
return 0
}
this_group := record[:next_group]
this_group = strings.Replace(this_group, "?", "#", -1)
// If the next group can't fit all the damaged springs, then abort
if this_group != strings.Repeat("#", next_group) {
return 0
}
// If the rest of the record is just the last group, then we're
// done and there's only one possibility
if len(record) == next_group {
// Make sure this is the last group
if len(groups) == 1 {
//We are valid
return 1
} else {
// There's more groups, we can't make it work
return 0
}
}
// Make sure the character that follows this group can be a seperator
if record[next_group] == '?' || record[next_group] == '.' {
// It can be seperator, so skip it and reduce to the next group
return calc(record[next_group+1:], groups[1:])
}
// Can't be handled, there are no possibilites
return 0
}
// Logic that treats the first character as dot "."
func dot(record string, groups []int) int {
// ADD LOGIC HERE ... need to process this character and call
// We just skip over the dot looking for the next pound
return calc(record[1:], groups)
}