---
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.




