package main import ( "bufio" "fmt" "os" "strings" "sync" "time" ) func main() { start := time.Now() partOne() duration := time.Since(start) partTwo() duration2 := time.Since(start) fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration) } func makeScanner(test bool) *bufio.Scanner { var f *os.File if test { f, _ = os.Open("inputs/testinput") } else { f, _ = os.Open("inputs/input") } reader := bufio.NewReader(f) return bufio.NewScanner(reader) } type room struct { name string links []string } func isBigRoom(name string) bool { return strings.ToUpper(name) == name } func canVisit(targetRoom room, visitedRooms []string) bool { if isBigRoom(targetRoom.name) { return true } for _, v := range visitedRooms { if targetRoom.name == v { return false } } return true } func doubleSmallAlready(visitedRooms []string) bool { counts := map[string]int{} for _, v := range visitedRooms { counts[v]++ } for room, v := range counts { if v == 2 && !isBigRoom(room) { return true } } return false } func descend(rooms map[string]room, currentRoom string, path []string, success chan struct{}, canVisitSmallRoomTwice bool) { var wg sync.WaitGroup if currentRoom == "end" { success <- struct{}{} return } for _, link := range rooms[currentRoom].links { newPath := []string{currentRoom} newPath = append(newPath, path...) if canVisit(rooms[link], path) || // part one (canVisitSmallRoomTwice && !doubleSmallAlready(newPath) && link != "start") { // part two wg.Add(1) go func(link string, path []string) { defer wg.Done() // in day 2, skip checks once we know we've visited a small room twice descend(rooms, link, path, success, canVisitSmallRoomTwice && !doubleSmallAlready(newPath)) }(link, newPath) } } wg.Wait() } func partOne() { scanner := makeScanner(false) rooms := map[string]room{} for scanner.Scan() { line := scanner.Text() p := strings.Split(line, "-") // create rooms if we haven't seen them before if _, ok := rooms[p[0]]; !ok { rooms[p[0]] = room{name: p[0]} } if _, ok := rooms[p[1]]; !ok { rooms[p[1]] = room{name: p[1]} } // set up links a := rooms[p[0]] b := rooms[p[1]] a.links = append(a.links, rooms[p[1]].name) b.links = append(b.links, rooms[p[0]].name) rooms[p[0]] = a rooms[p[1]] = b } successes := make(chan struct{}, 10000) descend(rooms, "start", []string{}, successes, false) fmt.Printf("%+v\n", len(successes)) } func partTwo() { scanner := makeScanner(false) rooms := map[string]room{} for scanner.Scan() { line := scanner.Text() p := strings.Split(line, "-") // create rooms if we haven't seen them before if _, ok := rooms[p[0]]; !ok { rooms[p[0]] = room{name: p[0]} } if _, ok := rooms[p[1]]; !ok { rooms[p[1]] = room{name: p[1]} } // set up links a := rooms[p[0]] b := rooms[p[1]] a.links = append(a.links, rooms[p[1]].name) b.links = append(b.links, rooms[p[0]].name) rooms[p[0]] = a rooms[p[1]] = b } successes := make(chan struct{}, 200000) descend(rooms, "start", []string{}, successes, true) fmt.Printf("%+v\n", len(successes)) }