diff --git a/day04/main_test.go b/day04/main_test.go index 9e71799..467f3e0 100644 --- a/day04/main_test.go +++ b/day04/main_test.go @@ -17,16 +17,18 @@ func TestExample(t *testing.T) { is := is.New(t) scan := bufio.NewScanner(bytes.NewReader(example)) - points, cards := run(scan) - is.Equal(points, 13) - is.Equal(cards, 30) + r, err := run(scan) + is.NoErr(err) + is.Equal(r.points, 13) + is.Equal(r.cards, 30) } func TestSolution(t *testing.T) { is := is.New(t) scan := bufio.NewScanner(bytes.NewReader(input)) - points, cards := run(scan) - is.Equal(points, 23235) - is.Equal(cards, 5920640) + r, err := run(scan) + is.NoErr(err) + is.Equal(r.points, 23235) + is.Equal(r.cards, 5920640) } diff --git a/day05/main_test.go b/day05/main_test.go index 817b25c..de7ac7b 100644 --- a/day05/main_test.go +++ b/day05/main_test.go @@ -20,19 +20,20 @@ func TestExample(t *testing.T) { is := is.New(t) scan := bufio.NewScanner(bytes.NewReader(example)) - minLocation, minRangeLocation := run(scan) - is.Equal(minLocation, 35) - is.Equal(minRangeLocation, 46) + r, err := run(scan) + is.NoErr(err) + is.Equal(r.minLocation, 35) + is.Equal(r.minRange, 46) } func SkipTestSolution(t *testing.T) { is := is.New(t) scan := bufio.NewScanner(bytes.NewReader(input)) - minLocation, minRangeLocation := run(scan) - is.Equal(minLocation, 199602917) - is.Equal(minRangeLocation, 0) - + r, err := run(scan) + is.NoErr(err) + is.Equal(r.minLocation, 199602917) + is.Equal(r.minRange, 0) } func TestLookup(t *testing.T) { @@ -44,9 +45,9 @@ func TestLookup(t *testing.T) { is.Equal(find.Find(79), 81) find = &Lookup{ranges: Ranges{ - {77,45,23}, - {45,81,19}, - {64,68,13}, + {77, 45, 23}, + {45, 81, 19}, + {64, 68, 13}, }} is.Equal(find.Find(77), 45) @@ -62,38 +63,38 @@ func TestFinder(t *testing.T) { }}, // soil-to-fertilizer &Lookup{ranges: Ranges{ - {15, 0,37}, - {52,37,2}, - {0,39,15}, + {15, 0, 37}, + {52, 37, 2}, + {0, 39, 15}, }}, // fertilizer-to-water &Lookup{ranges: Ranges{ - {53,49,8}, - {11,0,42}, - {0,42,7}, - {7,57,4}, + {53, 49, 8}, + {11, 0, 42}, + {0, 42, 7}, + {7, 57, 4}, }}, // water-to-light &Lookup{ranges: Ranges{ - {18,88,7}, - {25,18,70}, + {18, 88, 7}, + {25, 18, 70}, }}, // light-to-temperature &Lookup{ranges: Ranges{ - {77,45,23}, - {45,81,19}, - {64,68,13}, + {77, 45, 23}, + {45, 81, 19}, + {64, 68, 13}, }}, // temperature-to-humidity &Lookup{ranges: Ranges{ - {69,0,1}, - {0,1,69}, + {69, 0, 1}, + {0, 1, 69}, }}, // humidity-to-location &Lookup{ranges: Ranges{ - {56,60,37}, - {93,56,4}, + {56, 60, 37}, + {93, 56, 4}, }}, ) is.Equal(find.Find(82), 46) -} \ No newline at end of file +} diff --git a/day12/main.go b/day12/main.go index cf03a1d..1ae66bd 100644 --- a/day12/main.go +++ b/day12/main.go @@ -23,6 +23,7 @@ func (r result) String() string { return fmt.Sprintf("%#v", r) } func run(scan *bufio.Scanner) (*result, error) { var matches []int + var matches2 []int for scan.Scan() { text := scan.Text() @@ -31,16 +32,24 @@ func run(scan *bufio.Scanner) (*result, error) { continue } + // part 1 - brute force grouping := aoc.SliceMap(aoc.Atoi, strings.Split(text, ",")...) pattern := []rune(status) - missing := countQuestion(pattern) + // sp := spring{pattern: pattern, grouping: grouping, missingNo: countQuestion(pattern)} + // matches = append(matches, sp.findMatches()) + matches = append(matches, countPossible(pattern, grouping)) - s := spring{pattern: pattern, grouping: grouping, missingNo: missing} - - matches = append(matches, s.findMatches()) + // part 2 - NFA + b, a := status, text + bn, an := "", "" + for i := 0; i < 5; i++ { + bn, an = bn+b+"?", an+a+"," + } + b, a = strings.TrimSuffix(bn, "?"), strings.TrimSuffix(an, ",") + matches2 = append(matches2, countPossible([]rune(b), aoc.SliceMap(aoc.Atoi, strings.Split(a, ",")...))) } - return &result{valuePT1: aoc.Sum(matches...)}, nil + return &result{valuePT1: aoc.Sum(matches...), valuePT2: aoc.Sum(matches2...)}, nil } type spring struct { @@ -89,6 +98,63 @@ func (s *spring) genPatterns() []string { return lis } +func countPossible(s []rune, c []int) int { + pos := 0 + + cstates := map[state]int{{}: 1} // current state + nstates := map[state]int{} // next state + + for len(cstates) > 0 { + for st, num := range cstates { + si, ci, cc, expdot := st.springIndex, st.groupIndex, st.continuous, st.expectDot + + // have we reached the end? + if si == len(s) { + if ci == len(c) { + pos += num + } + continue + } + + switch { + case (s[si] == '#' || s[si] == '?') && ci < len(c) && !expdot: + // we are still looking for broken springs + if s[si] == '?' && cc == 0 { + // we are not in a run of broken springs, so ? can be working + nstates[state{si + 1, ci, cc, expdot}] += num + } + + cc++ + + if cc == c[ci] { + // we've found the full next contiguous section of broken springs + ci++ + cc = 0 + expdot = true // we only want a working spring next + } + + nstates[state{si + 1, ci, cc, expdot}] += num + + case (s[si] == '.' || s[si] == '?') && cc == 0: + // we are not in a contiguous run of broken springs + expdot = false + nstates[state{si + 1, ci, cc, expdot}] += num + } + } + + // swap and clear previous states + cstates, nstates = nstates, cstates + clear(nstates) + } + return pos +} + +type state struct { + springIndex int + groupIndex int + continuous int + expectDot bool +} func countQuestion(pattern []rune) int { count := 0 @@ -119,4 +185,3 @@ func countGroupings(pattern []rune) []int { } return groupings } - diff --git a/day12/main_test.go b/day12/main_test.go index 3ea209f..1700247 100644 --- a/day12/main_test.go +++ b/day12/main_test.go @@ -27,7 +27,7 @@ func TestExample(t *testing.T) { t.Log(result) is.Equal(result.valuePT1, 21) - is.Equal(result.valuePT2, 0) + is.Equal(result.valuePT2, 525152) } func TestSolution(t *testing.T) { @@ -39,7 +39,7 @@ func TestSolution(t *testing.T) { t.Log(result) is.Equal(result.valuePT1, 8193) - is.Equal(result.valuePT2, 0) + is.Equal(result.valuePT2, 45322533163795) } func TestPower2(t *testing.T) {