2021 day14, got confused with the recursive-stuff for part 2 :/

This commit is contained in:
alexchao26
2021-12-18 12:22:01 -05:00
parent c37bf58473
commit d9f24da95a
2 changed files with 220 additions and 0 deletions
+144
View File
@@ -0,0 +1,144 @@
package main
import (
_ "embed"
"flag"
"fmt"
"math"
"strings"
"github.com/alexchao26/advent-of-code-go/mathy"
"github.com/alexchao26/advent-of-code-go/util"
)
//go:embed input.txt
var input string
func init() {
// do this in init (not main) so test file has same input
input = strings.TrimRight(input, "\n")
if len(input) == 0 {
panic("empty input.txt file")
}
}
func main() {
var part int
flag.IntVar(&part, "part", 1, "part 1 or 2")
flag.Parse()
fmt.Println("Running part", part)
if part == 1 {
ans := part1(input)
util.CopyToClipboard(fmt.Sprintf("%v", ans))
fmt.Println("Output:", ans)
} else {
ans := part2(input)
util.CopyToClipboard(fmt.Sprintf("%v", ans))
fmt.Println("Output:", ans)
}
}
// naive/brute force solution that does not work for part 2 :(
func part1(input string) int {
template, rules := parseInput(input)
for step := 0; step < 10; step++ {
sp := strings.Split(template, "")
var next []string
for i := 0; i < len(sp)-1; i++ {
two := sp[i] + sp[i+1]
rule := rules[two]
next = append(next, sp[i])
next = append(next, rule)
}
next = append(next, sp[len(sp)-1])
template = strings.Join(next, "")
}
// most common element minus least common element
most, least := 0, math.MaxInt32
count := map[rune]int{}
for _, r := range template {
count[r]++
}
for _, ct := range count {
if ct > most {
most = ct
}
if ct < least {
least = ct
}
}
return most - least
}
func part2(input string) int {
template, rules := parseInput(input)
initialCount := map[string]int{}
for _, r := range template {
initialCount[string(r)]++
}
addlCountAfter40Steps := grow(template, 40, rules, map[string]map[string]int{})
// have to add the initial template back in because grow only accounts for characters that are
// added in ADDITION to the passed in template
for k, v := range initialCount {
addlCountAfter40Steps[k] += v
}
// most common element minus least common element
most, least := 0, math.MaxInt64
for _, v := range addlCountAfter40Steps {
most = mathy.MaxInt(most, v)
least = mathy.MinInt(least, v)
}
return most - least
}
func grow(template string, stepsLeft int, rules map[string]string, memo map[string]map[string]int) (addlCounts map[string]int) {
addlCounts = map[string]int{}
if stepsLeft == 0 {
return addlCounts
}
key := fmt.Sprint(template, stepsLeft)
if res, ok := memo[key]; ok {
return res
}
for i := 0; i < len(template)-1; i++ {
pair := template[i : i+2]
between := rules[pair]
addlCounts[between]++
// get the additional characters for recursing just this three character section
// calling grow will memoize that result to eliminate duplicate work
recurse := grow(pair[:1]+between+pair[1:], stepsLeft-1, rules, memo)
// merge those additional characters into this (parent function call) addlCounts map
for k, v := range recurse {
addlCounts[k] += v
}
}
// store it, return it
memo[key] = addlCounts
return addlCounts
}
func parseInput(input string) (template string, rules map[string]string) {
parts := strings.Split(input, "\n\n")
template = parts[0]
rules = map[string]string{}
for _, line := range strings.Split(parts[1], "\n") {
sp := strings.Split(line, " -> ")
rules[sp[0]] = sp[1]
}
return template, rules
}
+76
View File
@@ -0,0 +1,76 @@
package main
import (
"testing"
)
var example = `NNCB
CH -> B
HH -> N
CB -> H
NH -> C
HB -> C
HC -> B
HN -> C
NN -> C
BH -> H
NC -> B
NB -> B
BN -> B
BB -> N
BC -> B
CC -> N
CN -> C`
func Test_part1(t *testing.T) {
tests := []struct {
name string
input string
want int
}{
{
name: "example",
input: example,
want: 1588,
},
{
name: "actual",
input: input,
want: 2851,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := part1(tt.input); got != tt.want {
t.Errorf("part1() = %v, want %v", got, tt.want)
}
})
}
}
func Test_part2(t *testing.T) {
tests := []struct {
name string
input string
want int
}{
{
name: "example",
input: example,
want: 2188189693529,
},
{
name: "actual",
input: input,
want: 10002813279337,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := part2(tt.input); got != tt.want {
t.Errorf("part2() = %v, want %v", got, tt.want)
}
})
}
}