import fs from "fs"; const input = fs.readFileSync("./input.txt").toString(); const [seedStr, ...mapStrings] = input.split("\n\n"); export function chunks(data: Array, chunkSize: number) { return data.reduce((acc, value, index) => { // Initialize a new chunk if (index % chunkSize === 0) acc.push([]); acc[acc.length - 1].push(value); return acc; }, [] as T[][]); } const seedRanges = chunks(seedStr.match(/\d+/g)?.map(Number) || [], 2).map( ([start, size]) => { return { start, size }; }, ); const maps = mapStrings.map((mapString) => mapString .split("\n") .slice(1) .filter((x) => x.length) .map((s) => s.match(/\d+/g)?.map(Number) || []) .map(([destinationStart, sourceStart, rangeSize]) => ({ sourceStart, destinationStart, rangeSize, })), ); // We dont need to inspect whole seed ranges, instead we only select "interesting points" // Interesting points lie at starts/end of ranges defined in our maps const seeds = maps .flatMap((ranges) => ranges.flatMap((r) => [r.sourceStart, r.destinationStart + 1]), ) .filter((breakpoint) => seedRanges.find( (range) => breakpoint >= range.start && breakpoint < range.start + range.size, ), ); const locationNumbers = seeds.map((seed) => maps.reduce((currentNumber, mapRules) => { const validRule = mapRules.find( (rule) => currentNumber >= rule.sourceStart && currentNumber < rule.sourceStart + rule.rangeSize, ); if (!validRule) return currentNumber; return currentNumber - validRule.sourceStart + validRule.destinationStart; }, seed), ); const result = locationNumbers.reduce((acc, x) => Math.min(acc, x), Infinity); console.log({ result });