Advent of Code, Day 2

Day 2 of Advent of Code is here! Check out Day 1 for what this is all about.

Let’s do it!

But first, the site also provides a leaderboard, so I went and created one. I invite you to join my board, and we can see who can win it ;) Go to “Leaderboard” in the top nav and enter 1030369-e174b794 to join my board.

I have also loaded up the pure code on github, you can follow along there too: pretty pages or the repo.

Spoilers ahead!

I highly encourage you to get through the day’s challenge on your own. I would love to hear how you did though! Did you find a better approach than I did?

Day 2: Passport Philsophy

Part I

We have a page with a long list of “passwords” with their “policies”.

The definition is as follows: “Each line gives the password policy and then the password. The password policy indicates the lowest and highest number of times a given letter must appear for the password to be valid. For example, 1-3 a means that the password must contain a at least 1 time and at most 3 times.” ref

The challenge is to find how many of these passwords are correct.

I opened dev toolbar again, and start by grabbing all of the passwords from the page:

var nums = window.document.getElementsByTagName("pre")[0]
nums = nums.innerText.split('\n')

Now nums is an array containing each row of the text.

I then grabbed one of the string values and created a quick regex to match the string pattern:

var p = "5-11 t: glhbttzvzttkdx"
var a = p.match(/([0-9]*)-([0-9]*)\s(.):\s([a-zA-Z]*)/)

I could’ve went with splitting a string, but found regex is much more elegant, gets me everything I need in 1 swoop.

What is that regex doing?

  • ([0-9]*) matches the first number, before the -
    • the ( ) group that value into it’s own value in the output, you’ll see below
  • - signifies the dash in the string, I don’t need it, but need what comes around it
  • ([0-9]*) matches the second number, after the -
  • \s is a space, like
  • (.) is any letter
  • :\s is the colon and space in the string
  • ([a-zA-Z]*) is the password string, any letter, lower or upper case

Learn more about regex hands on, and experiment with your text here: https://regex101.com.

And the output looks like

["5-11 t: glhbttzvzttkdx", "5", "11", "t", "glhbttzvzttkdx", index: 0, input: "5-11 t: glhbttzvzttkdx", groups: undefined]

As you can see, the match() function returns an array of objects. The first element is the entire string it matched, but then the next few are the specific values I wanted. In my regex, I wrapped the values I wanted in ( ) which groups that value and outputs it into unique params.

Let’s now apply some logic to determine if the password is correct. Let’s get the values we want out of the above array

var min = Number(a[1])
var max = Number(a[2])
var letter = a[3]
var password = a[4]

Now the fun part, checking for the policy in the password:

if(password.indexOf(letter) > -1) {
  var regx = new RegExp(letter, 'ig')
  var letterCount = password.match(regx)

  if(letterCount.length >= min && letterCount.length <= max) {
    console.log(password,'is valid')
    counter++;
  }
}

Here’s what we’re doing:

  • Check if the letter even exists, if not, don’t bother doing anything else
  • We then create a little regular expression to find the letter, the ig at the end indicates case-insensitive and globally, basically, find all references to it.
  • Then we match the regex against the password, this returns an array of matches, which will be the count of occurrences of the letter.

Now we piece it all together and get a count!

var counter = 0;
nums.forEach(pwd => {
    var a = pwd.match(/([0-9]*)-([0-9]*)\s(.):\s([a-zA-Z]*)/)
    if(a && a.length > 4) {
        var min = Number(a[1])
        var max = Number(a[2])
        var letter = a[3]
        var password = a[4]
        if(password.indexOf(letter) > -1) {
            var regx = new RegExp(letter, 'ig')
            var letterCount = password.match(regx)

            if(letterCount.length >= min && letterCount.length <= max) {
                console.log(password,'is valid')
                counter++;
            }
        }
    } else {
        console.error(pwd, 'not a valid password and policy')
    }
});
console.log(counter, 'out of', nums.length, 'passwords are legit')

I had a pain point here, I forgot about the g in the regex, without that, it will only find the first instance of the letter, not all letters.

Part II

Ah, the password policy was wrong, well, poorly interpreted by the team. (requirements gathering is really important, understanding the requirements is even more important :D)

For part 2, the actual password policy is: “Each policy actually describes two positions in the password, where 1 means the first character, 2 means the second character, and so on. (Be careful; Toboggan Corporate Policies have no concept of “index zero”!) Exactly one of these positions must contain the given letter. Other occurrences of the letter are irrelevant for the purposes of policy enforcement.” ref

Let’s start with our previous function and update it for these new rules

var counter = 0;
nums.forEach(pwd => {
    var a = pwd.match(/([0-9]*)-([0-9]*)\s(.):\s([a-zA-Z]*)/)
    if(a && a.length > 4) {
        var min = Number(a[1]) - 1
        var max = Number(a[2]) - 1
        var letter = a[3]
        var password = a[4]
        var hasMin = password.substring(min, min+1) === letter
        var hasMax = password.substring(max, max+1) === letter
        if(hasMin !== hasMax) {
            console.log(password,'is valid')
            counter++;
        }
    } else {
        console.error(pwd, 'not a valid password and policy')
    }
});
console.log(counter, 'out of', nums.length, 'passwords are legit')

The big changes are right in the middle of the code set.

  • Checking if the letter at the min and max location is the right letter, hasMin and hasMax would be true if the letter exists
  • Then we do a check, if hasMin does not equal hasMax, then we have a valid password.
    • Does that make sense? If hasMin is true, and hasMax is true, then this will fail since they don’t match each other. If they are both false then it’ll fail since they match. Only if one is true and one is false will it be valid, per the requirements

How did you do?

How did you find the answer on your own? How did you do it? I’d love to hear how you did! Please comment below!

Series Navigation<< Advent of Code, Day 3Advent of Code, Day 1 >>

Leave a Reply

Up ↑

%d bloggers like this: