Compare commits
7 Commits
a6eb8f9f22
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7abe9e7dc6 | |||
| d19c8e2dcd | |||
| 0f9bc8bfd6 | |||
| b6dbbff30d | |||
| 6617f5365e | |||
| 948be64119 | |||
| 9de558024f |
@@ -2,6 +2,7 @@ package dayeleven
|
||||
|
||||
import (
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"advent-of-code/internal/registry"
|
||||
@@ -16,10 +17,137 @@ func ParseInput(filepath string) []string {
|
||||
return strings.Split(string(content), "\n")
|
||||
}
|
||||
|
||||
func PartOne(data []string) int {
|
||||
func buildGraph(data []string) map[string][]string {
|
||||
graph := make(map[string][]string)
|
||||
|
||||
for _, line := range data {
|
||||
device, outputsStr, _ := strings.Cut(line, ": ")
|
||||
graph[device] = strings.Fields(outputsStr)
|
||||
}
|
||||
|
||||
return graph
|
||||
}
|
||||
|
||||
func hasCycle(graph map[string][]string) bool {
|
||||
const (
|
||||
unvisited = iota
|
||||
visiting
|
||||
visited
|
||||
)
|
||||
|
||||
state := make(map[string]uint8)
|
||||
|
||||
var dfs func(node string) bool
|
||||
dfs = func(node string) bool {
|
||||
switch state[node] {
|
||||
case visiting:
|
||||
return true
|
||||
case visited:
|
||||
return false
|
||||
}
|
||||
|
||||
state[node] = visiting
|
||||
if slices.ContainsFunc(graph[node], dfs) {
|
||||
return true
|
||||
}
|
||||
state[node] = visited
|
||||
return false
|
||||
}
|
||||
|
||||
for node := range graph {
|
||||
if state[node] == unvisited {
|
||||
if dfs(node) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func countPaths(graph map[string][]string, start, end string, requiredNodes map[string]bool) int {
|
||||
requiredIndex := make(map[string]uint)
|
||||
var targetMask uint
|
||||
for node := range requiredNodes {
|
||||
requiredIndex[node] = uint(len(requiredIndex))
|
||||
targetMask |= 1 << requiredIndex[node]
|
||||
}
|
||||
|
||||
if hasCycle(graph) {
|
||||
var dfs func(node string, visited map[string]bool, seenMask uint) int
|
||||
dfs = func(node string, visited map[string]bool, seenMask uint) int {
|
||||
if node == end {
|
||||
if seenMask != targetMask {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
if idx, ok := requiredIndex[node]; ok {
|
||||
seenMask |= 1 << idx
|
||||
}
|
||||
|
||||
visited[node] = true
|
||||
defer delete(visited, node)
|
||||
|
||||
count := 0
|
||||
for _, neighbor := range graph[node] {
|
||||
if !visited[neighbor] {
|
||||
count += dfs(neighbor, visited, seenMask)
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
return dfs(start, make(map[string]bool), 0)
|
||||
}
|
||||
|
||||
type key struct {
|
||||
node string
|
||||
mask uint
|
||||
}
|
||||
memo := make(map[key]int)
|
||||
|
||||
var dfs func(node string, seenMask uint) int
|
||||
dfs = func(node string, seenMask uint) int {
|
||||
if node == end {
|
||||
if seenMask != targetMask {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
if idx, ok := requiredIndex[node]; ok {
|
||||
seenMask |= 1 << idx
|
||||
}
|
||||
|
||||
state := key{node: node, mask: seenMask}
|
||||
if cached, ok := memo[state]; ok {
|
||||
return cached
|
||||
}
|
||||
|
||||
count := 0
|
||||
for _, neighbor := range graph[node] {
|
||||
count += dfs(neighbor, seenMask)
|
||||
}
|
||||
|
||||
memo[state] = count
|
||||
return count
|
||||
}
|
||||
|
||||
return dfs(start, 0)
|
||||
}
|
||||
|
||||
func PartOne(data []string) int {
|
||||
graph := buildGraph(data)
|
||||
return countPaths(graph, "you", "out", nil)
|
||||
}
|
||||
|
||||
func PartTwo(data []string) int {
|
||||
return 0
|
||||
graph := buildGraph(data)
|
||||
requiredNodes := map[string]bool{
|
||||
"dac": true,
|
||||
"fft": true,
|
||||
}
|
||||
return countPaths(graph, "svr", "out", requiredNodes)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ package dayeleven
|
||||
|
||||
import "testing"
|
||||
|
||||
var testInput = []string{
|
||||
func TestPartOne(t *testing.T) {
|
||||
testInput := []string{
|
||||
"aaa: you hhh",
|
||||
"you: bbb ccc",
|
||||
"bbb: ddd eee",
|
||||
@@ -14,11 +15,32 @@ var testInput = []string{
|
||||
"hhh: ccc fff iii",
|
||||
"iii: out",
|
||||
}
|
||||
|
||||
func TestPartOne(t *testing.T) {
|
||||
expected := 5
|
||||
got := PartOne(testInput)
|
||||
if got != expected {
|
||||
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPartTwo(t *testing.T) {
|
||||
testInput := []string{
|
||||
"svr: aaa bbb",
|
||||
"aaa: fft",
|
||||
"fft: ccc",
|
||||
"bbb: tty",
|
||||
"tty: ccc",
|
||||
"ccc: ddd eee",
|
||||
"ddd: hub",
|
||||
"hub: fff",
|
||||
"eee: dac",
|
||||
"dac: fff",
|
||||
"fff: ggg hhh",
|
||||
"ggg: out",
|
||||
"hhh: out",
|
||||
}
|
||||
expected := 2
|
||||
got := PartTwo(testInput)
|
||||
if got != expected {
|
||||
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||
}
|
||||
}
|
||||
|
||||
17
internal/2025/DayTwelve/code.go
Normal file
17
internal/2025/DayTwelve/code.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package daytwelve
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"advent-of-code/internal/registry"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Register("2025D12", ParseInput, PartOne, PartTwo)
|
||||
}
|
||||
|
||||
func ParseInput(filepath string) []string {
|
||||
content, _ := os.ReadFile(filepath)
|
||||
return strings.Split(string(content), "\n")
|
||||
}
|
||||
47
internal/2025/DayTwelve/code_test.go
Normal file
47
internal/2025/DayTwelve/code_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package daytwelve
|
||||
|
||||
import "testing"
|
||||
|
||||
var testInput = []string{
|
||||
"0:",
|
||||
"###",
|
||||
"##.",
|
||||
"##.",
|
||||
"",
|
||||
"1:",
|
||||
"###",
|
||||
"##.",
|
||||
".##",
|
||||
"",
|
||||
"2:",
|
||||
".##",
|
||||
"###",
|
||||
"##.",
|
||||
"",
|
||||
"3:",
|
||||
"##.",
|
||||
"###",
|
||||
"##.",
|
||||
"",
|
||||
"4:",
|
||||
"###",
|
||||
"#..",
|
||||
"###",
|
||||
"",
|
||||
"5:",
|
||||
"###",
|
||||
".#.",
|
||||
"###",
|
||||
"",
|
||||
"4x4: 0 0 0 0 2 0",
|
||||
"12x5: 1 0 1 0 2 2",
|
||||
"12x5: 1 0 1 0 3 2",
|
||||
}
|
||||
|
||||
func TestPartOne(t *testing.T) {
|
||||
expected := 2
|
||||
got := PartOne(testInput)
|
||||
if got != expected {
|
||||
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||
}
|
||||
}
|
||||
@@ -11,5 +11,6 @@ import (
|
||||
_ "advent-of-code/internal/2025/DaySix"
|
||||
_ "advent-of-code/internal/2025/DayTen"
|
||||
_ "advent-of-code/internal/2025/DayThree"
|
||||
_ "advent-of-code/internal/2025/DayTwelve"
|
||||
_ "advent-of-code/internal/2025/DayTwo"
|
||||
)
|
||||
|
||||
1030
internal/data/2025/DayTwelve/input.txt
Normal file
1030
internal/data/2025/DayTwelve/input.txt
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user