From a23086ebff179315d49d54cab793121b37e100e8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 12 Dec 2024 08:35:55 +0200 Subject: [PATCH] Solve 2024 day 12 part 2 --- .../eu/sim642/adventofcode2024/Day12.scala | 56 +++++++++++++++++-- .../sim642/adventofcode2024/Day12Test.scala | 35 ++++++++++-- 2 files changed, 81 insertions(+), 10 deletions(-) diff --git a/src/main/scala/eu/sim642/adventofcode2024/Day12.scala b/src/main/scala/eu/sim642/adventofcode2024/Day12.scala index 860b2bd8..49964a45 100644 --- a/src/main/scala/eu/sim642/adventofcode2024/Day12.scala +++ b/src/main/scala/eu/sim642/adventofcode2024/Day12.scala @@ -5,9 +5,12 @@ import eu.sim642.adventofcodelib.GridImplicits.* import eu.sim642.adventofcodelib.box.Box import eu.sim642.adventofcodelib.graph.{BFS, GraphComponents} import eu.sim642.adventofcodelib.pos.Pos +import eu.sim642.adventofcode2018.Day13.DirectionPos object Day12 { + case class Edge(in: Pos, out: Pos) + case class Region(poss: Set[Pos]) { def area: Int = poss.size @@ -17,6 +20,34 @@ object Day12 { .map(pos => Pos.axisOffsets.map(pos + _).count(poss)) //.tapEach(println) .sum } + + def sides: Int = { + val edges = + for { + pos <- poss + offset <- Pos.axisOffsets + newPos = pos + offset + if !poss(newPos) + } yield Edge(pos, newPos) + + val graphComponents = new GraphComponents[Edge] { + override def nodes: IterableOnce[Edge] = edges + + override def unitNeighbors(edge: Edge): IterableOnce[Edge] = { + val Edge(in, out) = edge + val direction = out - in + for { + inOffset <- Seq(direction.left, direction.right) + newIn = in + inOffset + newOut = newIn + direction + newEdge = Edge(newIn, newOut) + if edges(newEdge) + } yield newEdge + } + } + + BFS.components(graphComponents).size + } } def regions(grid: Grid[Char]): Set[Region] = { @@ -37,11 +68,23 @@ object Day12 { BFS.components(graphComponents).map(x => Region(x.toSet)).toSet // TODO: avoid weird toSet-s } - def totalFencingPrice(grid: Grid[Char]): Int = { - val regs = regions(grid) - //for (region <- regs) - // println(s"${grid(region.poss.head)}: area=${region.area} perimeter=${region.perimeter}") - regs.iterator.map(region => region.area * region.perimeter).sum + trait Part { + def regionFencingPrice(region: Region): Int + + def totalFencingPrice(grid: Grid[Char]): Int = { + val regs = regions(grid) + //for (region <- regs) + // println(s"${grid(region.poss.head)}: area=${region.area} perimeter=${region.perimeter}") + regs.iterator.map(regionFencingPrice).sum + } + } + + object Part1 extends Part { + override def regionFencingPrice(region: Region): Int = region.area * region.perimeter + } + + object Part2 extends Part { + override def regionFencingPrice(region: Region): Int = region.area * region.sides } def parseGrid(input: String): Grid[Char] = input.linesIterator.map(_.toVector).toVector @@ -49,6 +92,7 @@ object Day12 { lazy val input: String = scala.io.Source.fromInputStream(getClass.getResourceAsStream("day12.txt")).mkString.trim def main(args: Array[String]): Unit = { - println(totalFencingPrice(parseGrid(input))) + println(Part1.totalFencingPrice(parseGrid(input))) + println(Part2.totalFencingPrice(parseGrid(input))) } } diff --git a/src/test/scala/eu/sim642/adventofcode2024/Day12Test.scala b/src/test/scala/eu/sim642/adventofcode2024/Day12Test.scala index 17204528..f0eecbba 100644 --- a/src/test/scala/eu/sim642/adventofcode2024/Day12Test.scala +++ b/src/test/scala/eu/sim642/adventofcode2024/Day12Test.scala @@ -30,13 +30,40 @@ class Day12Test extends AnyFunSuite { |MIIISIJEEE |MMMISSJEEE""".stripMargin + val exampleInput4 = + """EEEEE + |EXXXX + |EEEEE + |EXXXX + |EEEEE""".stripMargin + + val exampleInput5 = + """AAAAAA + |AAABBA + |AAABBA + |ABBAAA + |ABBAAA + |AAAAAA""".stripMargin + test("Part 1 examples") { - assert(totalFencingPrice(parseGrid(exampleInput)) == 140) - assert(totalFencingPrice(parseGrid(exampleInput2)) == 772) - assert(totalFencingPrice(parseGrid(exampleInput3)) == 1930) + assert(Part1.totalFencingPrice(parseGrid(exampleInput)) == 140) + assert(Part1.totalFencingPrice(parseGrid(exampleInput2)) == 772) + assert(Part1.totalFencingPrice(parseGrid(exampleInput3)) == 1930) } test("Part 1 input answer") { - assert(totalFencingPrice(parseGrid(input)) == 1433460) + assert(Part1.totalFencingPrice(parseGrid(input)) == 1433460) + } + + test("Part 2 examples") { + assert(Part2.totalFencingPrice(parseGrid(exampleInput)) == 80) + assert(Part2.totalFencingPrice(parseGrid(exampleInput2)) == 436) + assert(Part2.totalFencingPrice(parseGrid(exampleInput4)) == 236) + assert(Part2.totalFencingPrice(parseGrid(exampleInput5)) == 368) + assert(Part2.totalFencingPrice(parseGrid(exampleInput3)) == 1206) + } + + test("Part 2 input answer") { + assert(Part2.totalFencingPrice(parseGrid(input)) == 855082) } }