Scala3
case class Part(x: Range, m: Range, a: Range, s: Range):
def rating: Int = x.start + m.start + a.start + s.start
def combinations: Long = x.size.toLong * m.size.toLong * a.size.toLong * s.size.toLong
type ActionFunc = Part => (Option[(Part, String)], Option[Part])
case class Workflow(ops: List[ActionFunc]):
def process(p: Part): List[(Part, String)] =
@tailrec def go(p: Part, ops: List[ActionFunc], acc: List[(Part, String)]): List[(Part, String)] =
ops match
case o :: t => o(p) match
case (Some(branch), Some(fwd)) => go(fwd, t, branch::acc)
case (None, Some(fwd)) => go(fwd, t, acc)
case (Some(branch), None) => branch::acc
case (None, None) => acc
case _ => acc
go(p, ops, List())
def run(parts: List[Part], workflows: Map[String, Workflow]) =
@tailrec def go(parts: List[(Part, String)], accepted: List[Part]): List[Part] =
parts match
case (p, wf) :: t =>
val res = workflows(wf).process(p)
val (acc, rest) = res.partition((_, w) => w == "A")
val (rej, todo) = rest.partition((_, w) => w == "R")
go(todo ++ t, acc.map(_._1) ++ accepted)
case _ => accepted
go(parts.map(_ -> "in"), List())
def parseWorkflows(a: List[String]): Map[String, Workflow] =
def generateActionGt(n: Int, s: String, accessor: Part => Range, setter: (Part, Range) => Part): ActionFunc = p =>
val r = accessor(p)
(Option.when(r.end > n + 1)((setter(p, math.max(r.start, n + 1) until r.end), s)), Option.unless(r.start > n)(setter(p, r.start until math.min(r.end, n + 1))))
def generateAction(n: Int, s: String, accessor: Part => Range, setter: (Part, Range) => Part): ActionFunc = p =>
val r = accessor(p)
(Option.when(r.start < n)((setter(p, r.start until math.min(r.end, n)), s)), Option.unless(r.end <= n)(setter(p, math.max(r.start, n) until r.end)))
val accessors = Map("x"->((p:Part) => p.x), "m"->((p:Part) => p.m), "a"->((p:Part) => p.a), "s"->((p:Part) => p.s))
val setters = Map("x"->((p:Part, v:Range) => p.copy(x=v)), "m"->((p:Part, v:Range) => p.copy(m=v)), "a"->((p:Part, v:Range) => p.copy(a=v)), "s"->((p:Part, v:Range) => p.copy(s=v)))
def parseAction(a: String): ActionFunc =
a match
case s"$v<$n:$s" => generateAction(n.toInt, s, accessors(v), setters(v))
case s"$v>$n:$s" => generateActionGt(n.toInt, s, accessors(v), setters(v))
case s => p => (Some((p, s)), None)
a.map(_ match{ case s"$name{$items}" => name -> Workflow(items.split(",").map(parseAction).toList) }).toMap
def parsePart(a: String): Option[Part] =
a match
case s"{x=$x,m=$m,a=$a,s=$s}" => Some(Part(x.toInt until 1+x.toInt, m.toInt until 1+m.toInt, a.toInt until 1+a.toInt, s.toInt until 1+s.toInt))
case _ => None
def task1(a: List[String]): Long =
val in = a.chunk(_ == "")
val wfs = parseWorkflows(in(0))
val parts = in(1).flatMap(parsePart)
run(parts, wfs).map(_.rating).sum
def task2(a: List[String]): Long =
val wfs = parseWorkflows(a.chunk(_ == "").head)
val parts = List(Part(1 until 4001, 1 until 4001, 1 until 4001, 1 until 4001))
run(parts, wfs).map(_.combinations).sum
C++, kind of
Ok so this is a little weird. My code for task1 is attached to this comment, but I actually solved task2 by hand. After checking that bruteforce indeed takes longer than a second, I plotted the graph just to see what was going on, and you can immediately tell that the result is the least common multiple of four numbers, which can easily be obtained by running task1 with a debugger, and maybe read directly from the graph as well. I also pre-broke my include statements, so hopefully the XSS protection isn't completely removing them again.
My graph: https://files.catbox.moe/1u4daw.png
blue is the broadcaster/button, yellows are flipflops, purples are nand gates and green is the output gate.
Also I abandoned scala again, because there is so much state modification going on.