Solution for Day 12

This commit is contained in:
2021-12-12 23:26:23 +00:00
parent b0c161669e
commit be32018be9
9 changed files with 412 additions and 0 deletions

View File

@@ -120,3 +120,12 @@ The solution for "eleven" is:
There were 1669 flashes after 100 steps
They all flash on step 351
```
### Day twelve
```sh
$ ./aoc2021 --twelve
The solution for "twelve" is:
Found 5157 paths through the cave system
Found 144309 paths through the cave system
```

View File

@@ -15,6 +15,7 @@ import (
"unsupervised.ca/aoc2021/six"
"unsupervised.ca/aoc2021/ten"
"unsupervised.ca/aoc2021/three"
"unsupervised.ca/aoc2021/twelve"
"unsupervised.ca/aoc2021/two"
)
@@ -55,6 +56,8 @@ func main() {
day = ten.Init("ten/input.txt")
case "eleven":
day = eleven.Init("eleven/input.txt")
case "twelve":
day = twelve.Init("twelve/input.txt")
default:
fmt.Printf("%q does not have a solution.\n", flagParts[1])
help()

21
twelve/input.txt Normal file
View File

@@ -0,0 +1,21 @@
TR-start
xx-JT
xx-TR
hc-dd
ab-JT
hc-end
dd-JT
ab-dd
TR-ab
vh-xx
hc-JT
TR-vh
xx-start
hc-ME
vh-dd
JT-bm
end-ab
dd-xx
end-TR
hc-TR
start-vh

24
twelve/main.go Normal file
View File

@@ -0,0 +1,24 @@
package twelve
import "fmt"
type Twelve struct {
navigation navigation
}
func Init(filepath string) *Twelve {
twelve := &Twelve{
navigation: navigation{},
}
twelve.navigation.load(filepath)
return twelve
}
func (d *Twelve) Answer() string {
return fmt.Sprintf("Found %d paths through the cave system", len(d.navigation.findPaths()))
}
func (d *Twelve) FollowUp() string {
return fmt.Sprintf("Found %d paths through the cave system", len(d.navigation.findPaths2()))
}

240
twelve/navigation.go Normal file
View File

@@ -0,0 +1,240 @@
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
}

80
twelve/navigation_test.go Normal file
View File

@@ -0,0 +1,80 @@
package twelve
import (
"testing"
)
func Test_read(t *testing.T) {
n := navigation{}
if err := n.load("test_input.txt"); err != nil {
t.Log(err)
t.FailNow()
}
if len(n.input) != 7 {
t.Logf("Expected 7 inputs, found %d", len(n.input))
t.Fail()
}
if len(n.caves) != 6 {
t.Logf("Expected 6 caves, found %d", len(n.caves))
t.Fail()
}
}
func Test_findPathsInput1(t *testing.T) {
n := navigation{}
n.load("test_input.txt")
paths := n.findPaths()
if len(paths) != 10 {
t.Logf("Expected 10 paths, found %d", len(paths))
t.Fail()
}
paths = n.findPaths2()
if len(paths) != 36 {
t.Logf("Expected 36 paths, found %d", len(paths))
t.Fail()
}
}
func Test_findPathsInput2(t *testing.T) {
n := navigation{}
n.load("test_input2.txt")
paths := n.findPaths()
if len(paths) != 19 {
t.Logf("Expected 19 paths, found %d", len(paths))
t.Fail()
}
paths = n.findPaths2()
if len(paths) != 103 {
t.Logf("Expected 103 paths, found %d", len(paths))
t.Fail()
}
}
func Test_findPathsInput3(t *testing.T) {
n := navigation{}
n.load("test_input3.txt")
paths := n.findPaths()
if len(paths) != 226 {
t.Logf("Expected 226 paths, found %d", len(paths))
t.Fail()
}
paths = n.findPaths2()
if len(paths) != 3509 {
t.Logf("Expected 3509 paths, found %d", len(paths))
t.Fail()
}
}

7
twelve/test_input.txt Normal file
View File

@@ -0,0 +1,7 @@
start-A
start-b
A-c
A-b
b-d
A-end
b-end

10
twelve/test_input2.txt Normal file
View File

@@ -0,0 +1,10 @@
dc-end
HN-start
start-kj
dc-start
dc-HN
LN-dc
HN-end
kj-sa
kj-HN
kj-dc

18
twelve/test_input3.txt Normal file
View File

@@ -0,0 +1,18 @@
fs-end
he-DX
fs-he
start-DX
pj-DX
end-zg
zg-sl
zg-pj
pj-he
RW-he
fs-DX
pj-RW
zg-RW
start-pj
he-WI
zg-he
pj-fs
start-RW