diff --git a/12/main.go b/12/main.go new file mode 100644 index 0000000..4fda966 --- /dev/null +++ b/12/main.go @@ -0,0 +1,150 @@ +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 bool, canVisitSmallRoomTwice bool) { + var wg sync.WaitGroup + if currentRoom == "end" { + success <- true + 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() + descend(rooms, link, path, success, canVisitSmallRoomTwice) + }(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 bool, 10000) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + descend(rooms, "start", []string{}, successes, false) + }() + wg.Wait() + 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 bool, 200000) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + descend(rooms, "start", []string{}, successes, true) + }() + wg.Wait() + fmt.Printf("%+v\n", len(successes)) +}