advent-of-code

Entries to advent of code, multiple years
git clone git://git.finwo.net/misc/advent-of-code
Log | Files | Refs

commit 695babcefe9984276a345c525fc4317d6d37d281
parent 74c942a32d80836b5917cf66516b1c046a1f84f9
Author: finwo <finwo@pm.me>
Date:   Tue, 30 Dec 2025 21:20:55 +0100

Half 2025/10 implementation

Diffstat:
A2025/10/example.txt | 3+++
A2025/10/index.js | 230+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A2025/10/package-lock.json | 38++++++++++++++++++++++++++++++++++++++
A2025/10/package.json | 8++++++++
A2025/10/tsconfig.json | 24++++++++++++++++++++++++
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", + ], + } +}