-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
146 lines (130 loc) · 4.5 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import run from "aocrunner";
import { range } from "../utils/index.js";
const parseInput = (rawInput: string) => rawInput.split("\n");
const part1 = (rawInput: string) => {
const input = parseInput(rawInput);
// Regular expressions as constants
const AT_LEAST_ONE_DIGIT_REGEX = /\d+/g;
const NOT_A_DOT_OR_DIGIT_REGEX = /[^.\d]/g;
// Helper function to calculate the start and end indices for slicing
const getSliceIndices = (matchIndex: number, matchLength: number, lineLength: number) => {
const startIndex = Math.max(matchIndex - 1, 0);
const endIndex = Math.min(matchIndex + matchLength, lineLength) + 1;
return { startIndex, endIndex };
};
// Helper function to check if a range has symbol in a given line
const hasSymbolInRange = (line: string | undefined, start: number, end: number) => {
return line ? NOT_A_DOT_OR_DIGIT_REGEX.test(line.slice(start, end)) : false;
};
const getResult = () => {
return input.reduce((acc, line, i) => {
const digitsMatches = line.matchAll(AT_LEAST_ONE_DIGIT_REGEX);
for (const match of digitsMatches) {
const matchedString = match[0];
const { startIndex, endIndex } = getSliceIndices(match.index, matchedString.length, line.length);
const hasSymbolPrevLine = hasSymbolInRange(input[i - 1], startIndex, endIndex);
const hasSymbolCurrLine = hasSymbolInRange(input[i], startIndex, endIndex);
const hasSymbolNextLine = hasSymbolInRange(input[i + 1], startIndex, endIndex);
if (hasSymbolPrevLine || hasSymbolCurrLine || hasSymbolNextLine) {
acc += Number(matchedString);
}
}
return acc;
}, 0);
};
const result = getResult();
return result;
};
const part2 = (rawInput: string) => {
const input = parseInput(rawInput);
const AT_LEAST_ONE_DIGIT_REGEX = /\d+/g;
const ASTERISK_REGEX = /\*/g;
// Helper function to calculate the start and end indices for slicing
const getSliceIndicesPart2 = (matchIndex: number, matchLength: number, lineLength: number) => {
const startIndex = Math.max(matchIndex - 1, 0);
const endIndex = Math.min(matchIndex + matchLength, lineLength);
return { startIndex, endIndex };
};
const getMatchingNumbersByLine = (line: string, startIndex: number, endIndex: number) => {
const results = [];
const matches = line.matchAll(AT_LEAST_ONE_DIGIT_REGEX);
const starMatchingIndexes = range(startIndex, endIndex);
for (const match of matches) {
const matchedString = match[0];
const matchStartIndex = match.index;
const matchEndIndex = match.index + matchedString.length - 1;
const matchIndexes = range(matchStartIndex, matchEndIndex);
// Check if any of the match indexes overlap with the star matching indexes
const isIndexOverlap = matchIndexes.some((index) => starMatchingIndexes.includes(index));
if (isIndexOverlap) {
results.push(Number(matchedString));
}
}
return results;
};
const getMatchingNumbers = (lineIndex: number, startIndex: number, endIndex: number) => {
const results: Array<number> = [];
const prevLine = lineIndex > 0 ? input[lineIndex - 1] : "";
const currLine = input[lineIndex];
const nextLine = lineIndex < input.length - 1 ? input[lineIndex + 1] : "";
// Collect results from all three lines
results.push(...getMatchingNumbersByLine(prevLine, startIndex, endIndex));
results.push(...getMatchingNumbersByLine(currLine, startIndex, endIndex));
results.push(...getMatchingNumbersByLine(nextLine, startIndex, endIndex));
return results;
};
const result = input.reduce((acc, curr, index) => {
const matches = curr.matchAll(ASTERISK_REGEX);
for (const match of matches) {
const { startIndex, endIndex } = getSliceIndicesPart2(match.index, match[0].length, curr.length);
const matchingNumbers = getMatchingNumbers(index, startIndex, endIndex);
if (matchingNumbers.length === 2) {
acc += matchingNumbers[0] * matchingNumbers[1];
}
}
return acc;
}, 0);
return result;
};
run({
part1: {
tests: [
{
input: `
467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598..`,
expected: 4361,
},
],
solution: part1,
},
part2: {
tests: [
{
input: `
467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598..`,
expected: 467835,
},
],
solution: part2,
},
trimTestInputs: true,
onlyTests: true,
});