129 lines
2.1 KiB
Go
129 lines
2.1 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/jdkaplan/advent-of-code/aoc"
|
|
)
|
|
|
|
func main() {
|
|
text := aoc.Input().ReadFile("day14.txt")
|
|
start, rules := parse(text)
|
|
fmt.Println(run(start, rules, 10))
|
|
fmt.Println(run(start, rules, 40))
|
|
}
|
|
|
|
type Rule struct {
|
|
a, b, i rune
|
|
}
|
|
|
|
func parse(text string) (string, []Rule) {
|
|
start, lines := aoc.Cut(text, "\n\n")
|
|
var rules []Rule
|
|
for _, l := range strings.Split(lines, "\n") {
|
|
pair, ins := aoc.Cut(l, " -> ")
|
|
a, b := (pair[0]), pair[1]
|
|
rules = append(rules, Rule{rune(a), rune(b), rune(ins[0])})
|
|
}
|
|
return start, rules
|
|
}
|
|
|
|
type Pair [2]rune
|
|
|
|
type Re map[Pair]rune
|
|
|
|
func (r Re) Sub(p Pair) []Pair {
|
|
ins, ok := r[p]
|
|
if !ok {
|
|
return []Pair{p}
|
|
}
|
|
p1 := Pair{p[0], ins}
|
|
p2 := Pair{ins, p[1]}
|
|
return []Pair{p1, p2}
|
|
}
|
|
|
|
func (r Re) Put(a, b, i rune) {
|
|
r[Pair{a, b}] = i
|
|
}
|
|
|
|
func run(s string, rules []Rule, steps int) int {
|
|
re := make(Re)
|
|
for _, r := range rules {
|
|
re.Put(r.a, r.b, r.i)
|
|
}
|
|
|
|
pc := make(map[Pair]int)
|
|
for _, p := range allPairs(s) {
|
|
pc[p]++
|
|
}
|
|
first := rune(s[0])
|
|
|
|
for i := 0; i < steps; i++ {
|
|
pc = expand(pc, re)
|
|
}
|
|
|
|
counts := make(map[rune]int)
|
|
counts[first]++
|
|
for p, c := range pc {
|
|
counts[p[1]] += c
|
|
}
|
|
|
|
var chars []rune
|
|
for r := range counts {
|
|
chars = append(chars, r)
|
|
}
|
|
sort.Slice(chars, func(i, j int) bool {
|
|
return counts[chars[i]] < counts[chars[j]]
|
|
})
|
|
min, max := chars[0], chars[len(chars)-1]
|
|
return counts[max] - counts[min]
|
|
}
|
|
|
|
func allPairs(s string) []Pair {
|
|
var out []Pair
|
|
w := pairs(s)
|
|
for {
|
|
a, b, err := w.Next()
|
|
if errors.Is(err, io.EOF) {
|
|
return out
|
|
} else if err != nil {
|
|
panic(err)
|
|
}
|
|
out = append(out, Pair{a, b})
|
|
}
|
|
}
|
|
|
|
func expand(pc map[Pair]int, re Re) map[Pair]int {
|
|
out := make(map[Pair]int)
|
|
for p, c := range pc {
|
|
for _, b := range re.Sub(p) {
|
|
out[b] += c
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
type Window struct {
|
|
prev rune
|
|
r *strings.Reader
|
|
}
|
|
|
|
func (w *Window) Next() (a rune, b rune, err error) {
|
|
a = w.prev
|
|
b, _, err = w.r.ReadRune()
|
|
w.prev = b
|
|
return
|
|
}
|
|
|
|
func pairs(s string) *Window {
|
|
w := &Window{r: strings.NewReader(s)}
|
|
_, _, err := w.Next()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return w
|
|
}
|