package day12 import ( "fmt" "log" "os" "strconv" "strings" ) func Run() int { fmt.Println("hello day 12.") filename := "day12/example1" 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) // } for _, line := range strings.Split(text, "\n") { mask, blockLengs := ReadLine(line) blockLengthSum := 0 for _, blockLen := range blockLengs { blockLengthSum += blockLen } variants := generatePermutations("", len(mask), blockLengs, blockLengthSum, mask) match := 0 for range variants { match += 1 } fmt.Printf("for line %s blocks %+v matches %d\n", mask, blockLengs, match) result += match } 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) []string { // fmt.Printf("> entering with \n%s\nfor map \n%s\n\n", curString, mask) // time.Sleep(time.Second) if !isVariantMatchesMask(curString, mask) { return []string{} } // log.Printf("> entering with %s\n", curString) if len(blockLengths) == 0 { if len(curString) > targetLength { return []string{} } variant := curString + strings.Repeat(".", targetLength-len(curString)) if !isVariantMatchesMask(variant, mask) { return []string{} } return []string{variant} } nextBlock := blockLengths[0] restBlocks := blockLengths[1:] if len(curString)+blockLengthsSum > targetLength { return []string{} } isLast := len(restBlocks) == 0 rightPointRepeat := 1 if isLast { rightPointRepeat = 0 } whenPass := curString + "." whenAdd := curString + strings.Repeat("#", nextBlock) + strings.Repeat(".", rightPointRepeat) variantsWhenPass := generatePermutations(whenPass, targetLength, blockLengths, blockLengthsSum, mask) variantsWhenAdd := generatePermutations(whenAdd, targetLength, restBlocks, blockLengthsSum-nextBlock, mask) return append(variantsWhenAdd, variantsWhenPass...) } 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 }