Skip to content

Commit

Permalink
2023 Day 25 🎄
Browse files Browse the repository at this point in the history
Not an ideal approach, but finds the right edges to remove on the first try ~90% of the time

Most the runtime is rand.Perm - could probably just start at a random index into the list instead of generating a full permutation
  • Loading branch information
ictrobot committed Dec 25, 2023
1 parent 87bdbde commit 58421e9
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 0 deletions.
152 changes: 152 additions & 0 deletions internal/aoc2023/day25/day25.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package day25

import (
_ "embed"
"github.com/ictrobot/aoc/internal/util/parse"
"math/rand"
"strings"
)

//go:embed example
var Example string

type Day25 struct {
edges [][]int
}

func (d *Day25) Parse(input string) {
lines := parse.Grid(parse.Lines, parse.Whitespace, strings.ReplaceAll(input, ":", ""))

names := make(map[string]int)
for _, line := range lines {
for _, name := range line {
if _, exists := names[name]; !exists {
names[name] = len(names)
}
}
}

d.edges = make([][]int, len(names))
for _, line := range lines {
lhs := names[line[0]]
for _, name := range line[1:] {
rhs := names[name]
d.edges[lhs] = append(d.edges[lhs], rhs)
d.edges[rhs] = append(d.edges[rhs], lhs)
}
}
}

func (d *Day25) ParseExample() {
d.Parse(Example)
}

func (d *Day25) Part1() any {
for {
removedEdges := d.edgesToRemove()
count1, count2, ok := d.checkSplit(removedEdges)
if ok {
return count1 * count2
}
}
}

func (d *Day25) Part2() any {
// no part 2
return "🎄"
}

func (d *Day25) edgesToRemove() (removedEdges [3][2]int) {
edgeCount := make(map[[2]int]int)
visited := make([]bool, len(d.edges))

var dfs func(int, int)
dfs = func(v, removed int) {
visited[v] = true

// rand.Perm is needed to avoid always visiting the same edge first
// from a given vertex which leads to almost always visiting nodes
// in the same order
neighbours:
for _, p := range rand.Perm(len(d.edges[v])) {
neighbor := d.edges[v][p]
if visited[neighbor] {
continue
}

edge := [2]int{min(v, neighbor), max(v, neighbor)}
for _, e := range removedEdges[:removed] {
if e == edge {
continue neighbours
}
}
edgeCount[edge]++

dfs(neighbor, removed)
}
}

for i := 0; i < 3; i++ {
clear(edgeCount)

for source := range d.edges {
clear(visited)
dfs(source, i)
}

var maxCount int
for e, c := range edgeCount {
if c > maxCount {
maxCount = c
removedEdges[i] = e
}
}
}

return
}

func (d *Day25) checkSplit(removedEdges [3][2]int) (int, int, bool) {
visited := make([]bool, len(d.edges))

var countReachable func(int) int
countReachable = func(v int) int {
visited[v] = true
count := 1

for _, neighbor := range d.edges[v] {
if visited[neighbor] {
continue
}

edge := [2]int{min(v, neighbor), max(v, neighbor)}
if removedEdges[0] == edge || removedEdges[1] == edge || removedEdges[2] == edge {
continue
}

count += countReachable(neighbor)
}

return count
}

count1 := countReachable(0)

unvisited := -1
for i, value := range visited {
if !value {
unvisited = i
break
}
}
if unvisited == -1 {
return 0, 0, false
}

count2 := countReachable(unvisited)

if count1+count2 < len(d.edges) {
return 0, 0, false
}
return count1, count2, true
}
43 changes: 43 additions & 0 deletions internal/aoc2023/day25/day25_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package day25

import (
"github.com/stretchr/testify/assert"
"testing"
)

const Part1 = 54

func TestDay25_ParseExample(t *testing.T) {
d1 := Day25{}
d1.ParseExample()

d2 := Day25{}
d2.ParseExample()
d2.ParseExample()

assert.Equal(t, d1, d2, "should be idempotent")
}

func BenchmarkDay25_ParseExample(b *testing.B) {
d := Day25{}
for i := 0; i < b.N; i++ {
d.ParseExample()
}
}

func TestDay25_Part1(t *testing.T) {
d := Day25{}
d.ParseExample()

assert.EqualValues(t, Part1, d.Part1())
}

func BenchmarkDay25_Part1(b *testing.B) {
d := Day25{}
d.ParseExample()

b.ResetTimer()
for i := 0; i < b.N; i++ {
assert.EqualValues(b, Part1, d.Part1())
}
}
13 changes: 13 additions & 0 deletions internal/aoc2023/day25/example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
jqt: rhn xhk nvd
rsh: frs pzl lsr
xhk: hfx
cmg: qnr nvd lhk bvb
rhn: xhk bvb hfx
bvb: xhk hfx
pzl: lsr hfx nvd
qnr: nvd
ntq: jqt hfx bvb xhk
nvd: lhk
lsr: lhk
rzs: qnr cmg lsr rsh
frs: qnr lhk lsr
4 changes: 4 additions & 0 deletions internal/solution/solution_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 58421e9

Please sign in to comment.