123 lines
2.0 KiB
Go
123 lines
2.0 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
_ "embed"
|
||
|
"fmt"
|
||
|
"slices"
|
||
|
"strings"
|
||
|
|
||
|
aoc "go.sour.is/advent-of-code-2023"
|
||
|
)
|
||
|
|
||
|
// var log = aoc.Log
|
||
|
|
||
|
func main() { aoc.MustResult(aoc.Runner(run)) }
|
||
|
|
||
|
type result struct {
|
||
|
valuePT1 int
|
||
|
valuePT2 int
|
||
|
}
|
||
|
|
||
|
func (r result) String() string { return fmt.Sprintf("%#v", r) }
|
||
|
|
||
|
func run(scan *bufio.Scanner) (*result, error) {
|
||
|
var matches []int
|
||
|
|
||
|
for scan.Scan() {
|
||
|
text := scan.Text()
|
||
|
status, text, ok := strings.Cut(text, " ")
|
||
|
if !ok {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
grouping := aoc.SliceMap(aoc.Atoi, strings.Split(text, ",")...)
|
||
|
pattern := []rune(status)
|
||
|
missing := countQuestion(pattern)
|
||
|
|
||
|
s := spring{pattern: pattern, grouping: grouping, missingNo: missing}
|
||
|
|
||
|
matches = append(matches, s.findMatches())
|
||
|
}
|
||
|
|
||
|
return &result{valuePT1: aoc.Sum(matches...)}, nil
|
||
|
}
|
||
|
|
||
|
type spring struct {
|
||
|
pattern []rune
|
||
|
grouping []int
|
||
|
missingNo int
|
||
|
}
|
||
|
|
||
|
func (s *spring) findMatches() int {
|
||
|
matches := 0
|
||
|
for _, pattern := range s.genPatterns() {
|
||
|
pattern := []rune(pattern)
|
||
|
target := make([]rune, len(s.pattern))
|
||
|
i := 0
|
||
|
for j, r := range s.pattern {
|
||
|
if r == '?' {
|
||
|
target[j] = pattern[i]
|
||
|
i++
|
||
|
continue
|
||
|
}
|
||
|
target[j] = r
|
||
|
}
|
||
|
|
||
|
if slices.Equal(countGroupings(target), s.grouping) {
|
||
|
matches++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return matches
|
||
|
}
|
||
|
func (s *spring) genPatterns() []string {
|
||
|
buf := &strings.Builder{}
|
||
|
combinations := aoc.Power2(s.missingNo)
|
||
|
lis := make([]string, 0, combinations)
|
||
|
for i := 0; i < combinations; i++ {
|
||
|
for b := 0; b < s.missingNo; b++ {
|
||
|
if i>>b&0b1 == 1 {
|
||
|
buf.WriteRune('#')
|
||
|
} else {
|
||
|
buf.WriteRune('.')
|
||
|
}
|
||
|
}
|
||
|
lis = append(lis, buf.String())
|
||
|
buf.Reset()
|
||
|
}
|
||
|
|
||
|
return lis
|
||
|
}
|
||
|
|
||
|
func countQuestion(pattern []rune) int {
|
||
|
count := 0
|
||
|
for _, r := range pattern {
|
||
|
if r == '?' {
|
||
|
count++
|
||
|
}
|
||
|
}
|
||
|
return count
|
||
|
}
|
||
|
func countGroupings(pattern []rune) []int {
|
||
|
var groupings []int
|
||
|
inGroup := false
|
||
|
for _, r := range pattern {
|
||
|
|
||
|
if r == '#' {
|
||
|
if !inGroup {
|
||
|
groupings = append(groupings, 0)
|
||
|
}
|
||
|
|
||
|
inGroup = true
|
||
|
groupings[len(groupings)-1]++
|
||
|
|
||
|
}
|
||
|
if inGroup && r != '#' {
|
||
|
inGroup = false
|
||
|
}
|
||
|
}
|
||
|
return groupings
|
||
|
}
|
||
|
|