Compare commits
7 Commits
a6eb8f9f22
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7abe9e7dc6 | |||
| d19c8e2dcd | |||
| 0f9bc8bfd6 | |||
| b6dbbff30d | |||
| 6617f5365e | |||
| 948be64119 | |||
| 9de558024f |
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
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/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"
|
||||||
)
|
)
|
||||||
|
|||||||
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