Advent-of-Code-2023/day12/dayTwelve.go

185 lines
4.2 KiB
Go

package day12
import (
"fmt"
"log"
"os"
"strconv"
"strings"
"sync"
"time"
)
func Run() int {
fmt.Println("hello day 12.")
start := time.Now()
filename := "day12/input"
bytes, err := os.ReadFile(filename)
if err != nil {
panic(fmt.Sprintf("error reading file %s\n", filename))
}
result := 0
text := string(bytes)
text = strings.TrimSpace(text)
// testMask, testBlocks := ReadLine(".??..??...?##. 1,1,3")
// blocksSum := 0
// for _, block := range testBlocks {
// blocksSum += block
// }
// testVariants := generatePermutations("", len(testMask), testBlocks, blocksSum, testMask)
// fmt.Printf("for mask %s and blocks %+v\n", testMask, testBlocks)
// for _, variant := range testVariants {
// fmt.Println(variant)
// }
var wg sync.WaitGroup
lines := strings.Split(text, "\n")
wg.Add(len(lines))
matches := make(chan int)
go func() {
wg.Wait()
close(matches)
}()
for i, line := range lines {
go func(line string, lineNum int){
mask, blockLengs := ReadLine(line)
blockLengthSum := 0
for _, blockLen := range blockLengs {
blockLengthSum += blockLen
}
memo := make(map[string]int)
variantsCount := generatePermutations("", len(mask), blockLengs, blockLengthSum, mask, memo)
log.Printf("%d : for line %s blocks %+v matches %d\n", lineNum, mask, blockLengs, variantsCount)
matches <- variantsCount
wg.Done()
}(line, i)
}
num := 0
for match := range matches {
num += 1
result += match
log.Printf("%d. intermediate: %d\n", num, result)
fmt.Printf("%d\n", result)
}
end := time.Now()
diff := end.Sub(start)
log.Printf("> calculated for %s", diff.String())
return result
}
func myRepeat(line, sep string, amount int) string {
acc := ""
for i := 0; i < amount; i++ {
acc += sep
acc += line
}
acc, _ = strings.CutPrefix(acc, sep)
return acc
}
// ???.### 1,1,3
func ReadLine(line string) (string, []int) {
firstSplit := strings.Split(line, " ")
if len(firstSplit) != 2 {
panic(fmt.Sprintf("error splitting %s into 2", line))
}
mask := firstSplit[0]
mask = myRepeat(mask, "?", 5)
blocks := firstSplit[1]
blocks = myRepeat(blocks, ",", 5)
// log.Printf(">> repeating blocks %s", blocks)
blockLengthStrings := strings.Split(blocks, ",")
blockLengs := make([]int, len(blockLengthStrings))
for i, blockLenStr := range blockLengthStrings {
num, err := strconv.Atoi(blockLenStr)
if err != nil {
panic(fmt.Sprintf("error extracting num %s from %s\n", blockLenStr, line))
}
blockLengs[i] = num
}
return mask, blockLengs
}
func generatePermutations(curString string, targetLength int, blockLengths []int, blockLengthsSum int, mask string, memo map[string]int) int {
memoKey := fmt.Sprintf("%+v|%d", blockLengths, len(curString))
memoized, memoFound := memo[memoKey]
if memoFound {
return memoized
}
// fmt.Printf("> entering with \n%s\nfor map \n%s\n\n", curString, mask)
// time.Sleep(time.Second)
if !isVariantMatchesMask(curString, mask) {
return 0
}
// log.Printf("> entering with %s\n", curString)
if len(blockLengths) == 0 {
if len(curString) > targetLength {
return 0
}
variant := curString + strings.Repeat(".", targetLength-len(curString))
if !isVariantMatchesMask(variant, mask) {
return 0
}
memo[memoKey] = 1
return 1
}
nextBlock := blockLengths[0]
restBlocks := blockLengths[1:]
if len(curString) + blockLengthsSum + len(blockLengths) - 1 > targetLength {
return 0
}
isLast := len(restBlocks) == 0
rightPointRepeat := 1
if isLast {
rightPointRepeat = 0
}
whenPass := curString + "."
whenAdd := curString + strings.Repeat("#", nextBlock) + strings.Repeat(".", rightPointRepeat)
passCount := generatePermutations(whenPass, targetLength, blockLengths, blockLengthsSum, mask, memo)
addCount := generatePermutations(whenAdd, targetLength, restBlocks, blockLengthsSum-nextBlock, mask, memo)
memo[memoKey] = passCount + addCount
return passCount + addCount
}
func isVariantMatchesMask(variant, mask string) bool {
if len(mask) < len(variant) {
log.Printf("mask %s is less than variant %s\n", mask, variant)
}
maskRunes := []rune(mask)
for i, symb := range variant {
if maskRunes[i] == '?' {
continue
}
if maskRunes[i] != symb {
return false
}
}
return true
}