Advent of Code - Day 9 to 12

I've got a bunch of code to share so let's get started. As before, be sure to check my repo for the full code of my solutions.

Day 9

Day 9 was a doozy. Basically the idea was to work with a compressed string, think a zip file, and decompress it. Part one used this rule: Given the existence of (XxY) in a string, it means that you repeat the next X characters Y times. Your solution needs to decompress the string and output the length.


//console.log(decompress('ADVENT'));
//console.log('-------------------------');
//console.log(decompress('A(1x5)BC'));
//console.log('-------------------------');
//console.log(decompress('(3x3)XYZ'));
//console.log('-------------------------');
/*
console.log('ABCBCDEFEFG' == decompress('A(2x2)BCD(2x2)EFG'));
console.log('-------------------------');

console.log('(1x3)A' == decompress('(6x1)(1x3)A'));
console.log('-------------------------');

console.log('X(3x3)ABC(3x3)ABCY' == decompress('X(8x2)(3x3)ABCY'));
console.log('-------------------------');
*/
const fs = require('fs');
let input = fs.readFileSync('./input.txt','utf8');
//this should be one line
input = input.replace(/ /g, '');
input = input.replace(/\n/g, '');


// 120767 is too high
let result = decompress(input);
console.log(result.length);

function decompress(s) {
    let matches = s.match(/\(\d+x\d+\)/);
    if(!matches) return s;
    let result = '';

    while(matches) {
//        console.log(matches);
        let marker = matches[0].replace(/[\(\)]/g,'');
        let [numChar, numRep] = marker.split('x');
//        console.log('marker='+marker, numChar, numRep);
        numChar = Number(numChar); numRep = Number(numRep);

        let stringToRepeat = s.substr(matches.index+matches[0].length, numChar);
        let repeated = stringToRepeat.repeat(numRep);
//        console.log('stringToRepeat='+stringToRepeat, repeated);
        /*
        new string is:
        0-Where I found match
        Repeated String
        0 + Where I found match + length of match + length of string to repeat

        TO the end
        */
        result += s.substr(0, matches.index);
//        console.log('result so far = '+result);
        result += repeated;
//        console.log('result so far = '+result);
        //the last part is a bit more complex
        //we need to know the number here so we can search past it later
        minMatch = matches.index + matches[0].length + numChar;
//        console.log('lastPart num is ',minMatch);
        let lastPart = s.substr(minMatch);
//        console.log('lastPart',lastPart);
        s = lastPart;

        matches = s.match(/\(\d+x\d+\)/);
        //will we continue?
//        console.log(matches);
//        console.log('\n\n');
        if(!matches) result += s;
    }
    return result;
}

Nothing too crazy here, just a bunch of string manipulation. Part 2 did something crazy. Previously, if you found a marker within text that was being repeated, you ignored it. Now you need to decompress that too. What happens now is that the string gets so big, you run out of memory! So along with continuing to decompress, I had to switch my code to not actually create the result string, but just keep track of the length.


const fs = require('fs');
let input = fs.readFileSync('./input.txt','utf8');
//this should be one line
input = input.replace(/ /g, '');
input = input.replace(/\n/g, '');


let result = decompress(input);
console.log(result);
//final answer: 11,658,395,076

function decompress(s) {
    let matches = s.match(/\(\d+x\d+\)/);
    if(!matches) return s;
    let result = 0;

    while(matches) {
//        console.log(matches);
        let marker = matches[0].replace(/[\(\)]/g,'');
        let [numChar, numRep] = marker.split('x');
//        console.log('marker='+marker, numChar, numRep);
        numChar = Number(numChar); numRep = Number(numRep);

        let stringToRepeat = s.substr(matches.index+matches[0].length, numChar);
        let repeated = stringToRepeat.repeat(numRep);
//        console.log('stringToRepeat='+stringToRepeat, repeated);
        /*
        new string is:
        0-Where I found match
        Repeated String
        */
        result += (s.substr(0, matches.index)).length;
//        console.log('result so far = '+result);
        if(result % 500000 === 0) console.log(result);
//        result += repeated;
//        console.log('result so far = '+result);
        //the last part is a bit more complex
        //we need to know the number here so we can search past it later
        minMatch = matches.index + matches[0].length + numChar;
//        console.log('lastPart num is ',minMatch);
        let lastPart = s.substr(minMatch);
//        console.log('lastPart',lastPart);
        s = repeated += lastPart;

        matches = s.match(/\(\d+x\d+\)/);
        //will we continue?
        if(!matches) result += s.length;
    }
    return result;
}

Day 10

Day 10 was another fun one (two in a row, I should have known what was coming next). Basically your input is a set of instructions to bots that take values from an input bucket and then, eventually, copy them to an output bucket. Bots can hold two values and then have a rule saying, "I give my lower value to bot X and my higher value to bot Y", or instead they can copy a high/low value to the final output bucket. Your goal is to figure out what bot "held" values 61 and 17.


const fs = require('fs');
let input = fs.readFileSync('./input.txt','utf8');

let inputs = input.split('\n');

let bots = [];
let output = [];

function defineBot() {
    return {
        chips:[],
        logic:{'low':-1, 'high':-1}
    }
}

