diff --git a/aoc/src/dev/ctsk/aoc/Main.scala b/aoc/src/dev/ctsk/aoc/Main.scala index f411d34..68bb3a4 100644 --- a/aoc/src/dev/ctsk/aoc/Main.scala +++ b/aoc/src/dev/ctsk/aoc/Main.scala @@ -26,6 +26,7 @@ val solvers = Map[Int, Solver]( 21 -> Day21, 22 -> Day22, 23 -> Day23, + 24 -> Day24, 25 -> Day25 ) diff --git a/aoc/src/dev/ctsk/aoc/days/Day24.scala b/aoc/src/dev/ctsk/aoc/days/Day24.scala new file mode 100644 index 0000000..5e6fcaf --- /dev/null +++ b/aoc/src/dev/ctsk/aoc/days/Day24.scala @@ -0,0 +1,247 @@ +package dev.ctsk.aoc.days + +import dev.ctsk.aoc.* + +import scala.collection.mutable + +object Day24 extends Solver(24): + extension (s: String) + def binLong: Long = s.foldLeft(0L)((acc, c) => (acc << 1) + c.asDigit) + + enum Op(val f: Int => Int => Int): + case AND extends Op(x => y => x & y) + case OR extends Op(x => y => x | y) + case XOR extends Op(x => y => x ^ y) + + case class Gate(op: Op, lhs: String, rhs: String) + object Gate: + def of(op: Op, lhs: String, rhs: String): Gate = + Gate(op, Seq(lhs, rhs).min, Seq(lhs, rhs).max) + + override def run(input: os.ReadablePath): (Timings, Solution) = + val Array(inits_, wires_) = os.read(input).split("\n\n") + + val inits = inits_.linesIterator + .map(_.split(": ")) + .map { case Array(id, value) => (id, value.toInt) } + .toMap + + val wires = wires_.linesIterator + .map(_.split(" ")) + .map { case Array(in1, op, in2, _, out) => + (out, Gate.of(Op.valueOf(op), in1, in2)) + } + .toArray + + val outs = wires.toMap + val ins = mutable.Map.from(wires.map(_.swap)) + + def tag(t: Char)(i: Int): String = f"$t$i%02d" + val x = tag('x'); val y = tag('y'); val z = tag('z') + + def resolve_(id: String, f: String => Int): Int = + outs.get(id) match + case Some(gate) => gate.op.f(f(gate.lhs))(f(gate.rhs)) + case _ => inits(id) + val resolve = Memo.Y(resolve_) + + val p1 = timed { (0 to 45).reverse.map(z).map(resolve).mkString.binLong } + + val p2 = timed { + val renames = mutable.Map.empty[String, String] + def rename(a: String, b: String): Unit = + renames(a) = b + renames(b) = a + def re(a: String): String = renames.getOrElse(a, a) + def lookup(gate: Gate): Option[String] = + ins.get(Gate.of(gate.op, re(gate.lhs), re(gate.rhs))) + + var carry = ins(Gate.of(Op.AND, x(0), y(0))) + for i <- 1 to 44 + do + println((i, carry)) + val x_ = x(i) + val y_ = y(i) + val x_y_xor = lookup(Gate.of(Op.XOR, x_, y_)).get + val x_y_and = lookup(Gate.of(Op.AND, x_, y_)).get + val out = lookup(Gate.of(Op.XOR, x_y_xor, carry)) match + case Some(out) => out + case None => + rename(x_y_xor, x_y_and) + lookup(Gate.of(Op.XOR, x_y_xor, carry)).get + if out != z(i) then rename(out, z(i)) + val pre_carry = lookup(Gate.of(Op.AND, carry, x_y_xor)).get + carry = lookup(Gate.of(Op.OR, pre_carry, x_y_and)).get + + renames.keys.toArray.sorted.mkString(",") + } + + ( + Timings(0, p1._1, p2._1), + Solution(Long.box(p1._2), p2._2) + ) + +// def construct_(id: String, f: String => Node): Node = +// wires.get(id) match +// case Some(l, r, op) => Node.Inner(id, Op.valueOf(op), f(l), f(r)) +// case None => Node.Leaf(id) +// val construct = Memo.Y(construct_) + +// val ends = wires.keySet.map(construct) +// val nodes = ends ++ +// ends.collect { case Node.Inner(_, _, l, r) => Set(l, r) }.flatten + +// def downstream_(node: Node): Set[Node] = +// nodes.collect { +// case inner: Node.Inner if inner.lhs.id == node.id => inner +// case inner: Node.Inner if inner.rhs.id == node.id => inner +// } +// val downstream = Memo(downstream_) + +// def tag(t: Char)(i: Int): String = f"$t$i%02d" + +// val x = tag('x'); +// val y = tag('y'); +// val z = tag('z'); +// var lastCarry = ends.find(op(Op.AND, leaf(x(0)), leaf(y(0)))).get + +// for i <- 1 to 44 do +// val out = op(Op.XOR, op(Op.XOR, leaf(x(i)), leaf(y(i))), _ => true) +// val mkCarry = op( +// Op.OR, +// op(Op.AND, leaf(x(i)), leaf(y(i))), +// op( +// Op.AND, +// op(Op.XOR, leaf(x(i)), leaf(y(i))), +// { +// case Node.Inner(id, _, _, _) => id == lastCarry.id +// case _ => false +// } +// ) +// ) +// +// ends +// .find(out) match +// case Some(node) => () +// case None => +// println( +// ( +// z(i), +// wires(z(i)), +// ends.find(end => +// op(Op.XOR, leaf(x(i)), leaf(y(i)))(construct(end)) +// ) +// ) +// ) +// +// def dependencies_(id: String, f: String => Set[String]): Set[String] = +// wires.get(id) match +// case Some(in1, in2, op) => f(in1) ++ f(in2) +// case None => Set(id) +// val dependencies = Memo.Y(dependencies_) +// +// val allDeps = wires.keySet.groupBy(dependencies) + +// val carries = wires.filter((k, v) => v._3 == "OR").keySet + +// val xs = (0 to 44).map(x).toSet +// val ys = (0 to 44).map(y).toSet +// val zs = (0 to 45).map(z).toSet + +// (zs - "z45") +// def tag(i: Int): (String, String) = +// if i == 0 +// then +// val candidates = revDependencies(Set("x00", "y00")) +// val output = candidates.find(wires(_)._3 == "XOR") +// val carry = candidates.find(wires(_)._3 == "AND") +// (output, carry) +// else +// val candidates = revDependencies(Set("x00", "y00")) +// val (pOutput, pCarry) = tag(i - 1) + +// dependencies() +// +// def resolve_(id: String, f: String => Int): Int = +// wires.get(id) match +// case Some(in1, in2, "XOR") => f(in1) ^ f(in2) +// case Some(in1, in2, "OR") => f(in1) | f(in2) +// case Some(in1, in2, "AND") => f(in1) & f(in2) +// case _ => inits(id) +// val resolve = Memo.Y(resolve_) +// +// val p1 = timed { (0 to 45).reverse.map(z).map(resolve).mkString.binLong } +// +// val clear = +// (for +// i <- 0 to 44 +// v <- Seq("x", "y") +// yield f"$v${i}%02d" -> 0).toMap + +// for i <- 0 to 44 do +// for +// y <- 0 to 1 +// x <- 0 to 1 +// do +// val z = resolve( +// clear ++ +// Seq( +// f"y${i}%02d" -> y, +// f"x${i}%02d" -> x +// ).toMap, +// f"z${i}%02d" +// ) +// val carry = resolve( +// clear ++ Seq( +// f"y${i}%02d" -> y, +// f"x${i}%02d" -> x +// ).toMap, +// f"z${i + 1}%02d" +// ) +// +// if z != (x ^ y) then println(f"z$i") +// if carry != (x & y) then println(f"carry$i") +// assert(z == (x ^ y)) +// assert(carry == (x & y)) + +// val outs = wires.keySet +// .filter(_(0) == 'z') +// .map(out => (out, resolve(out))) +// .toArray +// .sorted +// .map(_._2) + +// +// wires.foreach { +// case (id, (in1, in2, op)) => +// val col = op match +// case "XOR" => "[color=\"red\"]" +// case "OR" => "[color=\"blue\"]" +// case "AND" => "[color=\"green\"]" +// println(f"$in1 -> $id $col") +// println(f"$in2 -> $id $col") +// } + +// println( +// outs.mkString.reverse.foldLeft(0L)((acc, c) => (acc << 1) + c.asDigit) +// ) + +// enum Op { case AND, OR, XOR } +// +// enum Node: +// case Inner(op: Op, lhs: Node, rhs: Node) +// case Leaf(id: String) +// def id: String = this match +// case inner: Inner => inner.id +// case leaf: Leaf => leaf.id +// +// def op(op: Op, l: Node => Boolean, r: Node => Boolean)(node: Node): Boolean = +// node match +// case Node.Inner(_, inner, lhs, rhs) => +// op == inner && (l(lhs) && r(rhs) || l(rhs) && r(lhs)) +// case Node.Leaf(_) => false +// +// def leaf(id: String)(node: Node): Boolean = +// node match +// case Node.Leaf(leaf) => leaf == id +// case _ => false diff --git a/data/24.ans b/data/24.ans new file mode 100644 index 0000000..d58ece2 --- /dev/null +++ b/data/24.ans @@ -0,0 +1,2 @@ +51837135476040 +hjf,kdh,kpp,sgj,vss,z14,z31,z35 diff --git a/data/24.in b/data/24.in new file mode 100644 index 0000000..0264b3b --- /dev/null +++ b/data/24.in @@ -0,0 +1,313 @@ +x00: 1 +x01: 0 +x02: 0 +x03: 1 +x04: 1 +x05: 1 +x06: 0 +x07: 0 +x08: 1 +x09: 1 +x10: 0 +x11: 1 +x12: 0 +x13: 0 +x14: 1 +x15: 1 +x16: 1 +x17: 1 +x18: 0 +x19: 0 +x20: 0 +x21: 0 +x22: 1 +x23: 1 +x24: 1 +x25: 1 +x26: 1 +x27: 1 +x28: 0 +x29: 1 +x30: 1 +x31: 0 +x32: 1 +x33: 0 +x34: 0 +x35: 0 +x36: 0 +x37: 1 +x38: 1 +x39: 0 +x40: 0 +x41: 0 +x42: 0 +x43: 1 +x44: 1 +y00: 1 +y01: 1 +y02: 1 +y03: 1 +y04: 0 +y05: 0 +y06: 0 +y07: 0 +y08: 0 +y09: 1 +y10: 0 +y11: 1 +y12: 0 +y13: 0 +y14: 1 +y15: 0 +y16: 0 +y17: 0 +y18: 0 +y19: 1 +y20: 0 +y21: 0 +y22: 0 +y23: 0 +y24: 0 +y25: 1 +y26: 1 +y27: 0 +y28: 1 +y29: 0 +y30: 1 +y31: 0 +y32: 1 +y33: 1 +y34: 0 +y35: 1 +y36: 1 +y37: 1 +y38: 0 +y39: 1 +y40: 0 +y41: 1 +y42: 1 +y43: 0 +y44: 1 + +x07 AND y07 -> ncs +y24 AND x24 -> wrf +x19 XOR y19 -> tsm +x40 XOR y40 -> svt +rjf OR src -> dfv +fsf XOR fgs -> z44 +mhc AND jqd -> qgn +nrr XOR sms -> kpp +y20 AND x20 -> ngc +y21 AND x21 -> hbc +sgj OR ptb -> rqf +hbc OR wdr -> gjn +tks XOR sbg -> z23 +ddh AND tnm -> hgg +hsf OR bjw -> vbb +x15 XOR y15 -> vqs +x10 AND y10 -> dtm +vqs XOR vss -> z15 +x29 XOR y29 -> mgd +srg OR cwb -> qtn +nmb OR mbk -> z45 +dhs OR njq -> tng +jfw OR jrf -> vpd +x07 XOR y07 -> gck +tdw XOR vrk -> z03 +y11 AND x11 -> ffw +x16 XOR y16 -> bth +x39 XOR y39 -> tnm +cfg AND ngh -> jrq +vpd AND mvn -> hbj +rgp XOR bth -> z16 +qtn AND cjd -> vrv +x14 AND y14 -> cgt +dwh XOR hsk -> z24 +tgp XOR dkh -> z13 +y26 XOR x26 -> cfg +cpc XOR nbm -> z42 +y42 XOR x42 -> cpc +x17 XOR y17 -> cjd +rqf XOR pqn -> z36 +x27 AND y27 -> nwg +bcq AND hnk -> vjp +tks AND sbg -> wqr +wvr OR skq -> gmw +cwm AND tpv -> pqw +x41 AND y41 -> tqh +jcw AND wpk -> sbr +tgp AND dkh -> tbh +wrg XOR nwq -> z38 +y32 XOR x32 -> gds +bmn OR hbj -> msb +wps XOR mtn -> z33 +ncs OR pjf -> sws +wqr OR tqk -> dwh +x31 AND y31 -> pwg +y12 XOR x12 -> jcw +nrr AND sms -> z31 +x38 AND y38 -> npd +y02 AND x02 -> fwt +y37 AND x37 -> rnc +fwt OR vtm -> tdw +x38 XOR y38 -> nwq +gds AND ghr -> ckd +ffw OR nfb -> wpk +ctv XOR wht -> z05 +y11 XOR x11 -> cmg +y05 XOR x05 -> ctv +jhw XOR tcv -> z18 +wrf OR gnt -> rmw +y01 AND x01 -> tnr +x36 XOR y36 -> pqn +gjq OR dfg -> skp +x40 AND y40 -> ptg +y39 AND x39 -> dqn +bjb OR hjf -> sbg +rrn OR rpt -> qfs +ctv AND wht -> dhs +sgs AND rsb -> ccw +rmw XOR psg -> z25 +y24 XOR x24 -> hsk +bgd XOR msb -> z10 +y17 AND x17 -> fvv +y22 AND x22 -> kdh +qfs AND rfv -> fgp +wds AND fps -> rhr +y18 XOR x18 -> tcv +ttd AND nhg -> tfw +bbc AND jkb -> ptb +djn OR tnr -> cpb +y35 XOR x35 -> bbc +tfw OR cgt -> z14 +rgp AND bth -> srg +dwh AND hsk -> gnt +pqw OR ngc -> frt +y25 XOR x25 -> psg +y13 XOR x13 -> tgp +x30 XOR y30 -> rbw +vrv OR fvv -> jhw +skp XOR mgd -> z29 +cmg XOR ntv -> z11 +vjr XOR vbb -> z04 +gkj XOR sws -> z08 +x20 XOR y20 -> tpv +ntv AND cmg -> nfb +x32 AND y32 -> tdk +wmr AND cpb -> vtm +x19 AND y19 -> jps +jhw AND tcv -> rqv +y27 XOR x27 -> bcq +x34 AND y34 -> hdk +wqc XOR qtf -> z01 +wgk OR sbr -> dkh +x43 AND y43 -> kdb +y04 XOR x04 -> vjr +rmw AND psg -> fgg +gkj AND sws -> jfw +cwm XOR tpv -> z20 +cjd XOR qtn -> z17 +fsf AND fgs -> nmb +wps AND mtn -> rpt +x33 XOR y33 -> mtn +bcq XOR hnk -> z27 +tbh OR wwk -> nhg +twb XOR tsm -> z19 +frt AND mhw -> wdr +y15 AND x15 -> pwr +rbw XOR dfv -> z30 +vss AND vqs -> ctt +x28 AND y28 -> gjq +y28 XOR x28 -> dvf +bbc XOR jkb -> sgj +x43 XOR y43 -> fps +y04 AND x04 -> khw +pwg OR kpp -> ghr +x31 XOR y31 -> nrr +gmw XOR gck -> z07 +frt XOR mhw -> z21 +spb AND tng -> skq +svt AND hkg -> knf +gjn AND kdh -> bjb +qfs XOR rfv -> z34 +cpc AND nbm -> jtt +tqh OR ccw -> nbm +jtt OR qkv -> wds +gds XOR ghr -> z32 +rbw AND dfv -> qnv +msb AND bgd -> gjr +qnv OR bjd -> sms +y18 AND x18 -> pns +x41 XOR y41 -> rsb +x26 AND y26 -> srh +nvc OR npd -> ddh +dtm OR gjr -> ntv +x08 AND y08 -> jrf +y14 XOR x14 -> ttd +y06 AND x06 -> wvr +y16 AND x16 -> cwb +rnc OR qgn -> wrg +y30 AND x30 -> bjd +jqd XOR mhc -> z37 +ddh XOR tnm -> z39 +x12 AND y12 -> wgk +cqh OR fgg -> ngh +kdh XOR gjn -> z22 +x01 XOR y01 -> wqc +khw OR djm -> wht +ctt OR pwr -> rgp +y21 XOR x21 -> mhw +vjp OR nwg -> dhh +x02 XOR y02 -> wmr +gck AND gmw -> pjf +rqv OR pns -> twb +y00 AND x00 -> qtf +y05 AND x05 -> njq +y29 AND x29 -> rjf +vrk AND tdw -> hsf +y42 AND x42 -> qkv +y10 XOR x10 -> bgd +cfg XOR ngh -> z26 +tng XOR spb -> z06 +y00 XOR x00 -> z00 +cpb XOR wmr -> z02 +ckd OR tdk -> wps +jrq OR srh -> hnk +y22 XOR x22 -> hjf +x03 AND y03 -> bjw +nhg XOR ttd -> vss +tsm AND twb -> rwg +dqn OR hgg -> hkg +y34 XOR x34 -> rfv +y35 AND x35 -> z35 +x25 AND y25 -> cqh +y33 AND x33 -> rrn +wqc AND qtf -> djn +sgs XOR rsb -> z41 +x08 XOR y08 -> gkj +rwg OR jps -> cwm +rqf AND pqn -> wbv +x37 XOR y37 -> mhc +dvf XOR dhh -> z28 +kdb OR rhr -> fgs +knf OR ptg -> sgs +svt XOR hkg -> z40 +y13 AND x13 -> wwk +y23 AND x23 -> tqk +fgp OR hdk -> jkb +jcw XOR wpk -> z12 +y06 XOR x06 -> spb +x23 XOR y23 -> tks +y09 AND x09 -> bmn +wds XOR fps -> z43 +dhh AND dvf -> dfg +mgd AND skp -> src +wrg AND nwq -> nvc +y03 XOR x03 -> vrk +y36 AND x36 -> kqk +vjr AND vbb -> djm +x44 XOR y44 -> fsf +x44 AND y44 -> mbk +kqk OR wbv -> jqd +vpd XOR mvn -> z09 +y09 XOR x09 -> mvn