type PartInfo = { partNumber: number; begin: number; end: number; } function nextPartNumber(line: string, pos: 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 } } 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, y: number) { const neighbors = queryNeighbors(info.begin, 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); if (!part) { break; } pos = part.end + 1; // check valid part number if (!checkValidPartNumber(lines, part, y)) { 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); console.log([...iterPartNumbers(lines)]) console.log([...iterPartNumbers(lines)].reduce((a, b) => a + b.partNumber, 0)) // console.log([...queryNeighbors(7, 1, 3, width, height)] // .map(n => lines[n[1]][n[0]]) // )