Skip to content

Instantly share code, notes, and snippets.

@bishabosha
Last active November 30, 2023 11:17
Show Gist options
  • Save bishabosha/4ccb226e09940f020fef7ef1a074b29b to your computer and use it in GitHub Desktop.
Save bishabosha/4ccb226e09940f020fef7ef1a074b29b to your computer and use it in GitHub Desktop.
Advent of Code Day 17 2021 using named tuples
//> using scala "3.4.0-RC1-namedtuples-bin-SNAPSHOT"
// original solution taken from https://scalacenter.github.io/scala-advent-of-code/puzzles/day17#final-code (by @bishabosha)
import scala.language.experimental.namedTuples
type Target = (xs: Range, ys: Range)
type Point = (x: Int, y: Int)
type Probe = (position: Point, velocity: Point)
val initial = (x = 0, y = 0)
def step(probe: Probe): Probe =
val (pos, vel) = probe
(
position = (x = pos.x + vel.x, y = pos.y + vel.y),
velocity = (x = vel.x - vel.x.sign, y = vel.y - 1)
)
def collides(probe: Probe, target: Target): Boolean =
val (pos, _) = probe
val (xs, ys) = target
xs.contains(pos.x) && ys.contains(pos.y)
def beyond(probe: Probe, target: Target): Boolean =
val (pos, vel) = probe
val (xs, ys) = target
val beyondX = (vel.x == 0 && pos.x < xs.min) || pos.x > xs.max
val beyondY = vel.y < 0 && pos.y < ys.min
beyondX || beyondY
def simulate(probe: Probe, target: Target): Option[Int] =
LazyList
.iterate((probe, 0))({ case (probe @ (pos, _), maxY) => (step(probe), maxY `max` pos.y)})
.dropWhile((probe, _) => !collides(probe, target) && !beyond(probe, target))
.headOption
.collect { case (probe, maxY) if collides(probe, target) => maxY }
def allMaxHeights(target: Target)(positiveOnly: Boolean): Seq[Int] =
val (xs, ys) = target
val upperBoundY = ys.min.abs
val lowerBoundY = if positiveOnly then 0 else -upperBoundY
for
vx <- 0 to xs.max
vy <- lowerBoundY to upperBoundY
maxy <- simulate((position = initial, velocity = (x = vx, y = vy)), target)
yield
maxy
type Parser[A] = PartialFunction[String, A]
val IntOf: Parser[Int] =
case s if s.matches(raw"-?\d+") => s.toInt
val RangeOf: Parser[Range] =
case s"${IntOf(begin)}..${IntOf(end)}" => begin to end
val Input: Parser[Target] =
case s"target area: x=${RangeOf(xs)}, y=${RangeOf(ys)}" => (xs, ys)
def part1(input: String) =
allMaxHeights(Input(input.trim))(positiveOnly = true).max
def part2(input: String) =
allMaxHeights(Input(input.trim))(positiveOnly = false).size
@main def p1 = println(s"the solution is ${part1(INPUT)}")
@main def p2 = println(s"the solution is ${part2(INPUT)}")
val INPUT = "target area: x=201..230, y=-99..-65"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment