--- title: "Advent of Code 2023: Day One" date: 2023-12-01T09:59:44 slug: advent-of-code-2023-day-one tags: [advent-of-code-2023] --- [Advent of Code](https://adventofcode.com) is here, and once again I'm going to attempt it. Last year I got to about Day 11 before giving up. This year, I'll _try_ and beat that (but no promises). You can follow my progress with the [Advent of Code tag](https://lewisdale.dev/post/tag/advent-of-code-2023), or by subscribing to the [RSS feed](https://lewisdale.dev/post/tag/advent-of-code-2023/feed/), and the code is on my [Git](https://git.lewisdale.dev/lewis/advent-of-code-2023). As expected, this post (and the subsequent ones) will contain lots of spoilers for Advent of Code. Read at your own peril. ## Language Originally, I was planning to do it in PICO-8. But that sounded too much like hard work, so I'm doing it in Typescript. ## Part One The first part was relatively easy, given a bunch of strings that may-or-may not contain numbers, pick the first and last number, smoosh them together, and then sum them all. An example string might be `gasdad15asd5`, which should produce `15`. Easy-peasy. I just use a really simple Regex, and then pick the results out: ```js function parseLine(line: string): number { const pattern = /\d/g; const matches = line.match(pattern); if (!matches.length) return 0; const valueStr = `${matches[0]}${matches[matches.length - 1]}`; return parseInt(valueStr); } function calculate() { const lines = fs.readFileSync('./path/to/input.txt').toString('utf-8').split('\n'); console.log(lines.reduce((total, line) => total + parseLine(line), 0)); } ``` That gave me the correct answer, and I was able to move onto Part 2. ## Part Two This was a doozy. Now, we also need to parse the written forms of the numbers, e.g. `one`, `two`, `three`. I extended my Regex to capture these too, and then added a parser function that would convert the written number to the digit: ```js function parseDigit(digit: string): string { switch (digit) { case "one": return "1"; case "two": return "2"; case "three": return "3"; case "four": return "4"; case "five": return "5"; case "six": return "6"; case "seven": return "7"; case "eight": return "8"; case "nine": return "9"; default: return digit; } } function parseLine(line: string): number { const pattern = /\d|one|two|three|four|five|six|seven|eight|nine/g; const matches = line.match(pattern); if (!matches.length) return 0; const converted = matches.map(parseDigit); const valueStr = `${converted[0]}${converted[converted.length - 1]}`; return parseInt(valueStr); } ``` Everything looked good, the tests passed but... nope. Wrong result. ### Regex fun It turned out that the test cases had no examples for when the digits overlap. For example, `oneight` should be `18`, but I was getting `11`. That meant that I was always getting the wrong result. This is because standard global pattern matching _consumes_ the string - so once I've parsed `one`, the remaining string is `ight`, which gets discarded. To get around this, I had to add a look-ahead with capture group to my Regex, and then use `string.matchAll()`, rather than `string.match()` - because `string.match()` ignores capture groups: ```js function parseLine(line: string): number { const pattern = /(?=(\d|one|two|three|four|five|six|seven|eight|nine))/g; const matches = line.matchAll(pattern); if (!matches) return 0; const converted = [...matches].flatap(match => match.map(parseDigit)); // Matches is an iterator of RegExpMatchArrays, this converts it to String[] const valueStr = `${converted[0]}${converted[converted.length - 1]}`; return parseInt(valueStr); } ``` And finally, that worked. This made it sound easier than it was, in reality I spent a good 30-40 minutes scratching my head, and even switched languages (originally I was using Rust, like last year). But anyway, it's done, and at least I can move onto Day Two.