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()) } }