152 lines
3.2 KiB
Go
152 lines
3.2 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()
|
|
// 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 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))
|
|
}
|