LD
Change your colour scheme

Advent of Code 2023: Day Six

Published:

Back to Advent of Code! This post contains spoilers. You can see the rest of the Advent of Code posts, or checkout the Git repository.

I missed yesterdays Advent of Code, so I’ll go back and do it later. But, until then, here’s Day Six.

Part One

We’re having a boat race! We’ve got some input that looks like:

Time:      7  15   30
Distance: 9 40 200

If we pair each column up, we get a Time/Distance pairing that tells us how long the race lasts, and how far we go. We can “charge up” our boat, by holding down the button, which gives us an extra 1mm/ms velocity. The task: work out how many different ways there are to win each race, and then multiply the results together.

Firstly, I’m using ParJS as an input parser, because I wanted something similar to Rust’s Nom that I used last year.

Creating the parser was simple enough:

const timeName = string('Time:').pipe(then(spaces1()));
const distanceName = string('Distance:').pipe(then(spaces1()));

const timeParser = int().pipe(manySepBy(whitespace().pipe(exactly(2))), between(timeName, whitespace()));
const distanceParser = int().pipe(manySepBy(whitespace().pipe(exactly(2))), between(distanceName, whitespace()));

const parser = timeParser.pipe(then(distanceParser));

export class BoatRace {
private races: Race[] = [];

constructor(input: string) {
const [times, distances] = parser.parse(input).value;

this.races = zip(times, distances) as Race[];
}
}

Then I just used Range from [immutable.js])(https://immutable-js.com/) to create all of the values between 0 and time, and filtered it using a function that tested whether the new velocity was enough to beat the distance with the remaining time. The resulting array length was my number of possible solutions:

 numberOfWinningMethods = (race: Race): number => {
const [time, distance] = race;

const canWin = (holdingTime: number) => ((time - holdingTime) * holdingTime) > distance;

const range = Range(0, time).filter(canWin).cacheResult();
return range.size || 0;
}
public totalNumberOfWaysToBeatRace(): number {
return this.races.map(this.numberOfWinningMethods).reduce((total, value) => total * value, 1);
}

Part Two

Oh no, bad kerning strikes again! It turns out that it’s not multiple races - it’s a single race and the whitespace is just throwing us off. So instead of 7 15 30, the time is actually 71530.

All I actually had to change here was my parser:

const numbersParser = anyCharOf("0123456789").pipe(manySepBy(whitespace().pipe(exactly(2))), stringify());
const timeParser = numbersParser.pipe(between(timeName, whitespace()));
const distanceParser = numbersParser.pipe(between(distanceName, whitespace()));

export class BoatRace {
private readonly race: Race;

constructor(input: string) {
const [times, distances] = parser.parse(input).value;
this.race = [parseInt(times), parseInt(distances)];
}

public totalNumberOfWaysToBeatRace(): number {
return this.numberOfWinningMethods(this.race);
}
}

And that was it! It ran first time! I’m sure there are more efficient solutions that you can get by working out the upper & lower bounds, but if I wanted to do maths by hand I wouldn’t have bought a computer.

Tags:

About the author

My face

I'm Lewis Dale, a software engineer and web developer based in the UK. I write about writing software, silly projects, and cycling. A lot of cycling. Too much, maybe.

Responses