I have not done anything like this since the last advent of code, and so felt very rusty trying to solve this one.

if (direction === "L") {
   clicksToZero = position || 100;
}

This is what really tripped me up as an edge case, if you start on 0 & move left, it's really 100 to zero, not 99. my answer kept being 2 short. two!!!!! and then this is just a lil trick that if it's false-y (which 0 is) then actually || it's 100 now ✨

tldr, final code with all of my comments:

import { sampleTurns, turns } from "./input";

let dialLocation: number = 50;
let zeroCount: number = 0;

function clicksToZero(
  direction: string,
  position: number,
  distance: number,
): number {
  let clicksToZero: number, remainingClicks: number;

  if (direction === "L") {
    clicksToZero = position || 100;
  }

  if (direction === "R") {
    clicksToZero = 100 - position;
  }

  remainingClicks = distance - clicksToZero;

  if (distance < clicksToZero) {
    return 0;
  }

  return 1 + Math.floor(remainingClicks / 100);
}

function turnDial(instruction: string) {
  // should do this in input.ts tomorrow
  const direction = instruction.substring(0, 1);
  const distanceNum = parseInt(instruction.replace(direction, ""), 10);

  // calculate the number of times we'll hit zero, before we move the dial (!! important in step two !!)
  zeroCount += clicksToZero(direction, dialLocation, distanceNum);

  if (direction === "L") {
    dialLocation -= distanceNum;
  } else if (direction === "R") {
    dialLocation += distanceNum;
  }

  dialLocation = ((dialLocation % 100) + 100) % 100;

  console.log("After turn:", direction, distanceNum);
  console.log("Current dial location:", dialLocation);
}

function solveCode() {
  const data = turns;
  // const data = sampleTurns;
  data.forEach(turnDial);
  console.log("The number of times the dial landed on zero:", zeroCount);
}

solveCode();