Day 20

import Solution from "./solution.ts";

// pulse: false = low, ture: high

type Signal = [string, string, boolean]; // from, to, pulse

class Broadcaster {
  #receivers: string[];
  #name: string;
  #pulses = [0, 0];

  constructor(name: string, receivers: string[]) {
    this.#name = name;
    this.#receivers = receivers.toReversed();
  }

  get receivers() {
    return this.#receivers;
  }

  get low() {
    return this.#pulses[0];
  }

  get high() {
    return this.#pulses[1];
  }

  get name() {
    return this.#name;
  }

  receive(_from: string, pulse: boolean): Signal[] {
    this.#pulses[pulse ? 1 : 0]++;
    return this.#receivers.map((r) => [this.name, r, pulse]);
  }
}

class FlipFlop extends Broadcaster {
  #state = false; // false is off, and true is on

  receive(from: string, pulse: boolean): Signal[] {
    super.receive(from, pulse);
    if (!pulse) {
      this.#state = !this.#state;
      return this.receivers.map((r) => [this.name, r, this.#state]);
    }
    return [];
  }
}

class Conjunction extends Broadcaster {
  #inputs = new Map<string, boolean>();

  addInput(name: string) {
    this.#inputs.set(name, false);
  }

  receive(from: string, pulse: boolean): Signal[] {
    super.receive(from, pulse);
    this.#inputs.set(from, pulse);
    return this.receivers.map((
      r,
    ) => [this.name, r, ![...this.#inputs.values()].every((p) => p)]);
  }
}

type Module = Broadcaster | FlipFlop | Conjunction;

const score = (modules: Map<string, Module>): [number, number] => {
  return [...modules.values()].reduce((p, c) => [p[0] + c.low, p[1] + c.high], [
    0,
    0,
  ]);
};

const greatestCommonDemoninator = (a: number, b: number): number =>
  b == 0 ? a : greatestCommonDemoninator(b, a % b);
const leastCommonMultiple = (a: number, b: number) =>
  (a / greatestCommonDemoninator(a, b)) * b;

const task = new Solution(
  (arr: Map<string, Module>[]) => {
    const modules = arr[0];
    for (let i = 1; i <= 1000; i++) {
      const queue = [["button", "broadcaster", false] as Signal];
      while (queue.length > 0) {
        const pulse = queue.pop()!;
        const module = modules.get(pulse[1]);
        if (module) {
          queue.unshift(
            ...module.receive(pulse[0], pulse[2]),
          );
        }
      }
    }
    return score(modules).reduce((p, c) => p * c);
  },
  (arr: Map<string, Module>[]) => {
    const modules = arr[0];
    let i = 1;
    const feeders = Object.fromEntries(
      [...modules.values()].filter((m) =>
        m.receivers.some((r) => modules.get(r)?.receivers.includes("rx"))
      ).map((m) => [m.name, 0]),
    );
    if (Object.keys(feeders).length === 0) return 0;
    while (Object.values(feeders).some((s) => s === 0)) {
      const queue = [["button", "broadcaster", false] as Signal];
      while (queue.length > 0) {
        const pulse = queue.pop()!;
        const module = modules.get(pulse[1]);
        if (module) {
          queue.unshift(
            ...module.receive(pulse[0], pulse[2]),
          );
        }
      }
      for (const [key, value] of Object.entries(feeders)) {
        if (modules.get(key)?.low !== 0 && value === 0) {
          feeders[key] = i;
        }
      }
      i++;
    }
    return Object.values(feeders).reduce((p, c) => leastCommonMultiple(p, c));
  },
  {
    transform: (all) => {
      const modules = new Map(
        all.split("\n").map((l): [string, Broadcaster] => {
          const [name, destinations] = l.split(" -> ");
          const dest = destinations.split(", ");
          if (name === "broadcaster") {
            return [name, new Broadcaster(name, dest)];
          }
          if (name.startsWith("&")) {
            return [name.slice(1), new Conjunction(name.slice(1), dest)];
          }
          if (name.startsWith("%")) {
            return [name.slice(1), new FlipFlop(name.slice(1), dest)];
          }
          throw new Error("unknown module");
        }),
      );
      for (const [n, m] of modules) {
        for (const r of m.receivers) {
          const d = modules.get(r);
          if (d instanceof Conjunction) {
            d.addInput(n);
          }
          if (d === undefined) {
            modules.set(r, new Broadcaster(r, []));
          }
        }
      }
      return modules;
    },
    sep: "\n\n",
  },
);
task.expect(11687500, 0);

export default task;

Last edited 04. April 2025 13:29