aoc-2023/day_3/solve_2.ts
2024-12-09 22:41:02 +09:00

162 lines
4.1 KiB
TypeScript

type PartInfo = {
partNumber: number;
begin: number;
end: number;
y: number;
}
function nextPartNumber(line: string, pos: number, y: number): PartInfo | null {
const numbers: string[] = [];
// eat characters until a number is found
while (pos < line.length) {
const ch = line[pos];
if (/\d/.test(ch)) {
break;
}
pos++;
}
// collect numbers
while (pos < line.length) {
const ch = line[pos];
if (!/\d/.test(ch)) {
break;
}
numbers.push(ch);
pos++;
}
if (numbers.length === 0) {
return null;
}
const partNumber = parseInt(numbers.join(""))
return {
partNumber,
begin: pos - numbers.length,
end: pos - 1, // inclusive
y,
}
}
function* queryNeighbors(x: number, y: number, boxWidth: number, width: number, height: number) {
boxWidth = Math.min(boxWidth, width - x);
if (x > 0) {
if (y > 0) {
yield [x - 1, y - 1];
}
yield [x - 1, y];
if (y < height - 1) {
yield [x - 1, y + 1];
}
}
if (x + boxWidth < width) {
if (y > 0) {
yield [x + boxWidth, y - 1];
}
yield [x + boxWidth, y];
if (y < height - 1) {
yield [x + boxWidth, y + 1];
}
}
if (y > 0) {
for (let i = x; i < x + boxWidth; i++) {
yield [i, y - 1];
}
}
if (y < height - 1) {
for (let i = x; i < x + boxWidth; i++) {
yield [i, y + 1];
}
}
}
function checkValidPartNumber(lines: string[], info: PartInfo) {
const neighbors = queryNeighbors(info.begin, info.y, info.end - info.begin + 1, lines[0].length, lines.length);
for (const [x, y] of neighbors) {
const line = lines[y];
const ch = line[x];
if ((!/\d/.test(ch)) && ch !== ".") {
return true;
}
}
return false;
}
function* iterPartNumbers(lines: string[]) {
let pos = 0;
let y = 0;
while (y < lines.length) {
const line = lines[y];
while (pos < line.length) {
const part = nextPartNumber(line, pos, y);
if (!part) {
break;
}
pos = part.end + 1;
// check valid part number
if (!checkValidPartNumber(lines, part)) {
continue;
}
yield part;
}
pos = 0;
y++;
}
}
// const example = `467..114..
// ...*......
// ..35..633.
// ......#...
// 617*......
// .....+.58.
// ..592.....
// ......755.
// ...$.*....
// .664.598..`;
const example = await Deno.readTextFile("input.txt");
const lines = example.split("\n").map(x => x.trim()).filter(x => x.length > 0);
const width = lines[0].length;
const height = lines.length;
console.log(width, height);
type GearCandidate = {
x: number;
y: number;
id: number;
neighbors: PartInfo[];
}
const gearCandidates: GearCandidate[] = [];
const coordinateToGearIndex = new Map<string, number>();
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
for (let j = 0; j < line.length; j++) {
if (line[j] === "*") {
const index = gearCandidates.length;
coordinateToGearIndex.set(`${j},${i}`, index);
gearCandidates.push({
x: j,
y: i,
id: index,
neighbors: [],
});
}
}
}
for (const part of iterPartNumbers(lines)) {
const { begin, end, y } = part;
const neighbors = queryNeighbors(begin, y, end - begin + 1, width, height);
for (const [x, y] of neighbors) {
const id = coordinateToGearIndex.get(`${x},${y}`);
if (id !== undefined) {
gearCandidates[id].neighbors.push(part);
}
}
}
console.log(gearCandidates);
console.log(gearCandidates.filter(c => c.neighbors.length == 2).map(c=> c.neighbors[0].partNumber * c.neighbors[1].partNumber).reduce((a, b) => a + b, 0));
// console.log([...queryNeighbors(7, 1, 3, width, height)]
// .map(n => lines[n[1]][n[0]])
// )