JavaScript generators are 👌

JavaScript generators are 👌

Advent of Code Day 15 was basically all about finding the right programming language. It needs built-in support for generators. You don’t want to build that from scratch.

But what’s a generator anyway?

Well, a generator is like an infinite loop that doesn’t cause problems. The computer holds it paused in memory and only advances to the next iteration when you say, “Hey, what’s the next value?”

I don’t know how it’s implemented internally. If I had to guess, I’d say the engine holds the entire state on a stack of some sort and advances values when you ask for the next step. A lot like recursion.

The full generator for Day 15 looks like this 👇

function* star2generator(factor, startingValue, denominator) {
    let val = startingValue;
    while (true) {
        val = (val * factor) % 2147483647;
        if (val % denominator === 0) {
            yield val;
        }
    }
}

This starts from a startingValue and multiplies val with factor and mods the result with 2147483647 forever. Notice the while(true)? When I say forever, I mean forever.

But it won’t crash your node runtime or your browser. Because function* and yield turn it into a generator that follows the “only execute when called” logic.

You get the next value like this:

const A = generator(16807, startA, denomA);
A.next().value // some number
A.next().value // next number

The full generator only returns values that are divisible by a denominator. That’s from part 2 of Day 15. The first part returned every value.

If that 2147483647 number looks familiar, it’s because that’s the highest number you can represent in 32 bits. This is significant because we’re going to be doing some bitwise operations and JavaScript can only do those up to 32 bits.

Numbers are generally represented as 64 bits in JavaScript.

Wtf are we doing?

Right, so that’s how generators work. The reason we need generators is that the puzzle for Advent of Code Day 15 goes like this

Take two generators that follow similar logic and see how often the lowest 16 bits of their output match in 40,000,000 tries.

For Star 2 we make them sync up a little better, check for only 5,000,000 tries

The generator above can handle both puzzle generators. They only differ in starting value and denominator.

To count the number of matches, I used this function 👇

function countMatches({
    startA,
    startB,
    generator,
    denominators = [],
    sampleSize
}) {
    const A = generator(16807, startA, denominators[0]),
        B = generator(48271, startB, denominators[1]);
 
    let judge = 0,
        mask = 0b00000000000000001111111111111111;
 
    for (let i = 0; i < sampleSize; i++) {
        if ((A.next().value & mask) === (B.next().value & mask)) {
            judge += 1;
        }
    }
 
    return judge;
}

The same function can do both Star 1 and Star 2 of the puzzle depending on input.

Here’s how it works:

  1. Create generators A and B
  2. Start at count of 0 (the judge variable)
  3. Create a mask that takes out the lower 16 bits of a number
  4. Loop until sampleSize
  5. Take next() values from the generators, bitwise AND them with the mask1, compare values
  6. If numbers match, increment judge
  7. When it’s done looping, judge is our result

Solving for Star 1 and Star 2 then becomes a matter of calling the matchCount function with our inputs.

console.log(
    countMatches({
        startA: 591,
        startB: 393,
        generator: star2generator,
        denominators: [4, 8],
        sampleSize: 5000000
    })
);

Boom 💥 puzzle solved in about 20 minutes. 🤙

And now you know how generators work. Although I still don’t know where you’d use them in a web app.

  1. If you’re not used to bitwise operations, they’re rare in JavaScript, the idea is that bits follow a truth table. 0 & 0 == 0, 0 & 1 == 0, 1 & 0 == 0, 1 & 1 == 1. You can use this to cut away parts of numbers with a mask. For example 0b1010 & 0b0011 == 0b0010. 🤓

Learned something new? Want to become a better engineer? 💌

Join 9,400+ people just like you already improving their skills.

Here's how it works 👇

Leave your email and I'll send you an Interactive Modern JavaScript Cheatsheet 📖 right away. After that you'll get a thoughtfully written email every week about React, JavaScript,  and lessons learned in my 20 years of writing code for companies ranging from tiny startups to Fortune5 behemoths.



Man, I love your way of writing these newsletters. Often very relatable and funny perspectives about the mundane struggles of a dev. Lightens up my day. ~ Kostas

PS: You should also follow me on twitter 👉 here.
It's where I go to shoot the shit about programming.