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) }