Day 22
This commit is contained in:
parent
ad6a55720a
commit
6a63f5d32e
82
Day22.scala
Normal file
82
Day22.scala
Normal file
|
@ -0,0 +1,82 @@
|
|||
package aoc.day22
|
||||
|
||||
import aoc._
|
||||
import scala.collection.mutable
|
||||
|
||||
case class Point3(x: Int, y: Int, z: Int):
|
||||
def min(other: Point3) = Point3(x.min(other.x), y.min(other.y), z.min(other.z))
|
||||
def max(other: Point3) = Point3(x.max(other.x), y.max(other.y), z.max(other.z))
|
||||
|
||||
def to2D = (x, y)
|
||||
|
||||
case class Brick private (index: Int, low: Point3, high: Point3):
|
||||
inline def height = high.z - low.z + 1
|
||||
def to2D = (low.to2D, high.to2D)
|
||||
|
||||
object Brick:
|
||||
private var index = 0
|
||||
def fromPoints(a: Point3, b: Point3) =
|
||||
index += 1
|
||||
Brick(index, a.min(b), a.max(b))
|
||||
|
||||
object Parser extends CommonParser:
|
||||
val point3 = num ~ "," ~ num ~ "," ~ num ^^ { case x ~ _ ~ y ~ _ ~ z => Point3(x, y, z) }
|
||||
val brick = point3 ~ "~" ~ point3 ^^ { case a ~ _ ~ b => Brick.fromPoints(a, b) }
|
||||
|
||||
val bricks =
|
||||
lines
|
||||
.map(Parser.parse(Parser.brick, _).get)
|
||||
.toSeq
|
||||
|
||||
// part 1
|
||||
|
||||
class Space:
|
||||
case class Cell(height: Int, occupied: Int /* index of the occupied brick */ )
|
||||
val space = mutable.Map.empty[(Int, Int), Cell]
|
||||
|
||||
// Override a brick falling and returns the height and unique brick that holds it, if it exists.
|
||||
def overrideBrick(b: Brick): (Int, Option[Int]) =
|
||||
// the space covered is
|
||||
val covered = (b.low.x to b.high.x).flatMap(x => (b.low.y to b.high.y).map((x, _)))
|
||||
// get the maximum height occupied
|
||||
val pastCells = covered.flatMap(space.get(_))
|
||||
val maxHeight = pastCells.map(_.height).maxOption.getOrElse(0)
|
||||
assert(maxHeight <= b.low.z)
|
||||
// update space
|
||||
covered.foreach(space.update(_, Cell(maxHeight + b.height, b.index)))
|
||||
// get the ones held
|
||||
val res = pastCells.filter(_.height == maxHeight).map(_.occupied).toSet.toSeq match
|
||||
case Seq(idx) => Some(idx)
|
||||
case _ => None
|
||||
(maxHeight, res)
|
||||
|
||||
// sort bricks by low z
|
||||
val sortedBricks = bricks.sortBy(_.low.z)
|
||||
|
||||
def part1() =
|
||||
val space = Space()
|
||||
val holdingBricks = sortedBricks.foldLeft(Set.empty[Int])(_ ++ space.overrideBrick(_)._2)
|
||||
println(bricks.size - holdingBricks.size)
|
||||
|
||||
def part2() =
|
||||
extension (bs: Iterator[Brick])
|
||||
def heights =
|
||||
val space = Space()
|
||||
bs.map(b => (b -> space.overrideBrick(b)._1)).toMap
|
||||
extension (bm: Map[Brick, Int])
|
||||
inline def countDifferences(original: Map[Brick, Int]) =
|
||||
bm.count((b, h) => h != original(b))
|
||||
|
||||
val original = sortedBricks.iterator.heights
|
||||
val res = bricks
|
||||
.map: exclude =>
|
||||
sortedBricks.iterator
|
||||
.filter(_ != exclude)
|
||||
.heights
|
||||
.countDifferences(original)
|
||||
.sum
|
||||
println(res)
|
||||
|
||||
@main def Day22(part: Int) = part match
|
||||
case 1 => part1()
|
||||
case 2 => part2()
|
1471
inputs/day22.input
Normal file
1471
inputs/day22.input
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue