Skip to content

Commit fafca3a

Browse files
committed
AoC2025: day 10 part 1
1 parent 1c1036e commit fafca3a

4 files changed

Lines changed: 123 additions & 0 deletions

File tree

2024-2025/setupDay.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ describe('${year} Day ${day}', () => {
8484
ws.write(content);
8585
ws.end();
8686

87+
const testInput1Path = path.join(`${dirName}/testInput1`);
88+
fs.writeFileSync(testInput1Path, '');
89+
console.log(`Empty testInput1 created!`);
90+
8791
const inputPath = path.join(`${dirName}/input`);
8892

8993
try {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import {part1, part2} from "./day10";
2+
3+
describe('2025 Day 10', () => {
4+
test('Part 1', async () => {
5+
expect(await part1('testInput1')).toEqual(7);
6+
expect(await part1('input')).toEqual(428);
7+
});
8+
9+
test('Part 2', async () => {
10+
// expect(await part2('testInput1')).toEqual(31);
11+
// expect(await part2('input')).toEqual(29379307);
12+
});
13+
});

2024-2025/src/2025/day10/day10.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import path from "node:path";
2+
import {readInputLineByLine} from "@utils/io";
3+
4+
export async function part1(inputFile: string) {
5+
return await day10(inputFile, calcButtonPresses);
6+
}
7+
8+
export async function part2(inputFile: string) {
9+
return await day10(inputFile);
10+
}
11+
12+
async function day10(inputFile: string, calcFn?: (machines: Machine[]) => number) {
13+
const inputPath = path.join(__dirname, inputFile);
14+
const lines = await readInputLineByLine(inputPath);
15+
const machines = lines.map(parseMachine);
16+
return calcFn?.(machines);
17+
}
18+
19+
type Machine = { target: bigint; buttons: bigint[]; };
20+
21+
function parseMachine(line: string): Machine {
22+
const diagramMatch = line.match(/\[([.#]+)\]/);
23+
const diagram = diagramMatch![1];
24+
let target = 0n;
25+
for (let i = 0; i < diagram.length; i++) {
26+
if (diagram[i] === '#') {
27+
target |= 1n << BigInt(i);
28+
}
29+
}
30+
31+
const buttons: bigint[] = [];
32+
const buttonMatches = line.matchAll(/\(([^)]+)\)/g);
33+
for (const match of buttonMatches) {
34+
const content = match[1].trim();
35+
if (content.length === 0) continue;
36+
let mask = 0n;
37+
for (const part of content.split(',')) {
38+
const idx = Number.parseInt(part.trim(), 10);
39+
if (Number.isNaN(idx)) continue;
40+
mask |= 1n << BigInt(idx);
41+
}
42+
buttons.push(mask);
43+
}
44+
45+
return {target, buttons};
46+
}
47+
48+
function calcButtonPresses(machines: Machine[]): number {
49+
let total = 0;
50+
machines.forEach((machine) => {
51+
total += minButtonPresses(machine.target, machine.buttons);
52+
});
53+
return total;
54+
}
55+
56+
function minButtonPresses(target: bigint, buttons: bigint[]): number {
57+
if (target === 0n) return 0;
58+
if (buttons.length === 0) return -1;
59+
60+
const mid = Math.floor(buttons.length / 2);
61+
const left = buttons.slice(0, mid);
62+
const right = buttons.slice(mid);
63+
64+
const leftMap = buildSubsetMinWeights(left);
65+
let best = Number.POSITIVE_INFINITY;
66+
67+
const visitRight = (index: number, mask: bigint, weight: number) => {
68+
if (index === right.length) {
69+
const needed = target ^ mask;
70+
const leftWeight = leftMap.get(needed);
71+
if (leftWeight !== undefined) {
72+
const total = leftWeight + weight;
73+
if (total < best) best = total;
74+
}
75+
return;
76+
}
77+
visitRight(index + 1, mask, weight);
78+
visitRight(index + 1, mask ^ right[index], weight + 1);
79+
};
80+
81+
visitRight(0, 0n, 0);
82+
83+
return Number.isFinite(best) ? best : -1;
84+
}
85+
86+
function buildSubsetMinWeights(buttons: bigint[]): Map<bigint, number> {
87+
const map = new Map<bigint, number>();
88+
89+
const visit = (index: number, mask: bigint, weight: number) => {
90+
if (index === buttons.length) {
91+
const existing = map.get(mask);
92+
if (existing === undefined || weight < existing) {
93+
map.set(mask, weight);
94+
}
95+
return;
96+
}
97+
visit(index + 1, mask, weight);
98+
visit(index + 1, mask ^ buttons[index], weight + 1);
99+
};
100+
101+
visit(0, 0n, 0);
102+
return map;
103+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}
2+
[...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2}
3+
[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}

0 commit comments

Comments
 (0)