commit 695babcefe9984276a345c525fc4317d6d37d281
parent 74c942a32d80836b5917cf66516b1c046a1f84f9
Author: finwo <finwo@pm.me>
Date: Tue, 30 Dec 2025 21:20:55 +0100
Half 2025/10 implementation
Diffstat:
5 files changed, 303 insertions(+), 0 deletions(-)
diff --git a/2025/10/example.txt b/2025/10/example.txt
@@ -0,0 +1,3 @@
+[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}
+[...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2}
+[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}
diff --git a/2025/10/index.js b/2025/10/index.js
@@ -0,0 +1,230 @@
+#!/usr/bin/env node
+
+const fs = require('node:fs');
+
+// Parse input into something digestable
+const input = fs
+ .readFileSync('example.txt', 'utf-8')
+ .split('\r\n').join('\n')
+ .split('\r').join('\n')
+ .split('\n')
+ .filter(e => e)
+ .map(line => {
+ const specs = line.split(' ');
+ const target = specs.shift()
+ .slice(1,-1)
+ .split('')
+ .map(light => light === '#' ? 1 : 0);
+ const joltage_req = specs.pop().slice(1,-1).split(',').map(n => parseInt(n, 10));
+ const buttons = specs
+ .map(button => button.slice(1,-1)
+ .split(',')
+ .map(toggle => parseInt(toggle, 10))
+ )
+ // .sort((a,b) => b.length - a.length)
+ return {
+ target,
+ buttons,
+ joltage_req,
+
+ part1_pattern: null,
+ part1_presses: Infinity,
+ };
+ });
+
+// Caution, modifies in-place
+function patternResult(lights, buttonSpecs, pressPattern) {
+ if ('number' === typeof pressPattern) pressPattern = pressPattern.toString(2);
+ pressPattern = '0'.repeat(buttonSpecs.length - pressPattern.length) + pressPattern;
+ const result = new Array(lights).fill(0).map(_=>0);
+ for(let i = 0; i < buttonSpecs.length; i++) {
+ if (pressPattern[i] !== '1') continue;
+ const increments = buttonSpecs[i];
+ for(const j of increments) {
+ result[j]++;
+ }
+ }
+ return result;
+}
+
+function patternPresses(pressPattern) {
+ if ('number' === typeof pressPattern) pressPattern = pressPattern.toString(2);
+ return pressPattern.split('').filter(c => c === '1').length;
+}
+
+function parityPresses(
+ target,
+ buttons
+) {
+ let chosen_pattern = '';
+ let chosen_presses = Infinity;
+ let chosen_result = [];
+
+ for(let combination = 0; combination < 2**buttons.length; combination++) {
+ const pattern = ('0'.repeat(buttons.length) + combination.toString(2)).slice(-(buttons.length));
+ const presses = patternPresses(pattern);
+ const result = patternResult(target.length, buttons, pattern);
+ const parity = result.map(n => n % 2);
+ if (parity.join('') !== target) continue;
+ if (presses < chosen_presses) {
+ chosen_presses = presses;
+ chosen_pattern = pattern;
+ chosen_result = result;
+ }
+ }
+
+ return {
+ pattern: chosen_pattern,
+ presses: chosen_presses,
+ result : chosen_result,
+ };
+}
+
+const machines = [];
+for(const machine of input) {
+ machines.push(machine);
+ const target = machine.target.join('');
+ const _part1 = parityPresses(target, machine.buttons);
+ machine.part1_pattern = _part1.pattern;
+ machine.part1_presses = _part1.presses;
+}
+
+const part1 = machines.reduce((r,machine) => r + machine.part1_presses, 0);
+process.stdout.write(`------[ Part 1: ${part1} ]------\n`);
+
+const dict = {
+ light : [],
+ button: [],
+};
+for(let i=0; i<1e2; i++) {
+ dict.light[i] = `_${i.toString(36)}`;
+ dict.button[i] = `__${i.toString(36)}`;
+}
+
+const Solver = require('js-solver');
+for(const machine of machines) {
+ const equations = {};
+ const known = {};
+
+ // Add button<>light mapping
+ for(let btn_idx = 0; btn_idx < machine.buttons.length ; btn_idx++) {
+ for(const light_idx of machine.buttons[btn_idx]) {
+ equations[dict.light[light_idx]] ||= [];
+ equations[dict.light[light_idx]].push(dict.button[btn_idx])
+ }
+ }
+ // Add inverse for each button
+ for(let btn_idx = 0; btn_idx < machine.buttons.length ; btn_idx++) {
+
+ const selected_light = machine.buttons[btn_idx][0];
+ const self = dict.button[btn_idx];
+ const others = equations[dict.light[selected_light]].filter(n => n !== self);
+ equations[self] = `${dict.light[selected_light]} - (${others.join(' + ')})`;
+
+ // // for(const light_idx of machine.buttons[btn_idx]) {
+ // console.log({ btn_idx, selected_light, others, self });
+ }
+
+ for(const key of Object.keys(equations)) {
+ if (dict.light.includes(key)) equations[key] = equations[key].join(' + ');
+ }
+
+ // console.log({ equations });
+
+ // Inject known values
+ for(let light_idx = 0; light_idx < machine.joltage_req.length; light_idx++) {
+ known[dict.light[light_idx]] = machine.joltage_req[light_idx];
+ }
+
+ // console.log({ equations, known });
+
+ // Let's see what the solver does
+ const machineSolver = new Solver(equations);
+ const output = machineSolver.solve(known);
+
+ console.log({ equations, output });
+}
+
+// const Solver = require('js-solver');
+// const methSolver = new Solver({
+// });
+
+// console.log({ Solver });
+
+// // Compare prioritizes overflow
+// function joltage_cmp(a: number[], b: number[]): number {
+// for(let idx = 0; idx < b.length; idx++) {
+// if (a[idx] > b[idx]) return 1;
+// }
+// for(let idx = 0; idx < b.length; idx++) {
+// if (a[idx] < b[idx]) return -1;
+// }
+// return 0; // match
+// }
+
+// function joltage_mult(len: number, weights: number[], buttons: number[][]) {
+// const result = new Array(len).fill(0).map(_=>0);
+// for(let b = 0 ; b < buttons.length ; b++) {
+// for(const idx of buttons[b]) result[idx] += weights[b];
+// }
+// return result;
+// }
+
+// part2_outer: for(const machine of machines) {
+
+// const weights = new Array(machine.buttons.length).fill(0).map(_=>0);
+// const len = machine.joltage_req.length;
+
+// // console.log({ weights, buttons: machine.buttons, m: joltage_mult(machine.joltage_req.length, weights, machine.buttons) });
+
+// let cpu_limit = 1e6;
+
+// let j = 0;
+// while(1) {
+
+// if (cpu_limit-- < 0) break;
+
+// let i = j;
+// weights[i]++;
+// let cmp_result = joltage_cmp(joltage_mult(len, weights, machine.buttons), machine.joltage_req);
+// while(cmp_result > 0) {
+// weights[i]--;
+// // i++;
+// // if (i >= weights.length) {
+// // weights[j] = Math.max(0, weights[j] - 1);
+// // j = (j + 1) % weights.length;
+// // if (j === 0) {
+// // for(let k = 0 ; k < weights.length ; k++ ) {
+// // weights[k] = Math.max(0, weights[k] - 1);
+// // }
+// // }
+// // break;
+// // }
+// // weights[i]++;
+// cmp_result = joltage_cmp(joltage_mult(len, weights, machine.buttons), machine.joltage_req);
+// }
+// console.log({ weights, cmp_result });
+// // if (cmp_result === 0) {
+// // machine.part2 = weights.reduce((r,a) => r+a, 0);
+// // continue part2_outer;
+// // }
+
+
+// }
+
+// console.log({ cpu_limit, weights });
+
+// // for(let i=0 ; i < weights.length ; i++) {
+
+
+// // while(joltage_cmp(joltage_mult(len, weights, machine.buttons), machine.joltage_req) < 0) {
+// // weights[i]++;
+// // }
+// // weights[i]--;
+// // }
+
+// console.log(machine, weights);
+// }
+
+
+
diff --git a/2025/10/package-lock.json b/2025/10/package-lock.json
@@ -0,0 +1,38 @@
+{
+ "name": "10",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "dependencies": {
+ "js-solver": "^0.0.2"
+ },
+ "devDependencies": {
+ "@types/node": "^25.0.3"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "25.0.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
+ "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/js-solver": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/js-solver/-/js-solver-0.0.2.tgz",
+ "integrity": "sha512-6ELx/9Q4gALa2lomVu9SD1CpmS2IQdbweLawgE6gyx96bSodkDb6Y+hcxq30OX4hxKSCpBp6nXnjOt0WVG6+Kw==",
+ "license": "BSD"
+ },
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "dev": true,
+ "license": "MIT"
+ }
+ }
+}
diff --git a/2025/10/package.json b/2025/10/package.json
@@ -0,0 +1,8 @@
+{
+ "devDependencies": {
+ "@types/node": "^25.0.3"
+ },
+ "dependencies": {
+ "js-solver": "^0.0.2"
+ }
+}
diff --git a/2025/10/tsconfig.json b/2025/10/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "declaration": true,
+ "removeComments": true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "allowSyntheticDefaultImports": true,
+ "target": "es2020",
+ "sourceMap": true,
+ "outDir": "./dist",
+ "baseUrl": "./",
+ "incremental": true,
+ "skipLibCheck": true,
+ "strictNullChecks": false,
+ "noImplicitAny": false,
+ "strictBindCallApply": false,
+ "forceConsistentCasingInFileNames": false,
+ "noFallthroughCasesInSwitch": false,
+ "types": [
+ "node",
+ ],
+ }
+}