rmion

Robert Mion

Posted on December 1, 2023

Trebuchet?!

Advent of Code 2023 Day 1

Part 1

Three steps - at least as I see it

  1. A regular expression to find the digits
  2. Extracting the matches at the book ends
  3. Coercing, summing and reducing

Let's get to it!

A regular expression to find the digits
a1b2c3d4e5f
Enter fullscreen mode Exit fullscreen mode

I need a regular expression that matches only the five digits 1-5 in the string above.

Thankfully, that's as simple as:

/\d/g
Enter fullscreen mode Exit fullscreen mode
Extracting the matches at the book ends

Once I have the list of matches, I want the first and last ones only, event if they are the same from having only one digit in the string:

  matches[0] , matches[matches.length - 1]
Enter fullscreen mode Exit fullscreen mode
Coercing, summing and reducing

Then I need to coerce each match to numbers and fuse them together into a two-digit number:

+(matches[0] + matches[matches.length - 1])
Enter fullscreen mode Exit fullscreen mode

Lastly, this can all fit nicely in a larger reducer function that iterates through the full input:

For each input string, accumulate a number starting at 0
  Find all digits in the string
  Extract the first and last matches
  Fuse them together into a two-digit number
  Return the sum of the accumulated number and this two-digit number

Return the accumulated sums
Enter fullscreen mode Exit fullscreen mode

Altogether, in JavaScript

input.split('\n').reduce(
  (sums, text) => {
    const matches = [...text.matchAll(/\d/g)].map(el => el[0])
    return sums + +(matches[0] + matches[matches.length - 1])
  }, 0
)
Enter fullscreen mode Exit fullscreen mode

It generates the correct answer for the example input!

Confirming it works before submitting

This is what I see when running the algorithm on a few lines of my input:

[ '4' ] 44
[ '2' ] 22
[ '6' ] 66
[ '4' ] 44
[ '5' ] 55
[ '5', '2' ] 52
[ '1', '3', '7', '8', '1' ] 11
[ '8' ] 88
[ '4', '3', '1' ] 41
[ '6', '2', '2', '3' ] 63
Enter fullscreen mode Exit fullscreen mode

It successfully doubles the single digits, and fuses together the first and last digits!

Getting excited for Part 2

A quick glance at my input revealed some numbers spelled out:

gtjtwonefourone6fouronefccmnpbpeightnine
     one    one     one             nine
        four    four           eight
Enter fullscreen mode Exit fullscreen mode

I wonder if part two will pose the same challenge, but using the spelled out words instead of the digits?!

Submitting my answer to Part 1

It was correct!

Part 2

I was right!...and then some!

Part 2 does require using the spelled out numbers.

But in addition to - not instead of - the digits!

Hmmm, this...just...got...trickier.

Matching the spelled out numbers

Seems like there's no way to avoid a legend:

['one','two','three','four',...,'nine']
Enter fullscreen mode Exit fullscreen mode

Or is there...?

Could I do this with a regular expression, still?

/one|two|three|four|five|six|seven|eight|nine|\d/g
Enter fullscreen mode Exit fullscreen mode

It works!

But how will I convert words to numbers before fusing or doubling?

Turning words into numbers when necessary

How about a dictionary a.k.a. object?

one: 1,
two: 2,
three: 3,
...
nine: 9
Enter fullscreen mode Exit fullscreen mode

I'll need to check the matched string for whether it is a number:

if (isNaN(match))
  look-up in dictionary
else
  already a number
Enter fullscreen mode Exit fullscreen mode

All of that, in pseudocode

Set dictionary mapping words to digits
For each input string, accumulate a number starting at 0
  Find all numbers - word or digit - in the string
  Extract the first and last matches
  If either match is a word
    Convert word to digit
  Fuse them together into a two-digit number
  Return the sum of the accumulated number and this two-digit number

Return the accumulated sums
Enter fullscreen mode Exit fullscreen mode

Now, in JavaScript

const dict = { 'one': '1', 'two': '2', ... }
input.split('\n').reduce((sums, text) => {
  const regex = /one|two|three|four|five|six|seven|eight|nine|\d/g
  const matches = [...text.matchAll(regex)].map(el => el[0])
  let [first, last] = [matches[0], matches[matches.length - 1]]
  if (isNaN(first)) first = dict[first]
  if (isNaN(last)) last = dict[last]
  return sums + +(first + last)
}, 0)
Enter fullscreen mode Exit fullscreen mode

Checking and submitting

It works on the example input!

The console shows the expected extractions for each line.

It generated the correct answer for my input, too!

Lovin' me some regex

I'm so glad I took the time to learn and practice regex in earlier AoC challenges.

Regexr has been an invaluable tool as a beginner.

I'd hate to imagine being a beginner and trying today's challenge!

If that's you, I hope you find this series helpful and validation that you too can go from CS newbie to near-expert solver (except for shortest-path and performance-based algorithms) with enough practice, persistence and patience.

And now we wait for Day 2 to unlock...

💖 💪 🙅 🚩
rmion
Robert Mion

Posted on December 1, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Trebuchet?!
adventofcode Trebuchet?!

December 1, 2023