151 lines
3.1 KiB
Go
151 lines
3.1 KiB
Go
|
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))
|
||
|
}
|