inputs.forEach(function(cmd) {
//    console.log('CMD: '+cmd);

    // value X goes to bot Y
    if(cmd.indexOf('value ') == 0) {
        let parts = cmd.split(' ');
        let value = parts[1];
        let bot = parts[5];
        if(!bots[bot]) bots[bot] = defineBot();

        bots[bot].chips.push(Number(value));
//        console.log('The bot is now: '+JSON.stringify(bots[bot]));
    }

    //bot X gives low to bot Y and high to bot Z
    //bot x gives low to OUTPUT Y
    if(cmd.indexOf('bot ') == 0) {
        let parts = cmd.split(' ');
        let owner = parts[1];

        if(!bots[owner]) bots[owner] = defineBot();

        bots[owner].logic.low = {target:parts[6], type:parts[5]};
        bots[owner].logic.high = {target:parts[11], type:parts[10]};
    }

});

//console.log(JSON.stringify(bots));
//process.exit(1);

//ok so in theory, we can process until we can't
var zz = 0;
while(hasStuffToDo(bots)) {
    for(let x=0;x<bots.length;x++) {
        let thisBot = bots[x];
        if(thisBot.chips.length == 2) {
//            console.log('processing bot '+JSON.stringify(thisBot));
            let sorted = thisBot.chips.sort((function(x,y) {
                if(x < y) return -1;
                if(x > y) return 1;
                return 0;
            }));
            let myLow = sorted[0];
            let myHigh = sorted[1];

            if(sorted[0] == 17 && sorted[1] == 61) {
                //13 is too low
                console.log('WINNER? '+x);
            }
//            console.log('my low is '+myLow+ ' and my high is '+myHigh);
            //hand stuf fout
            let lowTarget = thisBot.logic.low.target;
            let lowType = thisBot.logic.low.type;
            let highTarget = thisBot.logic.high.target;
            let highType = thisBot.logic.high.type;
            
            if(lowType === 'bot') {
//                console.log('hand my low chip to '+lowTarget);
                bots[lowTarget].chips.push(myLow);
            } else {
//                console.log('hand my low chip to output '+lowTarget);
                output[lowTarget] = myLow;
            }

            if(highType === 'bot') {
//                console.log('hand my high chip to '+highTarget);
                bots[highTarget].chips.push(myHigh);
            } else {
//                console.log('hand my high chip to output '+highTarget);
                output[highTarget] = myHigh;
            }

            bots[x].chips = [];
        }

    }
    zz++; if(zz > 100) { console.log('ABORT'); process.exit(); }
}

console.log('OUTPUT',output);

//I return true as long as one bot has 2 chips
function hasStuffToDo(bots) {
    for(var x=0;x<bots.length;x++) {
        if(bots[x].chips.length === 2) return true;
    }
    return false;
}

Part 2 simply asked you to output the product of some of the values in the output array, and since I had output them already, I didn't need to write any code at all.

Day 11

Well, it happened. Day 11 was the day that finally brought me to a complete stop. Intellectually I understood the problem, but honestly I couldn't see any way I was ever going to solve it. Last year when this happened I typically found a solution in another language and rewrote it in JavaScript, but I was a bit short on time so I really cheated. I found a Python solution and simply ran it. As I didn't have Python installed yet, I figured I was at least correcting that problem too.

Day 12

Woot! A fun one again! And one that's based on a puzzle from last year if I'm not mistaken. Day 12 basically has you implementing a little Assembly-like language. So for examlpe, your input may be:


cpy 41 a
inc a
inc a
dec a
jnz a 2
dec a

Your code either moves/sets values in registers or has you change the order of code execution. Outside of a few dumb mistakes I made, this one was simple.


let input = `cpy 1 a
cpy 1 b
cpy 26 d
jnz c 2
jnz 1 5
cpy 7 c
inc d
dec c
jnz c -2
cpy a c
inc a
dec b
jnz b -2
cpy c b
dec d
jnz d -6
cpy 17 c
cpy 18 d
inc a
dec d
jnz d -2
dec c
jnz c -5`;

let instructions = input.split('\n');
//problem 2 sets c to 1, problem 1 sets it to 0
let registers = {a:0, b:0, c:0, d:0 };

let sanity = 0;
for(var i=0;i<instructions.length;i++) {
    let instruction = instructions[i];
    //console.log(i+': '+instruction);
    
    if(instruction.indexOf('cpy') === 0) {
        let [,from,target] = instruction.split(' ');
    //    console.log('copy '+from+' to '+target);
        
        if(from === 'a') {
            registers[target] = registers["a"];
        } else if(from === 'b') {
            registers[target] = registers["b"];
        } else if(from === 'c') {
            registers[target] = registers["c"];
        } else if(from === 'd') {
            registers[target] = registers["d"];
        } else {
            registers[target] = Number(from);
        }
        
    }

    if(instruction.indexOf('inc') === 0) {
        let [,target] = instruction.split(' ');
  //      console.log('inc '+target);
        registers[target]++;
    }

    if(instruction.indexOf('dec') === 0) {
        let [,target] = instruction.split(' ');
//        console.log('dec '+target);
        registers[target]--;
    }

    if(instruction.indexOf('jnz') === 0) {
        /*
        jnz x y jumps to an instruction y away (positive means forward; negative means backward), but only if x is not zero
        */
        let [,x,y] = instruction.split(' ');
        if(registers[x]!=0) {
//            console.log('jump '+x+' '+y+' away');
            y--;
            i+=Number(y);
        }
    }

//    sanity++;
//    console.log(registers);
//    if(sanity > 300000) { console.log('EXIT'); process.exit(); }
}

console.log(registers);

Part two had you simply change an initial value (which is what I ended up committing to GitHub) so I didn't make a new file.

Like This?

If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can also subscribe to the email feed to get notified of new posts.

See Also