const sample = await Deno.readTextFile("sample.txt"); const input = await Deno.readTextFile("input.txt"); type Direction = "D" | "R" | "L" | "U"; type Vector = { x: number; y: number }; type Step = { k0: Vector; k1: Vector; k2: Vector; k3: Vector; k4: Vector; k5: Vector; k6: Vector; k7: Vector; k8: Vector; k9: Vector; }; const vector = { add(vec1: Vector, vec2: Vector): Vector { return { x: vec1.x + vec2.x, y: vec1.y + vec2.y }; }, distance(vec1: Vector, vec2: Vector): number { return Math.sqrt((vec2.x - vec1.x) ** 2 + (vec2.y - vec1.y) ** 2); }, between(vec1: Vector, vec2: Vector): Vector { return { x: Math.floor((vec1.x + vec2.x) / 2), y: Math.floor((vec1.y + vec2.y) / 2), }; }, } as const; const directions: Record = { R: { x: 1, y: 0 }, D: { x: 0, y: 1 }, L: { x: -1, y: 0 }, U: { x: 0, y: -1 }, }; /**** PART 1 ********************************/ function lerp(start: number, end: number, t: number): number { return (1 - t) * start + t * end; } function mapRange( value: number, start1: number, end1: number, start2: number, end2: number ): number { const t = (value - start1) / (end1 - start1); return lerp(start2, end2, t); } const print = (width: number, height: number, steps: Step[]): void => { const grid = Array.from(new Array(height), () => Array.from(new Array(width), () => ".") ); steps.forEach(({ k9 }) => { grid[k9.y][k9.x] = "#"; }); for (let y = 0; y < height; y += 1) { let line = ""; for (let x = 0; x < width; x += 1) { line += grid[y][x]; } console.log(line); } }; const solvePart1 = (data: string): number | any => { const visitedSteps = data .split("\n") .filter(Boolean) .map((line) => line.split(" ") as [string, string]) .map(([dir, amount]): [Direction, number] => [ dir as Direction, parseFloat(amount), ]) .reduce( (steps, [dir, amount]) => { for (let i = 0; i < amount; i += 1) { const lastStep = steps[steps.length - 1]; const { k0 } = lastStep; const newStep = { ...lastStep, k0: vector.add(k0, directions[dir]), } as Step; for (let i = 1; i < 9; i += 1) { newStep["k" + i] = vector.distance(lastStep["k" + (i - 1)], lastStep["k" + i]) >= 1.5 ? lastStep["k" + (i - 1)] : lastStep["k" + i]; } steps.push(newStep); } return steps; }, [ { k0: { x: 0, y: 0 }, k1: { x: 0, y: 0 }, k2: { x: 0, y: 0 }, k3: { x: 0, y: 0 }, k4: { x: 0, y: 0 }, k5: { x: 0, y: 0 }, k6: { x: 0, y: 0 }, k7: { x: 0, y: 0 }, k8: { x: 0, y: 0 }, k9: { x: 0, y: 0 }, }, ] as Step[] ); console.log("visitedSteps", visitedSteps); const widthRange = visitedSteps.reduce( ({ min, max }, { h, t }) => { const stepMin = h[0] < t[0] ? h[0] : t[0]; const stepMax = h[0] > t[0] ? h[0] : t[0]; return { min: min < stepMin ? min : stepMin, max: max > stepMax ? max : stepMax, }; }, { min: 0, max: 0 } ); const width = widthRange.max - widthRange.min + 1; const heightRange = visitedSteps.reduce( ({ min, max }, { h, t }) => { const stepMin = h[1] < t[1] ? h[1] : t[1]; const stepMax = h[1] > t[1] ? h[1] : t[1]; return { min: min < stepMin ? min : stepMin, max: max > stepMax ? max : stepMax, }; }, { min: 0, max: 0 } ); const height = heightRange.max - heightRange.min + 1; const normalizedVisistedSteps = visitedSteps.map(({ h, t }) => { const yT = Math.round( mapRange(t[1], heightRange.min, heightRange.max, 0, height - 1) ); const xT = Math.round( mapRange(t[0], widthRange.min, widthRange.max, 0, width - 1) ); const yH = Math.round( mapRange(h[1], heightRange.min, heightRange.max, 0, height - 1) ); const xH = Math.round( mapRange(h[0], widthRange.min, widthRange.max, 0, width - 1) ); return { h: [yH, xH], t: [yT, xT] } as Step; }); print(width, height, normalizedVisistedSteps); return new Set(normalizedVisistedSteps.map(({ t }) => `${t[0]},${t[1]}`)) .size; }; console.log("Sample:", solvePart1(sample)); // 13 console.log("Input", solvePart1(input)); // 1851