Last active
November 30, 2023 11:17
-
-
Save bishabosha/4ccb226e09940f020fef7ef1a074b29b to your computer and use it in GitHub Desktop.
Advent of Code Day 17 2021 using named tuples
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//> 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