diff --git a/input/2021/day17-sample1.txt b/input/2021/day17-sample1.txt new file mode 100644 index 0000000..a07e02d --- /dev/null +++ b/input/2021/day17-sample1.txt @@ -0,0 +1 @@ +target area: x=20..30, y=-10..-5 diff --git a/input/2021/day17-sample2.txt b/input/2021/day17-sample2.txt new file mode 100644 index 0000000..c115821 --- /dev/null +++ b/input/2021/day17-sample2.txt @@ -0,0 +1 @@ +target area: x=179..201, y=-109..-63 diff --git a/input/2021/day17.txt b/input/2021/day17.txt new file mode 100644 index 0000000..d86dcb1 --- /dev/null +++ b/input/2021/day17.txt @@ -0,0 +1 @@ +target area: x=143..177, y=-106..-71 diff --git a/src/main/scala/de.qwertyuiop.aoc/2021/day17.scala b/src/main/scala/de.qwertyuiop.aoc/2021/day17.scala index 6cf7c46..70c7141 100644 --- a/src/main/scala/de.qwertyuiop.aoc/2021/day17.scala +++ b/src/main/scala/de.qwertyuiop.aoc/2021/day17.scala @@ -1,6 +1,62 @@ package de.qwertyuiop.aoc.`2021` -import de.qwertyuiop.aoc.lib.* +import de.qwertyuiop.aoc.lib.{*, given} +import Vectors.* import cats.*, cats.implicits.given +import ProbeStatus.* -def day17(using InputSource): Unit = ??? +def day17(using InputSource): Unit = + given Ordering[Vec2D[Int]] = (summon[Ordering[(Int,Int)]].asInstanceOf[Ordering[Vec2D[Int]]]) + val s"target area: x=${xMinS}..${xMaxS}, y=${yMinS}..${yMaxS}" = input().head + val (xMin, xMax, yMin, yMax) = (xMinS.toInt, xMaxS.toInt, yMinS.toInt, yMaxS.toInt) + val xRange = xMin to xMax + val yRange = yMin to yMax + val zero = Vec2D(0,0) + + def simulateSteps(pos: Vec2D[Int], xVelocity: Int, yVelocity: Int, visited: List[Vec2D[Int]]): List[Vec2D[Int]] = + if pos.x > xMax || pos.y < yMin then pos :: visited + else if xVelocity == 0 && !xRange.contains(pos.x) then pos:: visited + else + simulateSteps( + pos + Vec2D(xVelocity, yVelocity), + if xVelocity == 0 then 0 else xVelocity- xVelocity.sign, + yVelocity - 1, + pos :: visited + ) + + def minXSpeedRec(start: Int, sum: Int): Int = + if sum < xMin then minXSpeedRec(start + 1, sum + start) + else start -2 + val minXSpeed = minXSpeedRec(0,0) + + def checkSolution(steps: List[Vec2D[Int]]): ProbeStatus = + val overshot :: candidate :: _ = steps + if xRange.contains(candidate.x) && yRange.contains(candidate.y) then Hit + else if overshot.x < xMin then TooNear + else TooFar + + case class Flight(xSpeed: Int, ySpeed: Int, highest: Int) + def highestY(xSpeed: Int) = + val hits = LazyList.range(yMin, yMin + 1000).map(ySpeed => { + val sim = simulateSteps(zero, xSpeed, ySpeed, List()) + (xSpeed, ySpeed, sim, checkSolution(sim)) + }).dropWhile(_._4 != Hit).take(1000).filter(_._4 == Hit) + hits.map{ case (x,y,t,_) => Flight(x, y, y * (y + 1) / 2) } + + val flights = ( + for + xSpeed <- minXSpeed to xMax + 10 + flight <- highestY(xSpeed).toList + yield flight + ) + val Flight(dxMax, dyMax, top) = flights.maxBy(_._3) + println(s"Top height: $top") + println(s"Number of valid shots: ${flights.size}") + + + + +enum ProbeStatus: + case Hit + case TooNear + case TooFar diff --git a/src/main/scala/de.qwertyuiop.aoc/lib/vectors.scala b/src/main/scala/de.qwertyuiop.aoc/lib/vectors.scala index 730eebd..6308d7d 100644 --- a/src/main/scala/de.qwertyuiop.aoc/lib/vectors.scala +++ b/src/main/scala/de.qwertyuiop.aoc/lib/vectors.scala @@ -89,7 +89,6 @@ object Vectors: given [T: Monoid]: Monoid[Vec3D[T]] = semiauto.monoid given [T: Monoid]: Monoid[Vec4D[T]] = semiauto.monoid - given [T: Numeric]: Vec[Vec3D[T]] with extension (v: Vec3D[T]) def +(w: Vec3D[T]): Vec3D[T] = (v._1 + w._1, v._2 + w._2, v._3 + w._3)