|
1 | 1 | import { memoize } from "../utils/memoize.js"; |
2 | 2 |
|
3 | | -// Helper function to generate all combinations |
4 | | -function* product(...arrays) { |
| 3 | +function* product(arrays) { |
5 | 4 | if (arrays.length === 0) yield []; |
6 | 5 | else { |
7 | | - const [head, ...tail] = arrays; |
8 | | - for (const item of head) { |
9 | | - for (const rest of product(...tail)) { |
10 | | - yield [item, ...rest]; |
11 | | - } |
| 6 | + let [head, ...tail] = arrays; |
| 7 | + for (let item of head) { |
| 8 | + for (let rest of product(tail)) yield [item, ...rest]; |
12 | 9 | } |
13 | 10 | } |
14 | 11 | } |
15 | 12 |
|
16 | 13 | function parse(input) { |
17 | | - let parseTuple = str => str.slice(1, -1).split(",").map(Number); |
18 | | - let machines = input.split("\n").map(line => { |
19 | | - const parts = line.split(" "); |
20 | | - const dia = Array.from(parts[0].slice(1, -1)).map(c => (c === "#" ? 1 : 0)); |
21 | | - const buttons = parts.slice(1, -1).map(x => parseTuple(x)); |
22 | | - const jolts = parseTuple(parts[parts.length - 1]); |
23 | | - return { dia, buttons, jolts }; |
| 14 | + return input.split("\n").map(line => { |
| 15 | + let parts = line.replaceAll(/[[\](){}]/g, "").split(" "); |
| 16 | + let indicator = parts[0].replace(/./g, c => (c === "#" ? 1 : 0)); |
| 17 | + let buttons = parts.map(x => x.split(",").map(Number)); |
| 18 | + let jolts = parts.at(-1).split(",").map(Number); |
| 19 | + return { indicator, buttons, jolts }; |
24 | 20 | }); |
25 | | - return machines; |
26 | 21 | } |
27 | 22 |
|
28 | | -function producePatterns(buttons, jolts) { |
29 | | - const ops = {}, |
30 | | - patterns = {}; |
31 | | - for (const pressed of product(...Array(buttons.length).fill([0, 1]))) { |
32 | | - const jolt = Array(jolts.length).fill(0); |
| 23 | +function producePatterns(buttons, length) { |
| 24 | + let patterns = {}; |
| 25 | + for (let pressed of product(Array(buttons.length).fill([0, 1]))) { |
| 26 | + let lights = Array(length).fill(0); |
33 | 27 | for (let i = 0; i < pressed.length; i++) { |
34 | | - if (pressed[i]) { |
35 | | - for (const j of buttons[i]) jolt[j] += pressed[i]; |
36 | | - } |
| 28 | + if (pressed[i]) for (let j of buttons[i]) lights[j] = lights[j] ? 0 : 1; |
37 | 29 | } |
38 | | - const lights = jolt.map(x => x % 2); |
39 | | - const key = JSON.stringify(pressed); |
40 | | - const lightsKey = JSON.stringify(lights); |
41 | | - ops[key] = jolt; |
42 | | - if (!patterns[lightsKey]) patterns[lightsKey] = []; |
43 | | - patterns[lightsKey].push(pressed); |
| 30 | + let key = lights.join(""); |
| 31 | + patterns[key] = [...(patterns[key] || []), pressed]; |
44 | 32 | } |
45 | | - return { ops, patterns }; |
| 33 | + return patterns; |
46 | 34 | } |
47 | 35 |
|
48 | 36 | export function part1(input) { |
49 | 37 | let machines = parse(input); |
50 | 38 | let p1 = 0; |
51 | | - for (const { dia, buttons, jolts } of machines) { |
52 | | - const { patterns } = producePatterns(buttons, jolts); |
53 | | - const diaKey = JSON.stringify(dia); |
54 | | - p1 += Math.min(...patterns[diaKey].map(x => x.reduce((a, b) => a + b, 0))); |
| 39 | + for (let { indicator, buttons } of machines) { |
| 40 | + let patterns = producePatterns(buttons, indicator.length); |
| 41 | + let sums = patterns[indicator].map(x => x.reduce((a, b) => a + b, 0)); |
| 42 | + p1 += Math.min(...sums); |
55 | 43 | } |
56 | 44 | return p1; |
57 | 45 | } |
58 | 46 |
|
59 | 47 | export function part2(input) { |
60 | | - let p2 = 0; |
61 | 48 | let machines = parse(input); |
62 | | - |
63 | | - for (const { buttons, jolts } of machines) { |
64 | | - const { ops, patterns } = producePatterns(buttons, jolts); |
65 | | - |
| 49 | + let p2 = 0; |
| 50 | + for (let { buttons, jolts } of machines) { |
| 51 | + let patterns = producePatterns(buttons, jolts.length); |
66 | 52 | let presses = memoize(target => { |
67 | 53 | if (target.every(x => x === 0)) return 0; |
68 | 54 | if (target.some(x => x < 0)) return Infinity; |
69 | 55 |
|
70 | | - const lights = target.map(x => x % 2); |
71 | | - const lightsKey = JSON.stringify(lights); |
72 | | - if (!patterns[lightsKey]) return Infinity; |
73 | | - |
74 | 56 | let total = Infinity; |
75 | | - for (const pressed of patterns[lightsKey]) { |
76 | | - const diff = ops[JSON.stringify(pressed)]; |
77 | | - const newTarget = diff.map((a, i) => (target[i] - a) / 2); |
78 | | - const sum = pressed.reduce((a, b) => a + b, 0); |
| 57 | + let options = patterns[target.map(x => x % 2).join("")] || []; |
| 58 | + for (let pressed of options) { |
| 59 | + let jolt = Array(jolts.length).fill(0); |
| 60 | + for (let i = 0; i < pressed.length; i++) { |
| 61 | + if (pressed[i]) for (let j of buttons[i]) jolt[j] += pressed[i]; |
| 62 | + } |
| 63 | + let newTarget = jolt.map((a, i) => (target[i] - a) / 2); |
| 64 | + let sum = pressed.reduce((a, b) => a + b, 0); |
79 | 65 | total = Math.min(total, sum + 2 * presses(newTarget)); |
80 | 66 | } |
81 | 67 | return total; |
82 | 68 | }); |
83 | | - |
84 | 69 | p2 += presses(jolts); |
85 | 70 | } |
86 | 71 | return p2; |
|
0 commit comments