Skip to content

Commit 4786c2c

Browse files
committed
feat(2024): day 23
1 parent cea612a commit 4786c2c

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed

2024/day23.scala

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package `2024`.day23
2+
3+
import prelude.*
4+
import scala.collection.parallel.CollectionConverters.*
5+
import scala.annotation.tailrec
6+
7+
type Connection = Map[String, Set[String]]
8+
9+
def parse(input: String): Connection = input
10+
.split('\n')
11+
.toSet
12+
.collect { case s"$a-$b" => Set(a -> b, b -> a) }
13+
.flatten
14+
.groupMap(_._1)(_._2)
15+
16+
def part1(input: String) =
17+
val connection = parse(input)
18+
19+
extension (a: String)
20+
inline infix def <->(b: String) =
21+
connection(a).contains(b) && connection(b).contains(a)
22+
23+
def isValidTriangle(vertices: Set[String]): Boolean = vertices.toList match
24+
case List(v1, v2, v3) => v1 <-> v2 && v2 <-> v3 && v3 <-> v1
25+
case _ => false
26+
27+
connection.par
28+
.flatMap { (vertex, neighbors) =>
29+
neighbors
30+
.subsets(2)
31+
.map(_ + vertex)
32+
.withFilter(_.exists(_.startsWith("t")))
33+
.filter(isValidTriangle)
34+
}
35+
.toSet
36+
.size
37+
38+
def part2(input: String) =
39+
val connection = parse(input)
40+
findMaximumCliqueBronKerbosch(connection).toList.sorted.mkString(",")
41+
42+
@main def main() =
43+
val input = fromFile(".cache/2024/23.txt").mkString
44+
println(part1(input))
45+
println(part2(input))
46+
47+
def findMaximumCliqueBronKerbosch(connections: Connection): Set[String] =
48+
def bronKerbosch(
49+
potential: Set[String],
50+
excluded: Set[String] = Set.empty,
51+
result: Set[String] = Set.empty,
52+
): Set[String] =
53+
if (potential.isEmpty && excluded.isEmpty) then result
54+
else
55+
// Choose pivot to minimize branching
56+
val pivot = (potential ++ excluded)
57+
.maxBy(vertex => potential.count(connections(vertex).contains))
58+
59+
val remaining = potential -- connections(pivot)
60+
61+
remaining.foldLeft(Set.empty[String]) { (currentMax, vertex) =>
62+
val neighbors = connections(vertex)
63+
val newClique = bronKerbosch(
64+
result = result + vertex,
65+
potential = potential & neighbors,
66+
excluded = excluded & neighbors,
67+
)
68+
if (newClique.size > currentMax.size) newClique else currentMax
69+
}
70+
71+
bronKerbosch(potential = connections.keySet)

2024/day23.test.scala

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package `2024`.day23
2+
3+
import munit.FunSuite
4+
import prelude.dedent
5+
6+
val example = """
7+
kh-tc
8+
qp-kh
9+
de-cg
10+
ka-co
11+
yn-aq
12+
qp-ub
13+
cg-tb
14+
vc-aq
15+
tb-ka
16+
wh-tc
17+
yn-cg
18+
kh-ub
19+
ta-co
20+
de-co
21+
tc-td
22+
tb-wq
23+
wh-td
24+
ta-ka
25+
td-qp
26+
aq-cg
27+
wq-ub
28+
ub-vc
29+
de-ta
30+
wq-aq
31+
wq-vc
32+
wh-yn
33+
ka-de
34+
kh-ta
35+
co-tc
36+
wh-qp
37+
tb-vc
38+
td-yn
39+
""".dedent
40+
41+
class Test extends FunSuite:
42+
test("example"):
43+
assertEquals(part1(example), 7)
44+
assertEquals(part2(example), "co,de,ka,ta")

0 commit comments

Comments
 (0)