2023-12-05 01:38:49 +00:00
|
|
|
async function loadInput(filename: string) {
|
|
|
|
const decoder = new TextDecoder("utf-8");
|
|
|
|
const contents = await Deno.readFile(filename);
|
|
|
|
return decoder.decode(contents);
|
|
|
|
}
|
|
|
|
|
|
|
|
function toSubComponents(l: string) {
|
|
|
|
const components: [number, string][] = [];
|
|
|
|
let current = "";
|
|
|
|
let idx = 0;
|
|
|
|
for (const c of l) {
|
|
|
|
if (
|
|
|
|
current.length === 0 ||
|
|
|
|
!Number.isNaN(Number(current[0])) && !Number.isNaN(Number(c))
|
|
|
|
) {
|
|
|
|
current += c;
|
|
|
|
} else {
|
|
|
|
components.push([idx - current.length, current]);
|
|
|
|
current = c;
|
|
|
|
}
|
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
components.push([idx - current.length, current]);
|
|
|
|
return components;
|
|
|
|
}
|
|
|
|
|
|
|
|
type Component = [number, number, string];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks whether two components are adjacent
|
|
|
|
* @param c1 First component.
|
|
|
|
* @param c2 Second component.
|
|
|
|
*/
|
|
|
|
function isAdjacent([y1, x1, s1]: Component, [y2, x2, s2]: Component) {
|
|
|
|
const [l1, l2] = [s1.length, s2.length];
|
|
|
|
// end x positions
|
|
|
|
const [x1e, x2e] = [x1 + l1 - 1, x2 + l2 - 1];
|
|
|
|
return (y1 === y2 - 1 || y1 === y2 + 1) && (
|
|
|
|
(x1 - 1 <= x2 && x2 <= x1e + 1) ||
|
|
|
|
(x1 - 1 <= x2e && x2e <= x1e + 1) ||
|
|
|
|
(x2 <= x1 - 1 && x1e + 1 <= x2e)
|
|
|
|
) || (y1 === y2) && (x2e === x1 - 1 || x2 === x1e + 1);
|
|
|
|
}
|
|
|
|
|
2023-12-05 01:41:44 +00:00
|
|
|
function isPartNumber(
|
|
|
|
components: Component[],
|
|
|
|
component: Component,
|
|
|
|
) {
|
|
|
|
return Boolean(
|
|
|
|
components.find((c) => isAdjacent(component, c) && c[2] !== "."),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-12-05 01:38:49 +00:00
|
|
|
function gearRatio(
|
|
|
|
components: Component[],
|
|
|
|
component: Component,
|
|
|
|
) {
|
|
|
|
if (component[2] !== "*") return null;
|
|
|
|
const numbers = components
|
|
|
|
.filter((c) => isAdjacent(component, c))
|
|
|
|
.map(([, , s]) => Number(s))
|
|
|
|
.filter((n) => !Number.isNaN(n));
|
|
|
|
if (numbers.length === 2) {
|
|
|
|
return numbers[0] * numbers[1];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function main() {
|
|
|
|
const lines = await loadInput("input.txt").then((s) => s.split("\n"));
|
|
|
|
const components = lines.map(toSubComponents)
|
|
|
|
.flatMap((cs, idx) => cs.map((c) => [idx, c[0], c[1]] as Component));
|
|
|
|
const numbers = components.filter(([, , s]) => !Number.isNaN(Number(s)));
|
|
|
|
const fnumbers = numbers.filter((c) => isPartNumber(components, c));
|
|
|
|
console.log(
|
|
|
|
"Part 1:",
|
|
|
|
fnumbers.map(([, , s]) => Number(s)).reduce((p, c) => p + c),
|
|
|
|
);
|
|
|
|
const gears = components
|
|
|
|
.map((c) => gearRatio(components, c))
|
|
|
|
.filter((c) => c !== null) as number[];
|
|
|
|
console.log("Part 2:", gears.reduce((p, c) => p + c));
|
|
|
|
}
|
|
|
|
|
|
|
|
await main();
|