DEV Community

Rachel
Rachel

Posted on

Advent of Code 2021 in Javascript: My beginner solutions (day 3)

Hi dev.to!

A couple of days ago I posted my solutions to days 1 & 2 of Advent of Code 2021, using sort of beginner-intermediate pure Javascript.

Now I'm back to continue on with day 3!

I found this one took me a bit longer, and I ended up with a fair amount of code. I'm sure there are much more elegant ways of doing this than nested loops... but again, I've tried to make it basic and easy to read. So here goes.

Day Three: Binary Diagnostic

(link)

Part One

In this puzzle we are given a list of binary numbers that we need to process in various ways. This involves finding the most common ('gamma') and least common ('epsilon') bits at each position. What the heck does that mean??

Well, a 'bit' is a single 0 or 1 within a binary number. So we can think of the list of numbers as having columns, for example, like this (using the sample input from the instructions):

Table showing positions of each bit in each number

Each row is one 5-bit binary number in the array, and the columns show the positions of each bit within their respective numbers. For example if we were to find the index of the last bit in the first number, you could notate it as array[0][4].

To find the most common bit in each position, we need to count the zeroes and ones in every column. After recording the most common bit of each column in order, we have a new 5-bit binary number, which is the gamma value. Then we repeat with the least common bits to get the epsilon value. The puzzle doesn't specify what to do if there is an equal number of zeroes and ones in the column, but in part two the instructions say to record 1 as the most common bit in that case, so I implemented that for part one as well.

The puzzle answer is the decimal value of your gamma and epsilon results multiplied together.

On to the code. The input we're given will be read as a string, so we split it into an array of strings. It's easiest to keep the binary numbers as strings so we can index them.

const arr = binaryList.split("\n");

let x = 0;
let mostCommon = [];
let leastCommon = [];
Enter fullscreen mode Exit fullscreen mode

We also set up a counter and two arrays to record the most and least common bits. Next, nested loops!

while (x < arr[0].length) {
    let zeroes = 0;
    let ones = 0;
    for (let i of arr) {
      if (parseInt(i[x]) === 0) {
        zeroes++;
      } else if (parseInt(i[x]) === 1) {
        ones++;
      }
    }
    if (zeroes > ones) {
      mostCommon.push(0);
      leastCommon.push(1);
    } else {
      mostCommon.push(1);
      leastCommon.push(0);
    }
    x++;
  }
Enter fullscreen mode Exit fullscreen mode

I set the length of the 'while' loop to the length of the binary numbers, so that it works with both the sample and real inputs. The value x, which increases by one each loop, represents which column we are up to.

We create counters for the zeroes and ones. These will reset to 0 at the start of each loop. Then, the inner 'for' loop iterates through the list, counting how many zeroes and ones there are in position x.

Next, we push the most common bit into the mostCommon array, and the least common into the leastCommon array. As the 'while' loop continues, these two arrays will construct our gamma and epsilon values. Then, we just need to parse them.

const gamma = parseInt(mostCommon.join(""), 2);
const epsilon = parseInt(leastCommon.join(""), 2);
Enter fullscreen mode Exit fullscreen mode

parseInt() is a super useful function that can turn a string into an integer. What's more, if you give it the number system (radix) of the number you're converting as its second argument, it will take that into account too! Because we're converting from binary to decimal, we need to supply the radix of 2.

Now we can console.log(gamma * epsilon). How cool is that!

Part Two

This one took a bit of thought to understand. In this part we are essentially doing the same thing - counting the most and least common bits - but now at the end of each loop we need to filter the array.

The values we're looking for are 'oxygen' and 'CO2'. We'll set up a similar 'while' loop to part one, that loops through the columns. To get the oxygen value, for each loop, we determine the least common bit and remove from the array every number that contains that bit at position x (the column we are up to). To get the CO2 value, we do the same thing but removing every number that has the most common bit at position x.

Here's what that looks like:

let x = 0;
let arr = binaryList.split("\n");

while (arr.length > 1) {
  let zeroes = 0;
  let ones = 0;
  for (let i of arr) {
    if (parseInt(i[x]) === 0) {
      zeroes++;
    } else if (parseInt(i[x]) === 1) {
      ones++;
    }
  }
  if (zeroes > ones) {
    arr = arr.filter((i) => parseInt(i[x]) === 0);
  } else {
    arr = arr.filter((i) => parseInt(i[x]) === 1);
  }
  x++;
}

const oxygen = parseInt(arr[0], 2);
Enter fullscreen mode Exit fullscreen mode

There are a few things here. My first thought was actually could we use the gamma and epsilon values from part one so we don't have to count the most and least common bits again? But then I realised that won't work, because the array will shorten for each loop, changing the number of bits! Instead we need to use the same inner 'for' loop as before, in order to count them each loop.

So the code ends up looks pretty similar to part one. However, instead of using the most and least common bits to construct new numbers, we use them to filter the array using the array.filter() method.

The filter method takes two arguments, an array and a callback function that needs to return true or false. It outputs a new array containing only the items for which the function returned true. Because it outputs a new array, I'm reassigning the original array's variable to the new array each time filter is run. Thus, we need to use 'let' instead of 'const' when initialising the array's variable.

We run the while loop until there is only one item left in the list (arr.length is no longer greater than 1). This is the oxygen value! We can now parse this using parseInt().

To get the CO2 value, it's essentially the same code, just with the numbers in the final if/else statements switched!

x = 0;
arr = binaryList.split("\n");

while (arr.length > 1) {
  let zeroes = 0;
  let ones = 0;
  for (let i of arr) {
    if (parseInt(i[x]) === 0) {
      zeroes++;
    } else if (parseInt(i[x]) === 1) {
      ones++;
    }
  }
  if (zeroes > ones) {
    arr = arr.filter((i) => parseInt(i[x]) === 1);
  } else {
    arr = arr.filter((i) => parseInt(i[x]) === 0);
  }
  x++;
}

const co2 = parseInt(arr[0], 2);
Enter fullscreen mode Exit fullscreen mode

Now we can multiply oxygen and co2 to get the final answer. :)

If you were struggling with understanding this puzzle, I hope that made sense! Let me know if it doesn't or if I got anything wrong.

Thanks for reading! 👋

Top comments (0)