advent-of-code

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

index.js (6637B)


      1 #!/usr/bin/env node
      2 
      3 const fs = require('node:fs');
      4 
      5 // Parse input into something digestable
      6 const input = fs
      7   .readFileSync('example.txt', 'utf-8')
      8   .split('\r\n').join('\n')
      9   .split('\r').join('\n')
     10   .split('\n')
     11   .filter(e => e)
     12   .map(line => {
     13     const specs = line.split(' ');
     14     const target = specs.shift()
     15       .slice(1,-1)
     16       .split('')
     17       .map(light => light === '#' ? 1 : 0);
     18     const joltage_req = specs.pop().slice(1,-1).split(',').map(n => parseInt(n, 10));
     19     const buttons = specs
     20       .map(button => button.slice(1,-1)
     21            .split(',')
     22            .map(toggle => parseInt(toggle, 10))
     23       )
     24       // .sort((a,b) => b.length - a.length)
     25     return {
     26       target,
     27       buttons,
     28       joltage_req,
     29 
     30       part1_pattern: null,
     31       part1_presses: Infinity,
     32     };
     33   });
     34 
     35 // Caution, modifies in-place
     36 function patternResult(lights, buttonSpecs, pressPattern) {
     37   if ('number' === typeof pressPattern) pressPattern = pressPattern.toString(2);
     38   pressPattern = '0'.repeat(buttonSpecs.length - pressPattern.length) + pressPattern;
     39   const result = new Array(lights).fill(0).map(_=>0);
     40   for(let i = 0; i < buttonSpecs.length; i++) {
     41     if (pressPattern[i] !== '1') continue;
     42     const increments = buttonSpecs[i];
     43     for(const j of increments) {
     44       result[j]++;
     45     }
     46   }
     47   return result;
     48 }
     49 
     50 function patternPresses(pressPattern) {
     51   if ('number' === typeof pressPattern) pressPattern = pressPattern.toString(2);
     52   return pressPattern.split('').filter(c => c === '1').length;
     53 }
     54 
     55 function parityPresses(
     56   target,
     57   buttons
     58 ) {
     59   let chosen_pattern = '';
     60   let chosen_presses = Infinity;
     61   let chosen_result  = [];
     62 
     63   for(let combination = 0; combination < 2**buttons.length; combination++) {
     64     const pattern = ('0'.repeat(buttons.length) + combination.toString(2)).slice(-(buttons.length));
     65     const presses = patternPresses(pattern);
     66     const result  = patternResult(target.length, buttons, pattern);
     67     const parity  = result.map(n => n % 2);
     68     if (parity.join('') !== target) continue;
     69     if (presses < chosen_presses) {
     70       chosen_presses = presses;
     71       chosen_pattern = pattern;
     72       chosen_result  = result;
     73     }
     74   }
     75 
     76   return {
     77     pattern: chosen_pattern,
     78     presses: chosen_presses,
     79     result : chosen_result,
     80   };
     81 }
     82 
     83 const machines = [];
     84 for(const machine of input) {
     85   machines.push(machine);
     86   const target = machine.target.join('');
     87   const _part1 = parityPresses(target, machine.buttons);
     88   machine.part1_pattern = _part1.pattern;
     89   machine.part1_presses = _part1.presses;
     90 }
     91 
     92 const part1 = machines.reduce((r,machine) => r + machine.part1_presses, 0);
     93 process.stdout.write(`------[ Part 1: ${part1} ]------\n`);
     94 
     95 const dict = {
     96   light : [],
     97   button: [],
     98 };
     99 for(let i=0; i<1e2; i++) {
    100   dict.light[i] = `_${i.toString(36)}`;
    101   dict.button[i] = `__${i.toString(36)}`;
    102 }
    103 
    104 const Solver = require('js-solver');
    105 for(const machine of machines) {
    106   const equations = {};
    107   const known     = {};
    108 
    109   // Add button<>light mapping
    110   for(let btn_idx = 0; btn_idx < machine.buttons.length ; btn_idx++) {
    111     for(const light_idx of machine.buttons[btn_idx]) {
    112       equations[dict.light[light_idx]] ||= [];
    113       equations[dict.light[light_idx]].push(dict.button[btn_idx])
    114     }
    115   }
    116   // Add inverse for each button
    117   for(let btn_idx = 0; btn_idx < machine.buttons.length ; btn_idx++) {
    118 
    119     const selected_light = machine.buttons[btn_idx][0];
    120     const self           = dict.button[btn_idx];
    121     const others         = equations[dict.light[selected_light]].filter(n => n !== self);
    122     equations[self]      = `${dict.light[selected_light]} - (${others.join(' + ')})`;
    123 
    124     // // for(const light_idx of machine.buttons[btn_idx]) {
    125     // console.log({ btn_idx, selected_light, others, self });
    126   }
    127 
    128   for(const key of Object.keys(equations)) {
    129     if (dict.light.includes(key)) equations[key] = equations[key].join(' + ');
    130   }
    131 
    132   // console.log({ equations });
    133 
    134   // Inject known values
    135   for(let light_idx = 0; light_idx < machine.joltage_req.length; light_idx++) {
    136     known[dict.light[light_idx]] = machine.joltage_req[light_idx];
    137   }
    138 
    139   // console.log({ equations, known });
    140 
    141   // Let's see what the solver does
    142   const machineSolver = new Solver(equations);
    143   const output        = machineSolver.solve(known);
    144 
    145   console.log({ equations, output });
    146 }
    147 
    148 // const Solver = require('js-solver');
    149 // const methSolver = new Solver({
    150 // });
    151 
    152 // console.log({ Solver });
    153 
    154 // // Compare prioritizes overflow
    155 // function joltage_cmp(a: number[], b: number[]): number {
    156 //   for(let idx = 0; idx < b.length; idx++) {
    157 //     if (a[idx] > b[idx]) return 1;
    158 //   }
    159 //   for(let idx = 0; idx < b.length; idx++) {
    160 //     if (a[idx] < b[idx]) return -1;
    161 //   }
    162 //   return 0; // match
    163 // }
    164 
    165 // function joltage_mult(len: number, weights: number[], buttons: number[][]) {
    166 //   const result = new Array(len).fill(0).map(_=>0);
    167 //   for(let b = 0 ; b < buttons.length ; b++) {
    168 //     for(const idx of buttons[b]) result[idx] += weights[b];
    169 //   }
    170 //   return result;
    171 // }
    172 
    173 // part2_outer: for(const machine of machines) {
    174 
    175 //   const weights = new Array(machine.buttons.length).fill(0).map(_=>0);
    176 //   const len     = machine.joltage_req.length;
    177 
    178 //   // console.log({ weights, buttons: machine.buttons, m: joltage_mult(machine.joltage_req.length, weights, machine.buttons) });
    179 
    180 //   let cpu_limit = 1e6;
    181 
    182 //   let j = 0;
    183 //   while(1) {
    184 
    185 //     if (cpu_limit-- < 0) break;
    186 
    187 //     let i = j;
    188 //     weights[i]++;
    189 //     let cmp_result = joltage_cmp(joltage_mult(len, weights, machine.buttons), machine.joltage_req);
    190 //     while(cmp_result > 0) {
    191 //       weights[i]--;
    192 //     //   i++;
    193 //     //   if (i >= weights.length) {
    194 //     //     weights[j] = Math.max(0, weights[j] - 1);
    195 //     //     j = (j + 1) % weights.length;
    196 //     //     if (j === 0) {
    197 //     //       for(let k = 0 ; k < weights.length ; k++ ) {
    198 //     //         weights[k] = Math.max(0, weights[k] - 1);
    199 //     //       }
    200 //     //     }
    201 //     //     break;
    202 //     //   }
    203 //     //   weights[i]++;
    204 //       cmp_result = joltage_cmp(joltage_mult(len, weights, machine.buttons), machine.joltage_req);
    205 //     }
    206 //     console.log({ weights, cmp_result });
    207 //     // if (cmp_result === 0) {
    208 //     //   machine.part2 = weights.reduce((r,a) => r+a, 0);
    209 //     //   continue part2_outer;
    210 //     // }
    211 
    212 
    213 //   }
    214 
    215 //   console.log({ cpu_limit, weights });
    216 
    217 //   // for(let i=0 ; i < weights.length ; i++) {
    218 
    219 
    220 //   //   while(joltage_cmp(joltage_mult(len, weights, machine.buttons), machine.joltage_req) < 0) {
    221 //   //     weights[i]++;
    222 //   //   }
    223 //   //   weights[i]--;
    224 //   // }
    225 
    226 //   console.log(machine, weights);
    227 // }
    228 
    229 
    230