159 lines
3.0 KiB
Go
159 lines
3.0 KiB
Go
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())
|
|
}
|
|
}
|