Skip to content

Commit 94acbef

Browse files
committed
Add Day 1
1 parent b9e302d commit 94acbef

File tree

5 files changed

+1206
-1
lines changed

5 files changed

+1206
-1
lines changed

day-1/README.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Day 1: Historian Hysteria
2+
3+
<br>
4+
5+
## Part 1
6+
7+
You haven't even left yet and the group of Elvish Senior Historians has already hit a problem: their list of locations to check is currently
8+
**empty**. Eventually, someone decides that the best place to check first would be the Chief Historian's office.
9+
10+
Upon pouring into the office, everyone confirms that the Chief Historian is indeed nowhere to be found. Instead, the Elves discover an
11+
assortment of notes and lists of historically significant locations! This seems to be the planning the Chief Historian was doing before he
12+
left. Perhaps these notes can be used to determine which locations to search?
13+
14+
Throughout the Chief's office, the historically significant locations are listed not by name but by a unique number called the **location
15+
ID**. To make sure they don't miss anything, The Historians split into two groups, each searching the office and trying to create their own
16+
complete list of location IDs.
17+
18+
There's just one problem: by holding the two lists up **side by side** (your puzzle input), it quickly becomes clear that the lists aren't
19+
very similar. Maybe you can help The Historians reconcile their lists?
20+
21+
For example:
22+
23+
```txt
24+
3 4
25+
4 3
26+
2 5
27+
1 3
28+
3 9
29+
3 3
30+
```
31+
32+
Maybe the lists are only off by a small amount! To find out, pair up the numbers and measure how far apart they are. Pair up the **smallest
33+
number in the left list** with the **smallest number in the right list**, then the **second-smallest left number** with the
34+
**second-smallest right number**, and so on.
35+
36+
Within each pair, figure out **how far apart** the two numbers are; you'll need to **add up all of those distances**. For example, if you
37+
pair up a `3` from the left list with a `7` from the right list, the distance apart is `4`; if you pair up a `9` with a `3`, the distance
38+
apart is `6`.
39+
40+
In the example list above, the pairs and distances would be as follows:
41+
42+
- The smallest number in the left list is `1`, and the smallest number in the right list is `3`. The distance between them is `2`.
43+
- The second-smallest number in the left list is `2`, and the second-smallest number in the right list is another `3`. The distance between
44+
them is `1`.
45+
- The third-smallest number in both lists is `3`, so the distance between them is `0`.
46+
- The next numbers to pair up are `3` and `4`, a distance of `1`.
47+
- The fifth-smallest numbers in each list are `3` and `5`, a distance of `2`.
48+
- Finally, the largest number in the left list is `4`, while the largest number in the right list is `9`; these are a distance `5` apart.
49+
50+
To find the **total distance** between the left list and the right list, add up the distances between all of the pairs you found. In the
51+
example above, this is `2 + 1 + 0 + 1 + 2 + 5`, a total distance of `11`!
52+
53+
Your actual left and right lists contain many location IDs. **What is the total distance between your lists?**
54+
55+
<br>
56+
57+
## Part 2
58+
59+
Your analysis only confirmed what everyone feared: the two lists of location IDs are indeed very different.
60+
61+
Or are they?
62+
63+
The Historians can't agree on which group made the mistakes **or** how to read most of the Chief's handwriting, but in the commotion you
64+
notice an interesting detail: a lot of location IDs appear in both lists! Maybe the other numbers aren't location IDs at all but rather
65+
misinterpreted handwriting.
66+
67+
This time, you'll need to figure out exactly how often each number from the left list appears in the right list. Calculate a total
68+
**similarity score** by adding up each number in the left list after multiplying it by the number of times that number appears in the right
69+
list.
70+
71+
Here are the same example lists again:
72+
73+
```txt
74+
3 4
75+
4 3
76+
2 5
77+
1 3
78+
3 9
79+
3 3
80+
```
81+
82+
For these example lists, here is the process of finding the similarity score:
83+
84+
- The first number in the left list is `3`. It appears in the right list three times, so the similarity score increases by `3 * 3 = 9`.
85+
- The second number in the left list is `4`. It appears in the right list once, so the similarity score increases by `4 * 1 = 4`.
86+
- The third number in the left list is `2`. It does not appear in the right list, so the similarity score does not increase (`2 * 0 = 0`).
87+
- The fourth number, `1`, also does not appear in the right list.
88+
- The fifth number, `3`, appears in the right list three times; the similarity score increases by `9`.
89+
- The last number, `3`, appears in the right list three times; the similarity score again increases by `9`.
90+
91+
So, for these example lists, the similarity score at the end of this process is `31` (`9 + 4 + 0 + 0 + 9 + 9`).
92+
93+
Once again consider your left and right lists. **What is their similarity score?**

