|  | 
|  | 1 | +import type { Point } from '../../2024-12-08/lib/types.js' | 
|  | 2 | +import type { InputOptions, PointSteps, PointDirection, TrailScores } from './types.js' | 
|  | 3 | + | 
|  | 4 | +import { | 
|  | 5 | +  findValidSteps, | 
|  | 6 | +  findZeroCoordinatePositions, | 
|  | 7 | +  getCoordinateSymbol | 
|  | 8 | +} from './utils.js' | 
|  | 9 | + | 
|  | 10 | +// List of trailhead scores | 
|  | 11 | +const scores: Record<string, string[]> = {} | 
|  | 12 | +let activeZeroIndex = '' | 
|  | 13 | + | 
|  | 14 | +/** | 
|  | 15 | + * Finds valid hiking trails (trailheads) from a point coordinate in a 2D array starting `0` and ending in `9` symbols and | 
|  | 16 | + * calculates the scores for each trailhead. | 
|  | 17 | + * @param {PointDirection} pointVector - Point (y,x) coordinate in a 2D array with a list of valid coordinates from its location. | 
|  | 18 | + * @param {number[][]} data - 2D number array containing hiking trail data | 
|  | 19 | + * @param {boolean} isRating - If `true`, calculates the trailhead ratings instead of the trailhead scores. Defaults to `false` | 
|  | 20 | + * @returns {void} | 
|  | 21 | + */ | 
|  | 22 | +const findPaths = (pointVector: PointDirection, data: number[][], isRating: boolean = false) => { | 
|  | 23 | +  const grid = { | 
|  | 24 | +    length: data.length, width: data[0]!.length | 
|  | 25 | +  } | 
|  | 26 | + | 
|  | 27 | +  if (pointVector.validSteps.length > 0) { | 
|  | 28 | +    while (pointVector.validSteps.length > 0) { | 
|  | 29 | +      const step = pointVector.validSteps.pop() | 
|  | 30 | + | 
|  | 31 | +      if (step === undefined) continue | 
|  | 32 | +      const pt = getCoordinateSymbol(step, data) | 
|  | 33 | + | 
|  | 34 | +      if (pt.symbol === 9) { | 
|  | 35 | +        if (isRating) { | 
|  | 36 | +          // Rating: count all trails ending in 9's | 
|  | 37 | +          scores[activeZeroIndex]?.push(pt.coordinate) | 
|  | 38 | +        } else { | 
|  | 39 | +          // Scores: count unique ending 9's that match with the starting 0 | 
|  | 40 | +          if (!scores[activeZeroIndex]!.includes(pt.coordinate)) { | 
|  | 41 | +            scores[activeZeroIndex]?.push(pt.coordinate) | 
|  | 42 | +          } | 
|  | 43 | +        } | 
|  | 44 | +      } | 
|  | 45 | + | 
|  | 46 | +      const point: PointDirection = { | 
|  | 47 | +        x: step!.x, | 
|  | 48 | +        y: step!.y, | 
|  | 49 | +        validSteps: findValidSteps(step as Point, grid, data) as PointSteps[] | 
|  | 50 | +      } | 
|  | 51 | + | 
|  | 52 | +      findPaths(point, data, isRating) | 
|  | 53 | +    } | 
|  | 54 | +  } | 
|  | 55 | +} | 
|  | 56 | + | 
|  | 57 | +/** | 
|  | 58 | + * Finds valid trailheads and counts each trailhead score. | 
|  | 59 | + * @param {number[][]} data - 2D number array containing hiking trail data | 
|  | 60 | + * @param {boolean} [printLog] - Flag to display the processing and total score logs | 
|  | 61 | + * @typedef {InputOptions} params - Input and logging parameter options | 
|  | 62 | + * @param {boolean} [params.printLog] - (Optional) Flag to display the miscellaneous data processing logs. | 
|  | 63 | + * @param {boolean} [params.isRating] - (Optional) Flag to calculate the trailhead rating instead of the score. | 
|  | 64 | + * @returns {TrailScores} | 
|  | 65 | + */ | 
|  | 66 | +export const countTrailScores = (data: number[][], params?: InputOptions): TrailScores => { | 
|  | 67 | +  // Find starting positions | 
|  | 68 | +  const starts = findZeroCoordinatePositions(data) | 
|  | 69 | + | 
|  | 70 | +  const grid = { | 
|  | 71 | +    length: data.length, width: data[0]!.length | 
|  | 72 | +  } | 
|  | 73 | + | 
|  | 74 | +  for (let i = 0; i < starts.length; i += 1) { | 
|  | 75 | +    const initStep: PointDirection = { | 
|  | 76 | +      x: starts[i]!.x, | 
|  | 77 | +      y: starts[i]!.y, | 
|  | 78 | +      validSteps: findValidSteps(starts[i] as Point, grid, data) as PointSteps[] | 
|  | 79 | +    } | 
|  | 80 | + | 
|  | 81 | +    const pt = getCoordinateSymbol(starts[i] as Point, data) | 
|  | 82 | +    activeZeroIndex = pt.coordinate | 
|  | 83 | +    scores[activeZeroIndex] = [] | 
|  | 84 | + | 
|  | 85 | +    findPaths(initStep, data, params?.isRating) | 
|  | 86 | +  } | 
|  | 87 | + | 
|  | 88 | +  const total = Object | 
|  | 89 | +    .values(scores) | 
|  | 90 | +    .map(x => x.length) | 
|  | 91 | +    .reduce((sum, item) => sum += item, 0) | 
|  | 92 | + | 
|  | 93 | +  if (params?.printLog) { | 
|  | 94 | +    for (const key in scores) { | 
|  | 95 | +      console.log(`[${key}]: ${scores[key]?.length} score`) | 
|  | 96 | +    } | 
|  | 97 | + | 
|  | 98 | +    console.log('--TOTAL SCORE', total) | 
|  | 99 | +  } | 
|  | 100 | + | 
|  | 101 | +  return { | 
|  | 102 | +    scores, | 
|  | 103 | +    total | 
|  | 104 | +  } | 
|  | 105 | +} | 
0 commit comments