From b5538b2540c1a07fcfac1ec9ddc839ed23df9c68 Mon Sep 17 00:00:00 2001 From: Jeremy Kaplan Date: Sat, 11 Dec 2021 22:12:58 -0800 Subject: [PATCH] 2021 Day 12 --- 2021/aoc/aoc.go | 4 + 2021/day12/main.go | 179 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 2021/day12/main.go diff --git a/2021/aoc/aoc.go b/2021/aoc/aoc.go index fec2efe..9ecfc27 100644 --- a/2021/aoc/aoc.go +++ b/2021/aoc/aoc.go @@ -91,3 +91,7 @@ func Ints(text string) (ns []int) { } return } + +func ExLines(text string) []string { + return strings.Split(strings.TrimSpace(text), "\n") +} diff --git a/2021/day12/main.go b/2021/day12/main.go new file mode 100644 index 0000000..1c7797f --- /dev/null +++ b/2021/day12/main.go @@ -0,0 +1,179 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/jdkaplan/advent-of-code/aoc" +) + +func main() { + lines := aoc.Input().ReadLines("day12.txt") + fmt.Println(part1(lines)) + fmt.Println(part2(lines)) +} + +func part1(lines []string) int { + g := NewGraph(lines) + return g.Paths() +} +func part2(lines []string) int { + g := NewGraph(lines) + return g.Paths2() +} + +type Graph struct { + Edges map[string][]string + Big map[string]struct{} + Small map[string]struct{} +} + +func NewGraph(lines []string) *Graph { + g := &Graph{ + Edges: make(map[string][]string), + Big: make(map[string]struct{}), + Small: make(map[string]struct{}), + } + for _, l := range lines { + a, b := aoc.Cut(l, "-") + g.Edges[a] = append(g.Edges[a], b) + g.Edges[b] = append(g.Edges[b], a) + if isBig(a) { + g.Big[a] = struct{}{} + } else { + g.Small[a] = struct{}{} + } + if isBig(b) { + g.Big[b] = struct{}{} + } else { + g.Small[b] = struct{}{} + } + } + return g +} + +func (g Graph) Neighbors(name string) []string { + return g.Edges[name] +} + +type path []string + +func (p path) String() string { + return strings.Join(p, ",") +} + +func (p path) Contains(name string) bool { + for _, n := range p { + if n == name { + return true + } + } + return false +} + +func (p path) Tip() string { + return p[len(p)-1] +} + +func (p path) Plus(name string) path { + // Copy memory to avoid overwriting the last element in the slice. + p2 := make(path, len(p)) + copy(p2, p) + return append(p2, name) +} + +func (g Graph) Paths() (count int) { + queue := []path{{"start"}} + for len(queue) > 0 { + p := queue[0] + queue = queue[1:] + tip := p.Tip() + for _, n := range g.Neighbors(tip) { + ext := p.Plus(n) + if n == "end" { + count++ + continue + } + if !isBig(n) && p.Contains(n) { + // Don't include small caves twice + continue + } + queue = append(queue, ext) + } + } + return +} + +func isBig(name string) bool { + return strings.ToUpper(name) == name +} + +type path2 struct { + caves []string + doubled bool +} + +func (p *path2) Plus(name string) (path2, bool) { + var caves []string + doubled := p.doubled + canDupe := isBig(name) + for _, c := range p.caves { + caves = append(caves, c) + if c != name { + // No dupe. + continue + } + if canDupe { + continue + } + // c == name for a small cave + if doubled { + // Already found a doubled cave, can't include this one. + return path2{}, false + } + // Contains two of c now. + doubled = true + } + return path2{ + caves: append(caves, name), + doubled: doubled, + }, true +} + +func (p path2) String() string { + return strings.Join(p.caves, ",") +} + +func (p path2) Tip() string { + return p.caves[len(p.caves)-1] +} + +func (g Graph) Paths2() (count int) { + queue := []path2{ + { + caves: []string{"start"}, + doubled: false, + }, + } + for len(queue) > 0 { + p := queue[0] + queue = queue[1:] + tip := p.Tip() + for _, n := range g.Neighbors(tip) { + if n == "start" { + // No loops! + continue + } + ext, ok := p.Plus(n) + if n == "end" { + count++ + continue + } + if !ok { + continue + } + queue = append(queue, ext) + } + } + return +}