const input = await Deno.readTextFile("test3.txt"); const SourceKindTable = { "%": "flipflop", "&": "conjunction", }; function getSourceKind(prefix: string): "flipflop" | "conjunction" { if (prefix in SourceKindTable) { return SourceKindTable[prefix as keyof typeof SourceKindTable] as "flipflop" | "conjunction"; } throw new Error(`unknown source kind ${prefix}`); } function parseInput(input: string) { const lines = input.split("\n").map(x => x.trim()).filter(x => x.length > 0 && x[0] !== "#"); return lines.map(line => { const [source, targetsLine] = line.split("->").map(x => x.trim()) const targets = targetsLine.split(",").map(x => x.trim()); if (source === "broadcaster") { return { sourceKind: source, sourceName: "broadcaster", targets } } const sourceKind = getSourceKind(source[0]); const sourceName = source.slice(1); return { sourceKind: sourceKind, sourceName: sourceName, targets } }); } const s = parseInput(input); console.log(s); type Signal = boolean; type ModuleMap = Record; type PropagateResult = { signal: Signal, }; interface Module { sourceName: string, targets: string[], propagate(sourceName: string, signal: Signal): null | PropagateResult; } class FlipFlop implements Module { public state = false; constructor( public sourceName: string, public targets: string[], ) { } propagate(_sourceName: string, signal: Signal) { if (!signal) { this.state = !this.state; return { signal: this.state }; } return null; } } class Conjunction implements Module { public state: Map; constructor( public sourceName: string, public targets: string[], public inputs: string[], ) { this.state = new Map(); for (const input of inputs) { this.state.set(input, false); } } propagate(sourceName: string, signal: Signal) { this.state.set(sourceName, signal); if ([...this.state.values()].every(x => x)) { return { signal: false, }; } return { signal: true, }; } } class Broadcaster implements Module { constructor( public sourceName: string, public targets: string[], ) { } propagate(_sourceName: string, signal: boolean): PropagateResult | null { return { signal }; } } const state = {} as ModuleMap; for (const line of s) { if (line.sourceKind === "broadcaster") { state[line.sourceName] = new Broadcaster(line.sourceName, line.targets); } else if (line.sourceKind === "flipflop") { state[line.sourceName] = new FlipFlop(line.sourceName, line.targets); } else if (line.sourceKind === "conjunction") { const sourceName = line.sourceName; const inputs = s.filter((x) => { return x.targets.includes(sourceName) }).map(x=> x.sourceName); state[line.sourceName] = new Conjunction(line.sourceName, line.targets, inputs); } } function drawMermaidChart(modules: Module[]) { let ret = ""; ret += ("flowchart TD\n"); ret += modules.map(module => { const kind = module instanceof Conjunction ? "c" : "f"; return module.targets.map(target=> { return (` ${module.sourceName}[${kind} ${module.sourceName}] --> ${target}`); }).join("\n"); }).join("\n"); return ret; } console.log(drawMermaidChart([...Object.values(state)])); function cycle(state: ModuleMap) { const queue = [{ from: "button", target: "broadcaster", signal: false, }]; while (queue.length > 0) { const { target, signal, from } = queue.shift()!; const targetModule = state[target]; if (targetModule === undefined) { // ignore continue; } const result = targetModule.propagate(from, signal); if (result !== null) { const { signal } = result; if (!signal && targetModule.targets.includes("rx")){ console.log("found rx"); return true; } targetModule.targets.forEach(x => { console.log(`${target} -${signal ? "high" : "low"}-> ${x}`); queue.push({ from: target, target: x, signal }); }); } } return false; } let i = 0; while (true) { console.log(i); const rx = cycle(state); i++; if (i % 10 === 0) { break; } if (rx) { break; } } console.log(i);