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 }