Skip to content

Commit

Permalink
Solve 2023 day 20 part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
sim642 committed Dec 20, 2023
1 parent 2cebbd5 commit f6c1689
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 0 deletions.
58 changes: 58 additions & 0 deletions src/main/resources/eu/sim642/adventofcode2023/day20.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
%bc -> ph
%hr -> zq
%sn -> sj
%df -> dn, hf
%lp -> dm
%lf -> sn, vk
&fv -> sq
%gd -> vm, gr
%jt -> vm, lz
%xf -> vk
%nf -> lp, kb
%dl -> nn
&sq -> rx
%vb -> vm, lg
%zq -> kb, fl
%fk -> fz
%gj -> pq
%jx -> jh
%pq -> kb, nf
&dn -> kg, vt, tg, fs, pn, jx
%ln -> dn, fs
%fz -> vm, jt
%fs -> df
%dm -> kb, hr
%ds -> kb
&kk -> sq
%tg -> tv
&vt -> sq
%fl -> zl, kb
&vk -> bc, sj, jd, lf, xr, sn
%jd -> pp
%tv -> dn, pn
%sb -> gf, vk
&kb -> kk, gj, gt, hr, lp
%pp -> vk, bc
%pn -> pf
%zc -> vm
&vm -> dl, fk, nn, fv, gd, lg
%rn -> dn
%gr -> vb, vm
%sj -> kq
%zl -> kb, ds
%lz -> vm, zc
%jh -> dn, ln
%pf -> dn, jx
%kq -> sb, vk
%ph -> md, vk
%nc -> gj, kb
%kg -> tg, dn
%hf -> dn, rn
%nn -> fk
%gf -> jd, vk
%lg -> pv
broadcaster -> gd, kg, gt, lf
%gt -> nc, kb
%pv -> dl, vm
&xr -> sq
%md -> vk, xf
110 changes: 110 additions & 0 deletions src/main/scala/eu/sim642/adventofcode2023/Day20.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package eu.sim642.adventofcode2023

import scala.collection.mutable

object Day20 {

enum Pulse {
case Low
case High
}

sealed trait Module {
def handle(from: String, pulse: Pulse): (Module, Option[Pulse])
}

case class FlipFlop(state: Boolean = false) extends Module {
override def handle(from: String, pulse: Pulse): (Module, Option[Pulse]) = pulse match {
case Pulse.High => (this, None)
case Pulse.Low => (FlipFlop(!state), Some(if (!state) Pulse.High else Pulse.Low))
}
}

case class Conjunction(remember: Map[String, Pulse] = Map.empty) extends Module { // TODO: add initial Lows
override def handle(from: String, pulse: Pulse): (Module, Option[Pulse]) = {
val newRemember = remember.updated(from, pulse)
val outPulse = if (newRemember.forall(_._2 == Pulse.High)) Pulse.Low else Pulse.High
(Conjunction(newRemember), Some(outPulse))
}
}

case object Broadcast extends Module {
override def handle(from: String, pulse: Pulse): (Module, Option[Pulse]) =
(this, Some(pulse))
}

type Circuit = Map[String, (Module, Seq[String])]

def countPulses(circuit: Circuit): Int = {
val mCircuit = circuit.to(mutable.Map)

var lowPulses = 0
var highPulses = 0

for (i <- 0 until 1000) {

val queue = mutable.Queue.empty[(String, Pulse, String)]
queue.enqueue(("button", Pulse.Low, "broadcaster"))

while (queue.nonEmpty) {
val s@(from, pulse, to) = queue.dequeue()
//println(s)

pulse match {
case Pulse.Low => lowPulses += 1
case Pulse.High => highPulses += 1
}

mCircuit.get(to) match {
case None => () // output
case Some(module, outputs) =>
val (newModule, outPulse) = module.handle(from, pulse)
mCircuit(to) = (newModule, outputs)

for {
pulse <- outPulse
output <- outputs
} queue.enqueue((to, pulse, output))
}
}
}

println((lowPulses, highPulses))
lowPulses * highPulses
}


def parseModule(s: String): (String, (Module, Seq[String])) = s match {
case s"$moduleStr -> $outputsStr" =>
val (name, module) = moduleStr match {
case s"broadcaster" => ("broadcaster", Broadcast)
case s"%$name" => (name, FlipFlop())
case s"&$name" => (name, Conjunction())
}
val outputs = outputsStr.split(", ").toSeq
(name, (module, outputs))
}

def initializeConjunctions(circuit: Circuit): Circuit = {
//println(circuit)
val inputs = (for {
(module, (_, outputs)) <- circuit.view // TODO: why need view?
output <- outputs
} yield module -> output).groupMap(_._2)(_._1)
//println(inputs)

circuit.map({
case (module, (Conjunction(_), outputs)) =>
module -> (Conjunction(inputs(module).map(_ -> Pulse.Low).toMap), outputs)
case other => other
})
}

def parseCircuit(input: String): Circuit = initializeConjunctions(input.linesIterator.map(parseModule).toMap)

lazy val input: String = scala.io.Source.fromInputStream(getClass.getResourceAsStream("day20.txt")).mkString.trim

def main(args: Array[String]): Unit = {
println(countPulses(parseCircuit(input)))
}
}
30 changes: 30 additions & 0 deletions src/test/scala/eu/sim642/adventofcode2023/Day20Test.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package eu.sim642.adventofcode2023

import Day20._
import org.scalatest.funsuite.AnyFunSuite

class Day20Test extends AnyFunSuite {

private val exampleInput =
"""broadcaster -> a, b, c
|%a -> b
|%b -> c
|%c -> inv
|&inv -> a""".stripMargin

private val exampleInput2 =
"""broadcaster -> a
|%a -> inv, con
|&inv -> b
|%b -> con
|&con -> output""".stripMargin

test("Part 1 examples") {
assert(countPulses(parseCircuit(exampleInput)) == 32000000)
assert(countPulses(parseCircuit(exampleInput2)) == 11687500)
}

test("Part 1 input answer") {
assert(countPulses(parseCircuit(input)) == 867118762)
}
}

0 comments on commit f6c1689

Please sign in to comment.