Skip to content
This repository was archived by the owner on Dec 28, 2024. It is now read-only.

Commit 7a565d1

Browse files
committed
Add solution day 23 part 1 and 2
1 parent 7b84516 commit 7a565d1

File tree

6 files changed

+221
-2
lines changed

6 files changed

+221
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ automatically rebuilt and redeployed every time the `common` module or their own
5555
| 07 | ⭐ ⭐ | 20 | ⭐ ⭐ |
5656
| 08 | ⭐ ⭐ | 21 | |
5757
| 09 | ⭐ ⭐ | 22 | ⭐ ⭐ |
58-
| 10 | ⭐ ⭐ | 23 | |
58+
| 10 | ⭐ ⭐ | 23 | ⭐ ⭐ |
5959
| 11 | ⭐ ⭐ | 24 | |
6060
| 12 | ⭐ ⭐ | 25 | |
6161
| 13 | ⭐ ⭐ | | |

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/terminalnode/adventofcode2024
33
go 1.23.3
44

55
require (
6+
gonum.org/v1/gonum v0.15.1
67
google.golang.org/grpc v1.68.1
78
google.golang.org/protobuf v1.35.2
89
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
22
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
33
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
44
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
5+
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
6+
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
57
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
68
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
79
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
810
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
911
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
1012
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
13+
gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0=
14+
gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o=
1115
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
1216
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
1317
google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0=

solutions/day23/bron-kerbosch.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package main
2+
3+
import (
4+
"gonum.org/v1/gonum/graph/simple"
5+
)
6+
7+
// I believe something like this is implemented in gonum already, seems
8+
// like it anyway, but I've already cheated on the graph DS so might at
9+
// least implement the algorithm myself.
10+
11+
func bronKerbosch(
12+
r []int64,
13+
p []int64,
14+
g *simple.UndirectedGraph,
15+
) [][]int64 {
16+
// If P is empty, this is a maximal click
17+
if len(p) == 0 {
18+
return [][]int64{r}
19+
}
20+
out := make([][]int64, 0, len(p))
21+
x := make(map[int64]bool)
22+
23+
existInP := make(map[int64]bool)
24+
for _, pEntry := range p {
25+
existInP[pEntry] = true
26+
}
27+
28+
for _, v := range p {
29+
skipV := false
30+
31+
fromV := g.From(v)
32+
newP := make([]int64, 0, fromV.Len())
33+
for fromV.Next() {
34+
id := fromV.Node().ID()
35+
if !existInP[id] {
36+
continue
37+
}
38+
39+
if x[id] {
40+
// X set would not be empty, and thus can't return anything
41+
skipV = true
42+
break
43+
}
44+
newP = append(newP, id)
45+
}
46+
47+
if !skipV {
48+
newR := append([]int64{v}, r...)
49+
out = append(out, bronKerbosch(newR, newP, g)...)
50+
}
51+
x[v] = true
52+
}
53+
54+
return out
55+
}

solutions/day23/main.go

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,71 @@
11
package main
22

33
import (
4+
"fmt"
45
"github.com/terminalnode/adventofcode2024/common"
6+
"slices"
7+
"strings"
58
)
69

710
func main() {
8-
common.Setup(23, nil, nil)
11+
common.Setup(23, part1, part2)
12+
}
13+
14+
func part1(
15+
input string,
16+
) string {
17+
cm, err := parse(input)
18+
if err != nil {
19+
return fmt.Sprintf("Failed to parse input: %v", err)
20+
}
21+
22+
visited := make(map[string]bool)
23+
for name1, conn1 := range cm {
24+
for name2, conn2 := range conn1.connMap {
25+
for name3, conn3 := range conn2.connMap {
26+
if conn3.connMap[name1] != nil {
27+
if name1[0] == 't' || name2[0] == 't' || name3[0] == 't' {
28+
names := []string{name1, name2, name3}
29+
slices.Sort(names)
30+
visited[strings.Join(names, "-")] = true
31+
}
32+
}
33+
}
34+
}
35+
}
36+
37+
return fmt.Sprintf("Clusters of 3 with computers starting with t: %d", len(visited))
38+
}
39+
40+
func part2(
41+
input string,
42+
) string {
43+
graph, intToName, err := parseGonumGraph(input)
44+
if err != nil {
45+
return fmt.Sprintf("Failed to parse input: %v", err)
46+
}
47+
48+
nodes := graph.Nodes()
49+
p := make([]int64, 0, nodes.Len())
50+
for nodes.Next() {
51+
p = append(p, nodes.Node().ID())
52+
}
53+
54+
cliques := bronKerbosch([]int64{}, p, graph)
55+
56+
var biggest []int64
57+
for _, c := range cliques {
58+
if len(c) > len(biggest) {
59+
biggest = c
60+
}
61+
}
62+
63+
names := make([]string, len(biggest))
64+
for i, n := range biggest {
65+
names[i] = intToName[n]
66+
}
67+
slices.Sort(names)
68+
name := strings.Join(names, ",")
69+
70+
return fmt.Sprintf("Password: %s", name)
971
}

solutions/day23/parse.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"gonum.org/v1/gonum/graph"
6+
"gonum.org/v1/gonum/graph/simple"
7+
"strings"
8+
)
9+
10+
type connection struct {
11+
name nodeName
12+
connMap connMap
13+
}
14+
type nodeName = string
15+
type connMap map[nodeName]*connection
16+
17+
type textNode struct {
18+
id int64
19+
name string
20+
}
21+
22+
func (n textNode) ID() int64 {
23+
return n.id
24+
}
25+
26+
func parseGonumGraph(
27+
input string,
28+
) (*simple.UndirectedGraph, map[int64]string, error) {
29+
out := simple.NewUndirectedGraph()
30+
visited := make(map[string]graph.Node)
31+
intToName := make(map[int64]string)
32+
33+
idCounter := int64(0)
34+
35+
lines := strings.Split(input, "\n")
36+
for _, line := range lines {
37+
split := strings.Split(line, "-")
38+
if len(split) != 2 {
39+
return out, intToName, fmt.Errorf("length after splitting '%s' should be 2, was %d", line, len(split))
40+
}
41+
name1 := split[0]
42+
name2 := split[1]
43+
44+
if visited[name1] == nil {
45+
idCounter++
46+
visited[name1] = simple.Node(idCounter)
47+
intToName[idCounter] = name1
48+
out.AddNode(visited[name1])
49+
}
50+
if visited[name2] == nil {
51+
idCounter++
52+
visited[name2] = simple.Node(idCounter)
53+
intToName[idCounter] = name2
54+
out.AddNode(visited[name2])
55+
}
56+
out.SetEdge(simple.Edge{F: visited[name1], T: visited[name2]})
57+
}
58+
59+
return out, intToName, nil
60+
}
61+
62+
func parse(
63+
input string,
64+
) (connMap, error) {
65+
lines := strings.Split(input, "\n")
66+
out := make(connMap)
67+
68+
for _, line := range lines {
69+
split := strings.Split(line, "-")
70+
if len(split) != 2 {
71+
return out, fmt.Errorf("length after splitting '%s' should be 2, was %d", line, len(split))
72+
}
73+
out.addConnection(split[0], split[1])
74+
}
75+
76+
return out, nil
77+
}
78+
79+
func (m connMap) addConnection(
80+
n1 nodeName,
81+
n2 nodeName,
82+
) {
83+
node1 := m.getOrCreateNode(n1)
84+
node2 := m.getOrCreateNode(n2)
85+
node1.connMap[n2] = node2
86+
node2.connMap[n1] = node1
87+
}
88+
89+
func (m connMap) getOrCreateNode(
90+
name nodeName,
91+
) *connection {
92+
if n, ok := m[name]; ok {
93+
return n
94+
}
95+
m[name] = &connection{name: name, connMap: make(connMap)}
96+
return m[name]
97+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy