import { readFileSync } from 'node:fs'; const input = readFileSync('input', 'utf-8'); const moves = input .split('\n') .filter(line => line) .map(line => { const [direction, multiplier] = line.split(' ', 2); return new Array(parseInt(multiplier)).fill(direction); }) .flat(); function ropeSimulation(nTailKnots) { const positions = [new Array(nTailKnots + 1).fill().map(_ => [0, 0])]; for (const move of moves) { const lastKnots = positions[positions.length - 1]; let [x, y] = lastKnots[0]; switch (move) { case 'U': y += 1; break; case 'D': y -= 1; break; case 'L': x -= 1; break; case 'R': x += 1; break; default: throw new Error('Invalid move'); } const knots = [[x, y]]; for (let n = 0; n < nTailKnots; n++) { ([x, y] = knots[knots.length - 1]); let [tx, ty] = lastKnots[n + 1]; if (y === ty && Math.abs(x - tx) === 2) { // Horizontal tavel tx += (x - tx) / Math.abs(x - tx); } else if (x === tx && Math.abs(y - ty) === 2) { // Vertical travel ty += (y - ty) / Math.abs(y - ty); } else if (Math.abs(x - tx) === 2 || Math.abs(y - ty) === 2) { // Diagonal travel tx += (x - tx) / Math.abs(x - tx); ty += (y - ty) / Math.abs(y - ty); } knots.push([tx, ty]); } positions.push(knots); } return positions; } function countLastKnotPositions(positions) { // Count unique positions of tail return positions .map(position => position[position.length - 1]) .filter((tailPosition, i, arr) => arr.findLastIndex(tp => tp[0] === tailPosition[0] && tp[1] === tailPosition[1] ) === i ) .length; } let positions = ropeSimulation(1); const totalPositionsOneKnot = countLastKnotPositions(positions); console.log( `Total unique positions of tail with 1 knot: ${totalPositionsOneKnot}` ); positions = ropeSimulation(9); const totalPositionsTenKnots = countLastKnotPositions(positions); console.log( `Total unique positions of tail with 10 knots: ${totalPositionsTenKnots}` );