day-1/day-1.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { describe, it } from 'node:test';
2+
import assert from 'node:assert';
3+
import path from 'node:path';
4+
5+
import { calculateSimilarityScore, calculateTotalDistance } from './day-1';
6+
7+
describe('Day 1: Historian Hysteria', () => {
8+
const locationIdListsFilePath = path.join(__dirname, 'location-id-lists.txt');
9+
10+
it('Part 1: should calculate total distance', async () => {
11+
const expectedTotalDistance = 1873376; // Verified for this dataset
12+
13+
const calculatedTotalDistance = await calculateTotalDistance(locationIdListsFilePath);
14+
15+
assert.strictEqual(calculatedTotalDistance, expectedTotalDistance);
16+
});
17+
18+
it('Part 2: should calculate similarity score', async () => {
19+
const expectedSimilarityScore = 18997088; // Verified for this dataset
20+
21+
const calculatedTotalDistance = await calculateSimilarityScore(locationIdListsFilePath);
22+
23+
assert.strictEqual(calculatedTotalDistance, expectedSimilarityScore);
24+
});
25+
});

day-1/day-1.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import fs from 'node:fs/promises';
2+
3+
/**
4+
* Read file
5+
*/
6+
const readFile = async (filePath: string): Promise<string> => {
7+
const fileContents = await fs.readFile(filePath, {
8+
encoding: 'utf8',
9+
});
10+
const normalizedFileContents = fileContents.trim().split(/\r?\n/).join('\n');
11+
return normalizedFileContents;
12+
};
13+
14+
/**
15+
* Parse location ID lists
16+
*/
17+
const parseLocationIdLists = (locationIdListsFileContent: string): [Array<number>, Array<number>] => {
18+
// Get all location IDs by file line (horizontal)
19+
const locationIdsPerFileLine = locationIdListsFileContent.split('\n').map((fileContentLine) => {
20+
return [...fileContentLine.matchAll(/\d+/g)]
21+
.map((locationIdMatch) => {
22+
return locationIdMatch[0];
23+
})
24+
.map((locationIdAsString) => {
25+
return parseInt(locationIdAsString, 10);
26+
});
27+
});
28+
29+
// Group location IDs into lists (vertical)
30+
const locationIdLists: [Array<number>, Array<number>] = [[], []];
31+
for (let fileLineIndex = 0; fileLineIndex < locationIdsPerFileLine.length; fileLineIndex++) {
32+
for (let locationIdIndex = 0; locationIdIndex < locationIdsPerFileLine[fileLineIndex].length; locationIdIndex++) {
33+
locationIdLists[locationIdIndex][fileLineIndex] = locationIdsPerFileLine[fileLineIndex][locationIdIndex];
34+
}
35+
}
36+
37+
// Done
38+
return locationIdLists;
39+
};
40+
41+
/**
42+
* Part 1: Calculate total distance
43+
*/
44+
export const calculateTotalDistance = async (locationIdListsFilePath: string) => {
45+
// Get data
46+
const locationIdListsFileContent = await readFile(locationIdListsFilePath);
47+
const locationIdLists = parseLocationIdLists(locationIdListsFileContent);
48+
49+
// Sort lists
50+
locationIdLists.forEach((locationList) => {
51+
locationList.sort(); // Note: Mutates existing array
52+
});
53+
54+
// Calculate total distance
55+
let totalDistance = 0;
56+
for (let locationIdListIndex = 0; locationIdListIndex < locationIdLists[0].length; locationIdListIndex++) {
57+
totalDistance += Math.abs(locationIdLists[0][locationIdListIndex] - locationIdLists[1][locationIdListIndex]);
58+
}
59+
60+
// Done
61+
return totalDistance;
62+
};
63+
64+
/**
65+
* Part 2: Calculate similarity score
66+
*/
67+
export const calculateSimilarityScore = async (locationIdListsFilePath: string) => {
68+
// Get data
69+
const locationIdListsFileContent = await readFile(locationIdListsFilePath);
70+
const locationIdLists = parseLocationIdLists(locationIdListsFileContent);
71+
72+
// Get similarity score
73+
let similarityScore = 0;
74+
for (let locationIdListIndex = 0; locationIdListIndex < locationIdLists[0].length; locationIdListIndex++) {
75+
let countIdenticalLocationIds = 0;
76+
for (let secondLocationIdListIndex = 0; secondLocationIdListIndex < locationIdLists[1].length; secondLocationIdListIndex++) {
77+
if (locationIdLists[1][secondLocationIdListIndex] === locationIdLists[0][locationIdListIndex]) {
78+
countIdenticalLocationIds++;
79+
}
80+
}
81+
similarityScore += countIdenticalLocationIds * locationIdLists[0][locationIdListIndex];
82+
}
83+
84+
// Done
85+
return similarityScore;
86+
};

0 commit comments

Comments
 (0)