Compare commits

..

7 Commits

Author SHA1 Message Date
7abe9e7dc6 test: add unit test for part one 2026-02-03 19:02:08 +01:00
d19c8e2dcd feat: parse input 2026-02-03 19:00:14 +01:00
0f9bc8bfd6 feat: register last day 2026-02-03 18:59:22 +01:00
b6dbbff30d chore: add 2025D12 dataset 2026-02-03 18:58:51 +01:00
6617f5365e refactor+feat: use bitmask approach and cyclic/acyclic dfs for resolution 2026-02-03 16:30:08 +01:00
948be64119 test: add unit test for part two 2026-02-03 16:10:35 +01:00
9de558024f feat: solve part one using dfs approach 2026-02-03 16:08:30 +01:00
6 changed files with 1260 additions and 15 deletions

View File

@@ -2,6 +2,7 @@ package dayeleven
import ( import (
"os" "os"
"slices"
"strings" "strings"
"advent-of-code/internal/registry" "advent-of-code/internal/registry"
@@ -16,10 +17,137 @@ func ParseInput(filepath string) []string {
return strings.Split(string(content), "\n") 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 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 { func PartTwo(data []string) int {
return 0 graph := buildGraph(data)
requiredNodes := map[string]bool{
"dac": true,
"fft": true,
}
return countPaths(graph, "svr", "out", requiredNodes)
} }

View File

@@ -2,7 +2,8 @@ package dayeleven
import "testing" import "testing"
var testInput = []string{ func TestPartOne(t *testing.T) {
testInput := []string{
"aaa: you hhh", "aaa: you hhh",
"you: bbb ccc", "you: bbb ccc",
"bbb: ddd eee", "bbb: ddd eee",
@@ -13,12 +14,33 @@ var testInput = []string{
"ggg: out", "ggg: out",
"hhh: ccc fff iii", "hhh: ccc fff iii",
"iii: out", "iii: out",
} }
func TestPartOne(t *testing.T) {
expected := 5 expected := 5
got := PartOne(testInput) got := PartOne(testInput)
if got != expected { if got != expected {
t.Errorf("PartOne() = %d, want %d", 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)
}
}

View 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")
}

View 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)
}
}

View File

@@ -11,5 +11,6 @@ import (
_ "advent-of-code/internal/2025/DaySix" _ "advent-of-code/internal/2025/DaySix"
_ "advent-of-code/internal/2025/DayTen" _ "advent-of-code/internal/2025/DayTen"
_ "advent-of-code/internal/2025/DayThree" _ "advent-of-code/internal/2025/DayThree"
_ "advent-of-code/internal/2025/DayTwelve"
_ "advent-of-code/internal/2025/DayTwo" _ "advent-of-code/internal/2025/DayTwo"
) )

File diff suppressed because it is too large Load Diff