Watch
1
0
Fork
You've already forked advent-of-code
0

2021 Day 12

This commit is contained in:
Jeremy Kaplan 2021-12-11 22:12:58 -08:00
commit b5538b2540
2 changed files with 183 additions and 0 deletions

View file

@ -91,3 +91,7 @@ func Ints(text string) (ns []int) {
} }
return return
} }
func ExLines(text string) []string {
return strings.Split(strings.TrimSpace(text), "\n")
}

179
2021/day12/main.go Normal file
View file

@ -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
}