|  | 
|  | 1 | +export {}; // make this a module | 
|  | 2 | + | 
|  | 3 | +const INPUT_FILE_NAME = './input.txt'; | 
|  | 4 | + | 
|  | 5 | +const getRawInput = async (fileName: string): Promise<string> => { | 
|  | 6 | +  const rawInput = await Deno.readTextFile(fileName); | 
|  | 7 | +  return rawInput; | 
|  | 8 | +} | 
|  | 9 | + | 
|  | 10 | +type Report = number[]; | 
|  | 11 | + | 
|  | 12 | +const parseInput = (rawInput: string): Report[] => { | 
|  | 13 | +  return rawInput.trim().split("\n").reduce<Report[]>( | 
|  | 14 | +    (reports, line) => { | 
|  | 15 | +      const values = line.trim().split(/\s+/).map((value) => parseInt(value.trim())); | 
|  | 16 | +      return reports.concat([values]); | 
|  | 17 | +    }, | 
|  | 18 | +    [], | 
|  | 19 | +  ); | 
|  | 20 | +} | 
|  | 21 | + | 
|  | 22 | +type Predicate<T> = (value: T) => boolean; | 
|  | 23 | + | 
|  | 24 | +const isReportSafe: Predicate<Report> = (report) => { | 
|  | 25 | +  if (report[1] === report[0]) { | 
|  | 26 | +    return false; | 
|  | 27 | +  } | 
|  | 28 | +  if (report[1] < report[0]) { | 
|  | 29 | +    return report.reduce( | 
|  | 30 | +      (isSafe, level, index) => { | 
|  | 31 | +        if (index === 0) { | 
|  | 32 | +          return isSafe; | 
|  | 33 | +        } | 
|  | 34 | +        const previousLevel = report[index - 1]; | 
|  | 35 | +        const difference = previousLevel - level; | 
|  | 36 | +        return isSafe && difference > 0 && difference <= 3; | 
|  | 37 | +      }, | 
|  | 38 | +      true, | 
|  | 39 | +    ); | 
|  | 40 | +  } | 
|  | 41 | +  if (report[1] > report[0]) { | 
|  | 42 | +    return report.reduce( | 
|  | 43 | +      (isSafe, level, index) => { | 
|  | 44 | +        if (index === 0) { | 
|  | 45 | +          return isSafe; | 
|  | 46 | +        } | 
|  | 47 | +        const previousLevel = report[index - 1]; | 
|  | 48 | +        const difference = level - previousLevel; | 
|  | 49 | +        return isSafe && difference > 0 && difference <= 3; | 
|  | 50 | +      }, | 
|  | 51 | +      true, | 
|  | 52 | +    ); | 
|  | 53 | +  } | 
|  | 54 | +  throw new Error('Invalid report'); | 
|  | 55 | +}; | 
|  | 56 | + | 
|  | 57 | + | 
|  | 58 | + | 
|  | 59 | +const countMatchingPredicate = <T>(predicate: Predicate<T>) => { | 
|  | 60 | +  return (values: T[]): number => { | 
|  | 61 | +    return values.reduce( | 
|  | 62 | +      (acc, value, index) => { | 
|  | 63 | +        return predicate(value) ? acc + 1 : acc; | 
|  | 64 | +      }, | 
|  | 65 | +      0 | 
|  | 66 | +    ) | 
|  | 67 | +  }; | 
|  | 68 | +}; | 
|  | 69 | + | 
|  | 70 | +void async function main() { | 
|  | 71 | +  const rawInput: string = await getRawInput(INPUT_FILE_NAME); | 
|  | 72 | +  const parsedInput: Report[] = parseInput(rawInput); | 
|  | 73 | +  const countSafeReports = countMatchingPredicate<Report>(isReportSafe); | 
|  | 74 | +  const safeReportCount: number = countSafeReports(parsedInput); | 
|  | 75 | +  console.log(safeReportCount); | 
|  | 76 | +}(); | 
0 commit comments