Day 20
This commit is contained in:
parent
6e3628e669
commit
4408075c94
2 changed files with 198 additions and 0 deletions
58
input/day20
Normal file
58
input/day20
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
%dj -> fj, jn
|
||||
%xz -> cm
|
||||
%fn -> rj
|
||||
%fv -> nt, zp
|
||||
%ls -> ph, cf
|
||||
%rk -> zp, tp
|
||||
&jn -> km, cr, vz
|
||||
%nh -> ph, ls
|
||||
%tx -> gb
|
||||
%xg -> dv, zp
|
||||
%tp -> gx
|
||||
&zp -> kj, kz, gx, fv, lv, tp
|
||||
&gq -> rx
|
||||
%fj -> sl, jn
|
||||
%cr -> vz, jn
|
||||
%rt -> fn, mf
|
||||
%kj -> tt
|
||||
%tk -> mg, ph
|
||||
%xt -> jn, gh
|
||||
%qx -> bx
|
||||
%lv -> sx
|
||||
%nz -> dp, ph
|
||||
%sx -> kj, zp
|
||||
%dd -> bf
|
||||
%gb -> jp
|
||||
%bj -> ph, nn
|
||||
%sk -> mf
|
||||
%bx -> tx, mf
|
||||
%mt -> xg, zp
|
||||
%vz -> hf
|
||||
%vx -> mf, sk
|
||||
%tt -> mt, zp
|
||||
%br -> jn, fk
|
||||
&xj -> gq
|
||||
%mg -> ph, ps
|
||||
%nt -> zp, rk
|
||||
&qs -> gq
|
||||
%rj -> qx, mf
|
||||
%bf -> vx, mf
|
||||
&kz -> gq
|
||||
%fk -> jn, gk
|
||||
%dv -> zp
|
||||
%dp -> ph
|
||||
&mf -> gb, tx, xj, dd, qx, rt, fn
|
||||
&ph -> nn, xz, tk, ps, qs
|
||||
%ps -> xz
|
||||
&km -> gq
|
||||
broadcaster -> fv, cr, rt, tk
|
||||
%gk -> jn, xt
|
||||
%cf -> ph, nz
|
||||
%tl -> jn, br
|
||||
%cm -> bj, ph
|
||||
%nn -> nh
|
||||
%jp -> mf, dd
|
||||
%gh -> jn, dj
|
||||
%hf -> tl, jn
|
||||
%sl -> jn
|
||||
%gx -> lv
|
||||
140
src/day20.scala
Normal file
140
src/day20.scala
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package aoc
|
||||
package day20
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.mutable
|
||||
import scala.util.boundary
|
||||
|
||||
val dayNumber = "20"
|
||||
|
||||
@main def part1: Unit =
|
||||
println(part1(loadInput(dayNumber)))
|
||||
|
||||
@main def part2: Unit =
|
||||
println(part2(loadInput(dayNumber)))
|
||||
|
||||
enum Pulse:
|
||||
case High, Low
|
||||
|
||||
enum Module:
|
||||
case FlipFlop(var on: Boolean = false)
|
||||
case Conjuction(var seenPulses: Map[String, Pulse] = Map.empty)
|
||||
case Broadcast
|
||||
case Sink(var lowCount: Long = 0, var highCount: Long = 0)
|
||||
object Module:
|
||||
def parseAll(str: String): Map[String, (Module, List[String])] =
|
||||
val modules = str
|
||||
.split('\n')
|
||||
.map {
|
||||
case s"broadcaster -> $targets" =>
|
||||
Module.Broadcast.toString -> (Module.Broadcast -> targets
|
||||
.split(',')
|
||||
.map(_.trim)
|
||||
.toList)
|
||||
case s"%$name -> $targets" =>
|
||||
name -> (Module.FlipFlop() -> targets.split(',').map(_.trim).toList)
|
||||
case s"&$name -> $targets" =>
|
||||
name -> (Module.Conjuction() -> targets.split(',').map(_.trim).toList)
|
||||
}
|
||||
.toMap
|
||||
|
||||
modules.foreach {
|
||||
case (name, (module: Module.Conjuction, _)) =>
|
||||
module.seenPulses = modules.collect {
|
||||
case (sourceName, (_, targets)) if targets.contains(name) =>
|
||||
(sourceName -> Pulse.Low)
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
val sinks = modules.values
|
||||
.flatMap(_._2)
|
||||
.toList
|
||||
.distinct
|
||||
.filterNot(modules.contains)
|
||||
.map(_ -> (Module.Sink(), List.empty))
|
||||
modules ++ sinks
|
||||
|
||||
def processPulse(
|
||||
source: String,
|
||||
pulse: Pulse,
|
||||
moduleName: String,
|
||||
modules: Map[String, (Module, List[String])]
|
||||
): List[(String, Pulse, String)] =
|
||||
val (module, targets) = modules(moduleName)
|
||||
module match
|
||||
case Module.Broadcast =>
|
||||
targets.map((moduleName, pulse, _))
|
||||
case module: Module.Sink =>
|
||||
if pulse == Pulse.Low then module.lowCount += 1
|
||||
else if pulse == Pulse.High then module.highCount += 1
|
||||
List.empty
|
||||
case module @ Module.FlipFlop(true) if pulse == Pulse.Low =>
|
||||
module.on = false
|
||||
targets.map((moduleName, Pulse.Low, _))
|
||||
case module @ Module.FlipFlop(false) if pulse == Pulse.Low =>
|
||||
module.on = true
|
||||
targets.map((moduleName, Pulse.High, _))
|
||||
case Module.FlipFlop(_) => List.empty
|
||||
case module: Module.Conjuction =>
|
||||
module.seenPulses = module.seenPulses.updated(source, pulse)
|
||||
if module.seenPulses.values.forall(_ == Pulse.High) then
|
||||
targets.map((moduleName, Pulse.Low, _))
|
||||
else targets.map((moduleName, Pulse.High, _))
|
||||
|
||||
def part1(input: String): String =
|
||||
val modules = Module.parseAll(input)
|
||||
var lowCount = 0L
|
||||
var highCount = 0L
|
||||
|
||||
def pushButton =
|
||||
val queue = mutable.Queue(("Button", Pulse.Low, Module.Broadcast.toString))
|
||||
while (queue.nonEmpty)
|
||||
val (source, pulse, moduleName) = queue.dequeue
|
||||
pulse match
|
||||
case Pulse.High => highCount += 1
|
||||
case Pulse.Low => lowCount += 1
|
||||
queue.enqueueAll(processPulse(source, pulse, moduleName, modules))
|
||||
|
||||
for _ <- 1 to 1000 do pushButton
|
||||
|
||||
(lowCount * highCount).toString
|
||||
|
||||
def part2(input: String): String =
|
||||
|
||||
def findModuleSources(
|
||||
name: String,
|
||||
modules: Map[String, (Module, List[String])]
|
||||
): List[String] =
|
||||
modules.collect {
|
||||
case (sourceName, (_, targets)) if targets.contains(name) => sourceName
|
||||
}.toList
|
||||
|
||||
def countButtonPushesUntilHighPulseFor(targetModuleName: String): Long =
|
||||
val modules = Module.parseAll(input)
|
||||
|
||||
boundary:
|
||||
for pushes <- 1L to Int.MaxValue do
|
||||
val queue =
|
||||
mutable.Queue(("Button", Pulse.Low, Module.Broadcast.toString))
|
||||
while (queue.nonEmpty)
|
||||
val (source, pulse, moduleName) = queue.dequeue
|
||||
if source == targetModuleName && pulse == Pulse.High then
|
||||
boundary.break(pushes)
|
||||
queue.enqueueAll(processPulse(source, pulse, moduleName, modules))
|
||||
throw RuntimeException(
|
||||
s"Module $targetModuleName never receives a high pulse"
|
||||
)
|
||||
|
||||
@tailrec def gcd(a: Long, b: Long): Long =
|
||||
if (b == 0) a.abs else gcd(b, a % b)
|
||||
def lcm(a: Long, b: Long) =
|
||||
(a * b).abs / gcd(a, b)
|
||||
|
||||
val modules = Module.parseAll(input)
|
||||
val rxSource = findModuleSources("rx", modules).head
|
||||
val sourceConjunctions = findModuleSources(rxSource, modules)
|
||||
|
||||
sourceConjunctions
|
||||
.map(countButtonPushesUntilHighPulseFor)
|
||||
.reduce(lcm)
|
||||
.toString
|
||||
Loading…
Add table
Add a link
Reference in a new issue