adventofcode/2021/day12/main.go

159 lines
3.0 KiB
Go

package main
import (
"fmt"
"strings"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
)
func main() {
inp := h.StdinToStringSlice()
part1(BuildCaves(inp))
fmt.Println()
part2(BuildCaves(inp))
}
func BuildCaves(inp []string) map[string]Cave {
ret := make(map[string]Cave)
for i := range inp {
pts := strings.Split(inp[i], "-")
var c1, c2 Cave
var ok bool
if c1, ok = ret[pts[0]]; !ok {
c1 = NewCave(pts[0])
}
if c2, ok = ret[pts[1]]; !ok {
c2 = NewCave(pts[1])
}
if !c1.ExitsTo(c2) {
c1.Exits = append(c1.Exits, c2.Name)
}
if !c2.ExitsTo(c1) {
c2.Exits = append(c2.Exits, c1.Name)
}
ret[c1.Name] = c1
ret[c2.Name] = c2
}
return ret
}
func part1(inp map[string]Cave) {
curr, _ := inp["start"]
res := part1_findPaths([]Cave{curr}, inp)
fmt.Println("# Part 1")
fmt.Println("There are", len(res), "paths")
}
func part1_shouldVisit(c string, path []Cave) bool {
if c[0] < 'A' || c[0] > 'Z' {
for i := range path {
if path[i].Name == c {
return false
}
}
}
return true
}
func part1_findPaths(path []Cave, system map[string]Cave) [][]Cave {
var ret [][]Cave
curr := path[len(path)-1]
if curr.Name == "end" {
return append(ret, path)
}
for _, c := range curr.Exits {
if part1_shouldVisit(c, path) {
newPaths := part1_findPaths(append(path, system[c]), system)
ret = append(ret, newPaths...)
}
}
return ret
}
func part2(inp map[string]Cave) {
curr, _ := inp["start"]
res := part2_findPaths([]Cave{curr}, inp)
fmt.Println("# Part 2")
fmt.Println("There are", len(res), "paths")
}
func part2_shouldVisit(c string, path []Cave) bool {
if path[len(path)-1].Name == "end" {
// If the previous spot was end, we shouldn't visit anything
return false
}
if c[0] < 'A' || c[0] > 'Z' {
if c == "start" { // We never return to start
return false
}
// Check if we've already visited this cave
visits := make(map[string]int)
var visited, twice bool
for i := range path {
if path[i].Name == c {
visited = true
}
visits[path[i].Name]++
if !path[i].Big && visits[path[i].Name] > 1 {
twice = true
}
}
if visited && twice {
return false
}
}
return true
}
func part2_findPaths(path []Cave, system map[string]Cave) [][]Cave {
var ret [][]Cave
curr := path[len(path)-1]
if curr.Name == "end" {
return append(ret, path)
}
for _, c := range curr.Exits {
if part2_shouldVisit(c, path) {
newPaths := part2_findPaths(append(path, system[c]), system)
ret = append(ret, newPaths...)
}
}
return ret
}
type Cave struct {
Name string
Big bool
Exits []string
}
func NewCave(n string) Cave {
return Cave{
Name: n,
Big: (n[0] >= 'A' && n[0] <= 'Z'),
}
}
func (c Cave) ExitsTo(e Cave) bool {
for i := range c.Exits {
if c.Exits[i] == e.Name {
return true
}
}
return false
}
func (c Cave) String() string {
return c.Name
}
func (c Cave) Detail() string {
return fmt.Sprintf("{%s -> %s}", c.Name, c.Exits)
}
func PrintMap(inp map[string]Cave) {
for k := range inp {
fmt.Println(inp[k].Detail())
}
}