From 1e411f6388acd89f95a0508b614bfe890e6f6bff Mon Sep 17 00:00:00 2001 From: Christopher De Vries Date: Tue, 12 Dec 2023 07:42:36 -0500 Subject: [PATCH] Finished part 1 of day 12. Working on part 2. --- day12p1/solution.go | 106 ++++++++++++++++++++++++++ day12p1/solution_test.go | 38 ++++++++++ day12p2/solution.go | 158 +++++++++++++++++++++++++++++++++++++++ day12p2/solution_test.go | 38 ++++++++++ inputs | 2 +- 5 files changed, 341 insertions(+), 1 deletion(-) create mode 100644 day12p1/solution.go create mode 100644 day12p1/solution_test.go create mode 100644 day12p2/solution.go create mode 100644 day12p2/solution_test.go diff --git a/day12p1/solution.go b/day12p1/solution.go new file mode 100644 index 0000000..642a96f --- /dev/null +++ b/day12p1/solution.go @@ -0,0 +1,106 @@ +package day12p1 + +import ( + "fmt" + "io" + "strconv" + "strings" + + "aoc/utils" +) + +func Solve(r io.Reader) any { + lines := utils.ReadLines(r) + + sum := int64(0) + for _, ln := range lines { + if utils.Verbose { + fmt.Printf("Starting %s\n", ln) + } + + parts := strings.Fields(ln) + + numbers := strings.Split(parts[1], ",") + + groups := make([]int64, len(numbers)) + + var err error + for i, v := range numbers { + groups[i], err = strconv.ParseInt(v, 10, 64) + utils.Check(err, "Unable to convert %s to int64", v) + } + + sum += findValid([]rune(parts[0]), groups) + } + return sum +} + +func findValid(row []rune, groups []int64) int64 { + valids := int64(0) + + for i, r := range row { + if r == '?' { + // replace with functional spring + trialA := make([]rune, len(row)) + copy(trialA, row) + trialA[i] = '.' + if isValid(trialA, groups) { + valids += findValid(trialA, groups) + } + + // replace with broken sprint + trialB := make([]rune, len(row)) + copy(trialB, row) + trialB[i] = '#' + if isValid(trialB, groups) { + valids += findValid(trialB, groups) + } + + return valids + } + } + + if isValid(row, groups) { + valids = 1 + if utils.Verbose { + fmt.Printf("\t%s\n", string(row)) + } + } + return valids +} + +func isValid(row []rune, groups []int64) bool { + cgroups := []int64{} + + n := int64(0) + + for _, r := range row { + switch r { + case '#': + n++ + case '.': + if n > 0 { + cgroups = append(cgroups, n) + n = 0 + } + case '?': + return true + } + } + if n > 0 { + cgroups = append(cgroups, n) + } + n = 0 + + if len(cgroups) != len(groups) { + return false + } + + for i, g := range groups { + if cgroups[i] != g { + return false + } + } + + return true +} diff --git a/day12p1/solution_test.go b/day12p1/solution_test.go new file mode 100644 index 0000000..a5bc616 --- /dev/null +++ b/day12p1/solution_test.go @@ -0,0 +1,38 @@ +package day12p1 + +import ( + "strings" + "testing" + + "aoc/utils" +) + +var testInput = `???.### 1,1,3 +.??..??...?##. 1,1,3 +?#?#?#?#?#?#?#? 1,3,1,6 +????.#...#... 4,1,1 +????.######..#####. 1,6,5 +?###???????? 3,2,1` + +func TestSolve(t *testing.T) { + tests := []struct { + input string + answer int64 + }{ + {testInput, 21}, + } + + if testing.Verbose() { + utils.Verbose = true + } + + for _, test := range tests { + r := strings.NewReader(test.input) + + result := Solve(r).(int64) + + if result != test.answer { + t.Errorf("Expected %d, got %d", test.answer, result) + } + } +} diff --git a/day12p2/solution.go b/day12p2/solution.go new file mode 100644 index 0000000..36e4b4c --- /dev/null +++ b/day12p2/solution.go @@ -0,0 +1,158 @@ +package day12p2 + +import ( + "fmt" + "io" + "strconv" + "strings" + + "aoc/utils" +) + +func Solve(r io.Reader) any { + lines := utils.ReadLines(r) + + sum := int64(0) + for _, ln := range lines { + if utils.Verbose { + fmt.Printf("Starting %s\n", ln) + } + + parts := strings.Fields(ln) + + numbers := strings.Split(parts[1], ",") + + groups := make([]int64, len(numbers)*5) + + for i, v := range numbers { + n, err := strconv.ParseInt(v, 10, 64) + utils.Check(err, "Unable to convert %s to int64", v) + for j := 0; j < 5; j++ { + groups[i+len(numbers)*j] = n + } + } + + var bld strings.Builder + for i := 0; i < 5; i++ { + bld.WriteString(parts[0]) + if i < 4 { + bld.WriteString("?") + } + } + + if utils.Verbose { + fmt.Printf("\texpanded: %s\n", bld.String()) + } + valid := findValid([]rune(bld.String()), groups) + if utils.Verbose { + fmt.Printf("\tValid: %d\n\n", valid) + } + sum += valid + } + return sum +} + +func findValid(row []rune, groups []int64) int64 { + valids := int64(0) + + for i, r := range row { + if r == '?' { + // replace with functional spring + trialA := make([]rune, len(row)) + copy(trialA, row) + trialA[i] = '.' + if isValid(trialA, groups) { + valids += findValid(trialA, groups) + } + + // replace with broken sprint + trialB := make([]rune, len(row)) + copy(trialB, row) + trialB[i] = '#' + if isValid(trialB, groups) { + valids += findValid(trialB, groups) + } + + return valids + } + } + + if isValid(row, groups) { + valids = 1 + // if utils.Verbose { + // fmt.Printf("\t%s\n", string(row)) + // } + } + return valids +} + +func isValid(row []rune, groups []int64) bool { + cgroups := []int64{} + + n := int64(0) + sum := int64(0) + empties := int64(0) + firstEmpty := false + + // To add: + // find minimum possible groups + // by checking for '.' between remaining '#' + // also: if !firstEmpty sum==sum(groups) + + for _, r := range row { + switch r { + case '#': + n++ + sum++ + case '.': + if !firstEmpty && n > 0 { + cgroups = append(cgroups, n) + n = 0 + } + case '?': + empties++ + if !firstEmpty && n > 0 { + cgroups = append(cgroups, n) + } + n = 0 + firstEmpty = true + } + } + if !firstEmpty && n > 0 { + cgroups = append(cgroups, n) + } + n = 0 + + if !firstEmpty && len(cgroups) != len(groups) { + return false + } + + if len(cgroups) > len(groups) { + // more groups already made than allowed + return false + } + + totalBroken := int64(0) + cgl := len(cgroups) + for i, g := range groups { + if i < cgl-1 && cgroups[i] != g { + return false + } else if i == cgl-1 && cgroups[i] > g { + // What if there are no empties? then cgroups[i] should equal g + return false + } + totalBroken += g + } + + if sum > totalBroken { + // too many defects + return false + } + + if sum+empties < totalBroken { + // not enough to make total broken + return false + } + + return true +} diff --git a/day12p2/solution_test.go b/day12p2/solution_test.go new file mode 100644 index 0000000..6a3e1eb --- /dev/null +++ b/day12p2/solution_test.go @@ -0,0 +1,38 @@ +package day12p2 + +import ( + "strings" + "testing" + + "aoc/utils" +) + +var testInput = `???.### 1,1,3 +.??..??...?##. 1,1,3 +?#?#?#?#?#?#?#? 1,3,1,6 +????.#...#... 4,1,1 +????.######..#####. 1,6,5 +?###???????? 3,2,1` + +func TestSolve(t *testing.T) { + tests := []struct { + input string + answer int64 + }{ + {testInput, 525152}, + } + + if testing.Verbose() { + utils.Verbose = true + } + + for _, test := range tests { + r := strings.NewReader(test.input) + + result := Solve(r).(int64) + + if result != test.answer { + t.Errorf("Expected %d, got %d", test.answer, result) + } + } +} diff --git a/inputs b/inputs index 5255730..4fd3f41 160000 --- a/inputs +++ b/inputs @@ -1 +1 @@ -Subproject commit 52557307d2eb9cfac32c4f6621ff4470cbed5055 +Subproject commit 4fd3f4158c2005601be414c2042535b68c9c8135