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
|
||||
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/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
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