Advent of Code, Day 14

Day 14 of Advent of Code is here!

See all of my other solutions here. If you’re interested, check out what my colleagues at Slalom are doing with Advent of Code!

Do your best!

I highly encourage you to get through the day’s challenge on your own. I’m finding these are getting more complex, not impossible, just taking longer to complete. Like Day 13 and today, and others I’m sure, getting through these is taking more time than I have (full time job, 4 kids, and other hobbies). I’ll continue to do my best, but I may start deferring to some of my colleague’s code.

Day 14: Docking Data

Part I

Check out day 14 for the storyline. I’ll go ahead and say I didn’t write this out myself. I made a valiant attempt, but ran out of time, so below I reference the masterful Josh’s code for this one.

Before we start, let’s get get all the data

Pulling in the above values from my input file

const fs = require('fs');
const sets = fs.readFileSync('./input.txt', 'utf-8').trim().split('\n');

Then we loop through all of the sets

let mask = '';
let memory = [];

sets.forEach(set => {
	let [instruction, value] = set.split(' = ');

	if (instruction === 'mask') {
		mask = value;
	}
	if (instruction.match(/mem/)) {
		memory[getAddress(instruction)] = applyMask(value);
	}
});

We are splitting the set on = and then determine if we’re dealing with a mask or a memory address. If it’s a mask, we set our variable mask to that value. We’ll see this again shortly. If it’s a memory address, we call a function applyMask sending that value. We pass that output into the memory array at the address of the instruction, using getAddress:

const getAddress = mem => parseInt(mem.replace(/[^\d]/g, ''));

This is pretty easy, we are removing any non-digit, [^\d], in the string, and converting the string to a number.

And here we have applyMask

const applyMask = decimal => {
	// Convert the value to 36bit string
	const binaryValue = parseInt(decimal).toString(2).padStart(36, '0');
	
	return parseInt(mask                                  // Take the mask
		.split('')                                    // Look at every bit
		.map(b => parseInt(b))                        // Run it through a parseInt(), if it's 1, or 0, return it, X will throw NaN
		.map((b, i) => isNaN(b) ? binaryValue[i] : b) // for each (b)it, if it's NaN return the bit in the slot vv[i], otherwise, b 
		.join(''), 2);                                // smash it back together, convert to number, \o/
}

Short and sweet, but yet does so much

  • First we get the binary value of the decimal value, which is the memory value from the sets.forEach loop
  • Then we take the mask we set back in the loop, we split it on nothing, which will make an array of every character
  • Using map() we convert each character to a number with parseInt(). The mask will have X in them, that will return a NaN value, which means Not a Number
  • Using another map() we check for the NaN using isNaN(). If it not a number, so if isNaN returns true, then we grab the value for this index location from the binaryValue, if it is a number, we want to use it, so we return that (as b) instead
  • Finally, we join the code back together to get our new memory address

To find the final answer, we add all the memory addresses together, using a reduce()

console.log('PART 1', memory.reduce((a, b) => a + b, 0));

Part II

Again, big thanks to the masterful Josh for working through this and sharing :D

const fs = require('fs');
const set = fs.readFileSync('./input.txt', 'utf-8').trim().split('\n');

let mask = '';
let memory = [];

const getAddr = m => parseInt(m.replace(/[^\d]/g, ''));

const applyVals = (addr, v) => {
	let maskBits = mask.split('');
	let numAddrs = maskBits.filter(b => b === 'X').length;
	let p = 0;
	let xplace = 0;
	let addrBits = addr.toString(2).padStart(36, '0').split('');

	// For every element we want to set mem, 
	// we are going to set however many Xs are in the mask
	// Iterate over each num, build the binary representation, (p)
	// place them in the X places (xplace).
	for (let i = 0; i < Math.pow(2, numAddrs); i++) {
		xplace = 0;
		p = i.toString(2).padStart(numAddrs, '0').split('');

		let memAddr = parseInt(maskBits.map((b, j) => {
			if (b === '1') { return '1'; }
			if (b === '0') { return addrBits[j]; }
			if (b === 'X') { return p[xplace++]; }
		}).map(b => parseInt(b)).join(''), 2);

		// since this array will get larger than
		// 2^32 elements, using a string key hash 
		// will guarantee we don't lose any elements
		memory[memAddr.toString()] = v;
	}
}

set.forEach(set => {
	let [instruction, value] = set.split(' = ');

	if (instruction === 'mask') {
		mask = value;
	}
	if (instruction.match(/mem/)) {
		applyVals(getAddr(instruction), parseInt(value));
	}
});

let sum = 0;
for (let i in memory) sum += memory[i];

console.log('PART 2:', sum);

Take a look through and see what he’s doing there. You’ll notice a few similar pieces from Part 1, and then the core function, applyVals, is where Part 2 makes the biggest change. Josh included some comments above to help explain what is going on. Enjoy!

How did it go for you?

How did you find the answer on your own? How did you do it? Anything stump you? I’d love to hear how you did! Please comment below! I have also loaded up just the challenge and code from this and my other days on github, you can follow along there too: pretty Git pages or the code in the repo.

Series Navigation<< Advent of Code, Day 15Advent of Code, Day 13 >>

One thought on “Advent of Code, Day 14

Add yours

Leave a Reply

Up ↑

Discover more from David Lozzi

Subscribe now to keep reading and get access to the full archive.

Continue reading