3_2.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import { DeepClone, Inspect, LoadInput } from "../common.ts";
  2. import { BuildSchematicsMap, ParseSchematics, ValidateEngineParts, SchematicsGear as SchematicsGear, EnginePart } from "./3_common.ts";
  3. const tests = [
  4. "467..114..",
  5. "...*......",
  6. "..35..633.",
  7. "......#...",
  8. "617*......",
  9. ".....+.58.",
  10. "..592.....",
  11. "......755.",
  12. "...$.*....",
  13. ".664.598..",
  14. ];
  15. /**
  16. * Test case from /u/i_have_no_biscuits
  17. * @see https://www.reddit.com/r/adventofcode/comments/189q9wv/2023_day_3_another_sample_grid_to_use/
  18. */
  19. const tests_reddit = [
  20. "12.......*..",
  21. "+.........34",
  22. ".......-12..",
  23. "..78........",
  24. "..*....60...",
  25. "78..........",
  26. ".......23...",
  27. "....90*12...",
  28. "............",
  29. "2.2......12.",
  30. ".*.........*",
  31. "1.1.......56",
  32. ];
  33. const input = await LoadInput(3);
  34. // Build the find
  35. const schematics = BuildSchematicsMap(input);
  36. // Find all possible parts
  37. let engineParts = ParseSchematics(schematics);
  38. // Validate those parts
  39. engineParts = ValidateEngineParts(engineParts, schematics);
  40. // Find all potential gears
  41. let gears = FindPotentialGears(schematics);
  42. // Validate those gears
  43. gears = ValidateGears(gears, engineParts);
  44. // Get our output
  45. const sumOfGearRatios = gears.reduce((accumulator, gear) => accumulator += gear.GearRatio, 0);
  46. console.log(`The sum of Gear Ratios is: ${sumOfGearRatios}`);
  47. /**
  48. * Find all potential gears within a set of schematics
  49. *
  50. * A gear is any * symbol. A valid gear is any gear thats adjacent to
  51. * exactly two part numbers.
  52. *
  53. * @param {string[][]} schematicsMap A 2D grid of characters making up a schematic
  54. * @returns {SchematicsGear[]} A list of potential gears
  55. */
  56. function FindPotentialGears(schematicsMap: string[][]): SchematicsGear[] {
  57. const gearsInSchematics: SchematicsGear[] = [];
  58. for (let y = 0; y < schematicsMap.length; y++) {
  59. for (let x = 0; x < schematicsMap[y].length; x++) {
  60. // Test if the current cell is a gear
  61. if (/[*]/.test(schematicsMap[y][x])) {
  62. gearsInSchematics.push({
  63. Symbol: "*",
  64. Coordinates: {
  65. X: x,
  66. Y: y,
  67. },
  68. GearRatio: -1,
  69. });
  70. }
  71. }
  72. }
  73. return gearsInSchematics;
  74. }
  75. /**
  76. * Validate a list of gears
  77. *
  78. * A valid gear is any gear thats adjacent to exactly two part numbers.
  79. *
  80. * @param {SchematicsGear[]} potentialGears A list of potential gears to validate
  81. * @param {EnginePart[]} engineParts A list of valid Engine Parts from the schematics
  82. * @returns {SchematicsGear[]} A list of valid gears
  83. */
  84. function ValidateGears(potentialGears: SchematicsGear[], engineParts: EnginePart[]): SchematicsGear[] {
  85. const validGears: SchematicsGear[] = [];
  86. for (const gear of potentialGears) {
  87. // Clone the parts list so we can freely modify it
  88. let partsClone = DeepClone(engineParts) as EnginePart[];
  89. // Filter for parts that are on the same Y level as the gear, or 1 above or below the gear
  90. partsClone = partsClone.filter((part) => {
  91. return part.Coordinates[0].Y == gear.Coordinates.Y
  92. || part.Coordinates[0].Y - 1 == gear.Coordinates.Y
  93. || part.Coordinates[0].Y + 1 == gear.Coordinates.Y;
  94. });
  95. // If there aren't at least two left, then it's definitely not valid
  96. if (partsClone.length < 2) { continue; }
  97. // Filter for parts that are on the same X-level, or 1 to the right or left of the gear
  98. partsClone = partsClone.filter((part) => {
  99. for(const coord of part.Coordinates) {
  100. if(gear.Coordinates.X == coord.X
  101. || gear.Coordinates.X + 1 == coord.X
  102. || gear.Coordinates.X - 1 == coord.X) {
  103. return true;
  104. }
  105. }
  106. return false;
  107. });
  108. // If there are exactly two left, it's valid
  109. if (partsClone.length == 2 ) {
  110. gear.GearRatio = partsClone.reduce((accumulator, part) => accumulator *= part.PartNumber, 1);
  111. validGears.push(gear);
  112. }
  113. }
  114. return validGears;
  115. }