Files
aoc2021/twelve/navigation.go
2021-12-12 23:26:23 +00:00

241 lines
4.3 KiB
Go

package twelve
import (
"bufio"
"fmt"
"os"
"strings"
)
type caveSize int
const (
startLabel = "start"
endLabel = "end"
small caveSize = 0
large caveSize = 1
)
type cave struct {
label string
connections []*cave
size caveSize
}
type navigation struct {
input []string
start *cave
end *cave
caves map[string]*cave
}
func (n *navigation) load(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
n.caves = map[string]*cave{}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
n.input = append(n.input, scanner.Text())
caves := strings.Split(scanner.Text(), "-")
if len(caves) != 2 {
return fmt.Errorf("expected 2 caves connected, found %s", scanner.Text())
}
if n.caves[caves[0]] == nil {
n.caves[caves[0]] = &cave{
label: caves[0],
size: size(caves[0]),
}
}
if n.caves[caves[1]] == nil {
n.caves[caves[1]] = &cave{
label: caves[1],
size: size(caves[1]),
}
}
// Add connection to 1 on 0
found := false
for _, c := range n.caves[caves[0]].connections {
if c.label == caves[1] {
found = true
}
}
if !found {
n.caves[caves[0]].connections = append(n.caves[caves[0]].connections, n.caves[caves[1]])
}
// Add connection to 0 on 1
found = false
for _, c := range n.caves[caves[1]].connections {
if c.label == caves[0] {
found = true
}
}
if !found && caves[0] != startLabel {
n.caves[caves[1]].connections = append(n.caves[caves[1]].connections, n.caves[caves[0]])
}
}
// Initialize start/end
n.findStart()
n.findEnd()
return nil
}
func size(label string) caveSize {
if strings.ToUpper(label) == label {
return large
}
return small
}
func (n *navigation) findStart() *cave {
if n.start != nil {
return n.start
}
for k := range n.caves {
if k == startLabel {
n.start = n.caves[k]
}
}
return n.start
}
func (n *navigation) findEnd() *cave {
if n.end != nil {
return n.end
}
for k := range n.caves {
if k == endLabel {
n.end = n.caves[k]
}
}
return n.end
}
func (n *navigation) findPaths() [][]string {
s := n.findStart()
foundPaths := [][]string{}
for _, c := range s.connections {
paths := n.findPathsRecursive([]string{s.label}, c, 1)
for _, p := range paths {
labels := []string{"start"}
for _, c := range p {
labels = append(labels, c.label)
}
foundPaths = append(foundPaths, labels)
}
}
return foundPaths
}
func (n *navigation) findPaths2() [][]string {
s := n.findStart()
foundPaths := [][]string{}
for _, c := range s.connections {
paths := n.findPathsRecursive([]string{s.label}, c, 2)
for _, p := range paths {
labels := []string{"start"}
for _, c := range p {
labels = append(labels, c.label)
}
foundPaths = append(foundPaths, labels)
}
}
// for k := range n.caves {
// fmt.Println(n.caves[k])
// }
// for _, p := range foundPaths {
// fmt.Println(p)
// }
return foundPaths
}
func (n *navigation) findPathsRecursive(from []string, in *cave, smallCaveLimit int) [][]*cave {
paths := [][]*cave{}
if in == n.end {
paths = append(paths, []*cave{
n.end,
})
return paths
}
for _, c := range in.connections {
if c.label == n.start.label {
continue
}
// No back and forth shenanigans with small caves!
// Check if we have already been to the small cave
if c.size == small {
connectionOption := true
smallVisits := map[string]int{}
for _, l := range from {
if size(l) == small {
smallVisits[l]++
}
}
if in.size == small {
smallVisits[in.label]++
}
if smallCaveLimit > 1 {
for k := range smallVisits {
if smallVisits[k] == smallCaveLimit {
connectionOption = false
}
}
}
if smallVisits[c.label] == smallCaveLimit {
connectionOption = false
}
if smallVisits[c.label] == 0 {
connectionOption = true
}
if !connectionOption {
continue
}
}
pathsFromHere := n.findPathsRecursive(append(from, in.label), c, smallCaveLimit)
if len(pathsFromHere) == 0 {
continue
}
for _, path := range pathsFromHere {
// Add the current to the front of each new path
newOption := []*cave{
in,
}
newOption = append(newOption, path...)
paths = append(paths, newOption)
}
}
return paths
}