@@ -2,17 +2,103 @@ package eu.sim642.adventofcode2025
22
33import eu .sim642 .adventofcodelib .box .Box
44import eu .sim642 .adventofcodelib .pos .Pos
5+ import eu .sim642 .adventofcodelib .SeqImplicits .*
6+ import eu .sim642 .adventofcodelib .graph .{BFS , GraphTraversal , UnitNeighbors }
7+
8+ import scala .collection .immutable .SortedSet
9+ import scala .collection .mutable
510
611object Day9 {
712
8- def largestArea (redTiles : Seq [Pos ]): Long = {
9- (for {
10- // faster than combinations(2)
11- (p1, i) <- redTiles.iterator.zipWithIndex
12- p2 <- redTiles.view.slice(i + 1 , redTiles.size).iterator
13- } yield Box .bounding(Seq (p1, p2)).size[Long ]).max
13+ trait Part {
14+ def largestArea (redTiles : Seq [Pos ]): Long
15+ }
16+
17+ object Part1 extends Part {
18+ override def largestArea (redTiles : Seq [Pos ]): Long = {
19+ (for {
20+ // faster than combinations(2)
21+ (p1, i) <- redTiles.iterator.zipWithIndex
22+ p2 <- redTiles.view.slice(i + 1 , redTiles.size).iterator
23+ } yield Box .bounding(Seq (p1, p2)).size[Long ]).max
24+ }
25+ }
26+
27+ object Part2 extends Part {
28+ override def largestArea (redTiles : Seq [Pos ]): Long = {
29+ // TODO: clean up
30+ // TODO: optimize (with polygon checks?)
31+ val xs = redTiles.map(_.x).distinct.sorted
32+ val ys = redTiles.map(_.y).distinct.sorted
33+ // println(xs)
34+ // println(ys)
35+
36+ def mapPos (p : Pos ): Pos =
37+ Pos (xs.indexOf(p.x) * 2 + 1 , ys.indexOf(p.y) * 2 + 1 ) // leave edge around for fill
38+
39+ val grid = mutable.ArraySeq .fill(ys.size * 2 - 1 + 2 , xs.size * 2 - 1 + 2 )('.' )
40+
41+ for ((redTile1, redTile2) <- redTiles lazyZip redTiles.rotateLeft(1 )) {
42+ val gridPos1 = mapPos(redTile1)
43+ val gridPos2 = mapPos(redTile2)
44+ for (gridPos <- Box .bounding(Seq (gridPos1, gridPos2)).iterator)
45+ grid(gridPos.y)(gridPos.x) = 'X'
46+ }
47+
48+ for (redTile <- redTiles) {
49+ val gridPos = mapPos(redTile)
50+ // println((redTile, gridPos))
51+ grid(gridPos.y)(gridPos.x) = '#'
52+ }
53+
54+ // for (row <- grid) {
55+ // for (cell <- row)
56+ // print(cell)
57+ // println()
58+ // }
59+
60+ val graphTraversal = new GraphTraversal [Pos ] with UnitNeighbors [Pos ] {
61+ override val startNode : Pos = Pos .zero
62+
63+ override def unitNeighbors (pos : Pos ): IterableOnce [Pos ] =
64+ Pos .axisOffsets.map(pos + _).filter(p => p.x >= 0 && p.y >= 0 && p.y < grid.size && p.x < grid(p.y).size && grid(p.y)(p.x) == '.' )
65+ }
66+
67+ val outside = BFS .traverse(graphTraversal).nodes
68+
69+ def isValid (box : Box ): Boolean = {
70+ val gridBox = Box (mapPos(box.min), mapPos(box.max))
71+ ! gridBox.iterator.exists(outside)
72+ }
73+
74+ (for {
75+ // faster than combinations(2)
76+ (p1, i) <- redTiles.iterator.zipWithIndex
77+ p2 <- redTiles.view.slice(i + 1 , redTiles.size).iterator
78+ box = Box .bounding(Seq (p1, p2))
79+ if isValid(box)
80+ } yield box.size[Long ]).max
81+ }
1482 }
1583
84+ /* object Part2 extends Part {
85+ override def largestArea(redTiles: Seq[Pos]): Long = {
86+
87+ def isValid(box: Box): Boolean = {
88+ val boxCorners = Seq(box.min, Pos(box.max.x, box.min.y), box.max, Pos(box.min.x, box.max.y))
89+ ???
90+ }
91+
92+ (for {
93+ // faster than combinations(2)
94+ (p1, i) <- redTiles.iterator.zipWithIndex
95+ p2 <- redTiles.view.slice(i + 1, redTiles.size).iterator
96+ box = Box.bounding(Seq(p1, p2))
97+ if isValid(box)
98+ } yield box.size[Long]).max
99+ }
100+ }*/
101+
16102 def parseRedTile (s : String ): Pos = s match {
17103 case s " $x, $y" => Pos (x.toInt, y.toInt)
18104 }
@@ -22,6 +108,7 @@ object Day9 {
22108 lazy val input : String = scala.io.Source .fromInputStream(getClass.getResourceAsStream(" day9.txt" )).mkString.trim
23109
24110 def main (args : Array [String ]): Unit = {
25- println(largestArea(parseRedTiles(input)))
111+ println(Part1 .largestArea(parseRedTiles(input)))
112+ println(Part2 .largestArea(parseRedTiles(input)))
26113 }
27114}
0 commit comments