diff --git a/aoc/src/main/scala/aoc/y2017/Day10.scala b/aoc/src/main/scala/aoc/y2017/Day10.scala index 4ba2766..af2b67d 100644 --- a/aoc/src/main/scala/aoc/y2017/Day10.scala +++ b/aoc/src/main/scala/aoc/y2017/Day10.scala @@ -22,13 +22,7 @@ object Day10 extends Day { last } - override def part1(input: String): String = { - val shifts = input.split(',').map(_.toInt).toList - val last = runShifts(shifts) - (last.head * last.tail.head).toString - } - - override def part2(input: String): String = { + def knotHash(input: String): String = { val magic = List(17, 31, 73, 47, 23) val codes = input.map(_.toInt) ++ magic val shifts = List.fill(64)(codes).flatten @@ -39,4 +33,14 @@ object Day10 extends Day { hexed.mkString } + override def part1(input: String): String = { + val shifts = input.split(',').map(_.toInt).toList + val last = runShifts(shifts) + (last.head * last.tail.head).toString + } + + override def part2(input: String): String = { + knotHash(input) + } + } diff --git a/aoc/src/main/scala/aoc/y2017/Day13.scala b/aoc/src/main/scala/aoc/y2017/Day13.scala new file mode 100644 index 0000000..447635d --- /dev/null +++ b/aoc/src/main/scala/aoc/y2017/Day13.scala @@ -0,0 +1,34 @@ +package aoc.y2017 + +import aoc.Day + +object Day13 extends Day { + + import fastparse._, SingleLineWhitespace._ + + def parseNumber[_: P]: P[Int] = P(CharIn("0-9").rep(1).!).map(_.toInt) + def parseScanner[_: P]: P[(Int, Int)] = P(parseNumber ~ ": " ~ parseNumber) + def parseScanners[_: P]: P[Map[Int, Int]] = P(parseScanner.rep(1, "\n")).map(Map(_: _*)) + + def scannerAtStart(time: Int, range: Int): Boolean = (time % (2 * (range - 1))) == 0 + + def scanners(input: String): Map[Int, Int] = parse(input, parseScanners(_)).get.value + def severity(scanners: Map[Int, Int], delay: Int): Int = scanners.collect { + case (index, range) if scannerAtStart(index + delay, range) => index * range + }.sum + + override def part1(input: String): String = { + severity(scanners(input), delay = 0).toString + } + + override def part2(input: String): String = { + val parsedScanners = scanners(input) + lazy val packetDelays: LazyList[Int] = LazyList.from(0) + lazy val tripSeverities = packetDelays.map(delay => (delay, severity(parsedScanners, delay))) + val avoidanceDelay = tripSeverities.indexWhere { + case (delay, severity) => severity == 0 && delay % (2 * (parsedScanners(0) - 1)) != 0 + } + avoidanceDelay.toString + } + +} diff --git a/aoc/src/main/scala/aoc/y2017/Day14.scala b/aoc/src/main/scala/aoc/y2017/Day14.scala new file mode 100644 index 0000000..8d5c88a --- /dev/null +++ b/aoc/src/main/scala/aoc/y2017/Day14.scala @@ -0,0 +1,51 @@ +package aoc.y2017 + +import aoc.Day + +object Day14 extends Day { + + def getRows(input: String): Seq[BigInt] = { + val keyStrings = (0 to 127).map(row => s"$input-$row") + val knotHashes = keyStrings.map(Day10.knotHash) + knotHashes.map(BigInt(_, 16)) + } + + override def part1(input: String): String = { + val bigInts = getRows(input) + val bits = bigInts.map(_.bitCount).sum + bits.toString + } + + override def part2(input: String): String = { + val bigInts = getRows(input) + val disk = bigInts.toVector.map { bigInt => + (0 to bigInt.bitLength).toVector.map(bigInt.testBit).padTo(128, false).take(128) + } + type GroupedDisk = Vector[Vector[Option[Int]]] + val numberBlocks: GroupedDisk = disk.zipWithIndex.map { + case (row, y) => row.zipWithIndex.map { + case (true, x) => Some(y * 128 + x) + case (false, _) => None + } + } + def simplifyGroups(disk: GroupedDisk): GroupedDisk = { + disk.zipWithIndex.map { + case (row, y) => row.zipWithIndex.map { + case (None, _) => None + case (Some(g), x) => + val left = if(x > 0) disk(y)(x - 1) else None + val right = if(x < 127) disk(y)(x + 1) else None + val up = if(y > 0) disk(y - 1)(x) else None + val down = if(y < 127) disk(y + 1)(x) else None + Vector(Some(g), left, right, up, down).collect { case Some(v) => v }.minOption + } + } + } + lazy val defrags: LazyList[GroupedDisk] = numberBlocks #:: defrags.map(simplifyGroups) + val (simplest, _) = defrags.zipWithIndex.takeWhile { + case (thisPass, index) => index < 1 || defrags(index - 1) != thisPass + }.last + simplest.flatten.collect { case Some(g) => g }.groupBy(identity).size.toString + } + +} diff --git a/aoc/src/main/scala/aoc/y2017/Day15.scala b/aoc/src/main/scala/aoc/y2017/Day15.scala new file mode 100644 index 0000000..49a94f3 --- /dev/null +++ b/aoc/src/main/scala/aoc/y2017/Day15.scala @@ -0,0 +1,48 @@ +package aoc.y2017 + +import aoc.Day + +import scala.annotation.tailrec + +object Day15 extends Day { + + import fastparse._, NoWhitespace._ + + def parseNumber[_: P]: P[Int] = P(CharIn("0-9").rep(1).!).map(_.toInt) + def parseGenerator[_: P](name: String): P[Int] = P("Generator " ~ name ~ " starts with " ~ parseNumber) + def parseGenerators[_: P]: P[(Int, Int)] = P(parseGenerator("A") ~ "\n" ~ parseGenerator("B")) + + @tailrec def step(number: Int, multiplier: Int, forceMultiple: Int = 1): Int = { + val product = number.toLong * multiplier.toLong + val remainder = (product % Int.MaxValue).toInt + if(remainder % forceMultiple != 0) step(remainder, multiplier, forceMultiple) + else remainder + } + + @tailrec def go(genANum: Int, genBNum: Int, genAMult: Int, genBMult: Int, forceMultipleA: Int = 1, forceMultipleB: Int = 1, left: Int = 40_000_000, sum: Int = 0): Int = { + if(left < 0) sum + else { + val nextA = step(genANum, genAMult, forceMultipleA) + val nextB = step(genBNum, genBMult, forceMultipleB) + val nextLeft = left - 1 + val nextSum = if(((nextA ^ nextB) & ((1 << 16) - 1)) == 0) sum + 1 else sum + go(nextA, nextB, genAMult, genBMult, forceMultipleA, forceMultipleB, nextLeft, nextSum) + } + } + + def run(input: String, pairs: Int, forceMultiple: Boolean): Int = { + val genAMult = 16807 + val genBMult = 48271 + val (genAStart, genBStart) = parse(input, parseGenerators(_)).get.value + go(genAStart, genBStart, genAMult, genBMult, if(forceMultiple) 4 else 1, if(forceMultiple) 8 else 1, pairs) + } + + override def part1(input: String): String = { + run(input, pairs = 40_000_000, forceMultiple = false).toString + } + + override def part2(input: String): String = { + run(input, pairs = 5_000_000, forceMultiple = true).toString + } + +} diff --git a/aoc/src/main/scala/aoc/y2017/package.scala b/aoc/src/main/scala/aoc/y2017/package.scala index 4536417..fdaf43a 100644 --- a/aoc/src/main/scala/aoc/y2017/package.scala +++ b/aoc/src/main/scala/aoc/y2017/package.scala @@ -15,7 +15,10 @@ package object y2017 extends Year { "10" -> Day10, "11" -> Day11, "12" -> Day12, - "25" -> Day25 + "13" -> Day13, + "14" -> Day14, + "15" -> Day15, + "25" -> Day25, ) }