Skip to content

Instantly share code, notes, and snippets.

@waynejo
Created January 12, 2024 13:31
Show Gist options
  • Save waynejo/f8f48edc834302682e63da00f55926bb to your computer and use it in GitHub Desktop.
Save waynejo/f8f48edc834302682e63da00f55926bb to your computer and use it in GitHub Desktop.
import java.io.FileInputStream
import scala.annotation.tailrec
import scala.io.StdIn
case class Input(seedInput: Vector[BigInt], mapping: Vector[MappingGroup])
case class Range(start: BigInt, length: BigInt)
case class Mapping(source: Range, destination: Range)
case class MappingGroup(mappings: Vector[Mapping]):
@tailrec
private def _applyMapping(remain: Vector[Range], remainMappings: Vector[Mapping], acc: Vector[Range]): Vector[Range] =
(remain, remainMappings) match
case (Vector(), _) => acc
case (_, Vector()) => acc ++ remain
case (v +: vs, m +: ms) =>
if v.start < m.source.start then
val newMapped = Range(v.start, (m.source.start - v.start) min v.length)
val newValue = Range(m.source.start, v.start + v.length - m.source.start)
_applyMapping((newValue +: vs).filter(0 < _.length), remainMappings, acc :+ newMapped)
else if v.start >= m.source.start + m.source.length then
_applyMapping(v +: vs, ms, acc)
else
val newMappedSrc = Range(v.start, (m.source.start + m.source.length) - v.start min v.length)
val newMapped = Range(newMappedSrc.start + (m.destination.start - m.source.start), newMappedSrc.length)
val newValue = Range(m.source.start + m.source.length, (v.start + v.length) - (m.source.start + m.source.length) min v.length)
_applyMapping((newValue +: vs).filter(0 < _.length), remainMappings, (acc :+ newMapped).filter(0 < _.length))
def applyMapping(v: Vector[Range]): Vector[Range] =
_applyMapping(v.sortBy(_.start), mappings, Vector.empty).sortBy(_.start)
object MappingGroup:
def apply(mappings: Vector[Mapping]): MappingGroup =
new MappingGroup(mappings.sortBy(_.source.start))
def parseInput(inputs: Vector[String]): Input =
val seeds = inputs.head.split(" ").tail.map(BigInt.apply).toVector
def _parseGroupStart(inputs: Vector[String], acc: Input): Input =
_parseGroupBody(inputs.tail, acc, Vector.empty)
@tailrec
def _parseGroupBody(inputs: Vector[String], acc: Input, mappings: Vector[Mapping]): Input =
inputs.headOption.map(_.trim) match
case None => acc.copy(mapping = acc.mapping :+ MappingGroup(mappings))
case Some("") => _parseGroupStart(inputs.tail, acc.copy(mapping = acc.mapping :+ MappingGroup(mappings)))
case _ =>
val Array(destination, source, length) = inputs.head.split(" ").map(BigInt.apply)
_parseGroupBody(inputs.tail, acc, mappings :+ Mapping(Range(source, length), Range(destination, length)))
_parseGroupStart(inputs.drop(2), Input(seeds, Vector.empty))
def solve5_1(input: Input): BigInt =
val seeds = input.seedInput.map(Range(_, 1))
input.mapping.foldLeft(seeds) { (seeds, group) =>
group.applyMapping(seeds)
}.head.start
def solve5_2(input: Input): BigInt =
val seeds = input.seedInput.grouped(2).map(v => Range(v.head, v.last)).toVector
input.mapping.foldLeft(seeds) { (seeds, group) =>
group.applyMapping(seeds)
}.head.start
@main def solve5(): Unit =
val in = new FileInputStream("example5-2.in")
System.setIn(in)
val inputs = Iterator.continually(StdIn.readLine())
.takeWhile(line => null != line)
.toVector
println(solve5_1(parseInput(inputs)))
println(solve5_2(parseInput(inputs)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment