Solution for Day 12
This commit is contained in:
@@ -120,3 +120,12 @@ The solution for "eleven" is:
|
|||||||
There were 1669 flashes after 100 steps
|
There were 1669 flashes after 100 steps
|
||||||
They all flash on step 351
|
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
|
||||||
|
```
|
||||||
|
3
main.go
3
main.go
@@ -15,6 +15,7 @@ import (
|
|||||||
"unsupervised.ca/aoc2021/six"
|
"unsupervised.ca/aoc2021/six"
|
||||||
"unsupervised.ca/aoc2021/ten"
|
"unsupervised.ca/aoc2021/ten"
|
||||||
"unsupervised.ca/aoc2021/three"
|
"unsupervised.ca/aoc2021/three"
|
||||||
|
"unsupervised.ca/aoc2021/twelve"
|
||||||
"unsupervised.ca/aoc2021/two"
|
"unsupervised.ca/aoc2021/two"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -55,6 +56,8 @@ func main() {
|
|||||||
day = ten.Init("ten/input.txt")
|
day = ten.Init("ten/input.txt")
|
||||||
case "eleven":
|
case "eleven":
|
||||||
day = eleven.Init("eleven/input.txt")
|
day = eleven.Init("eleven/input.txt")
|
||||||
|
case "twelve":
|
||||||
|
day = twelve.Init("twelve/input.txt")
|
||||||
default:
|
default:
|
||||||
fmt.Printf("%q does not have a solution.\n", flagParts[1])
|
fmt.Printf("%q does not have a solution.\n", flagParts[1])
|
||||||
help()
|
help()
|
||||||
|
21
twelve/input.txt
Normal file
21
twelve/input.txt
Normal 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
24
twelve/main.go
Normal 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
240
twelve/navigation.go
Normal 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
80
twelve/navigation_test.go
Normal 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
7
twelve/test_input.txt
Normal 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
10
twelve/test_input2.txt
Normal 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
18
twelve/test_input3.txt
Normal 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
|
Reference in New Issue
Block a user