import { DeepClone, Inspect, LoadInput } from "../common.ts"; import { BuildSchematicsMap, ParseSchematics, ValidateEngineParts, SchematicsGear as SchematicsGear, EnginePart } from "./3_common.ts"; const tests = [ "467..114..", "...*......", "..35..633.", "......#...", "617*......", ".....+.58.", "..592.....", "......755.", "...$.*....", ".664.598..", ]; /** * Test case from /u/i_have_no_biscuits * @see https://www.reddit.com/r/adventofcode/comments/189q9wv/2023_day_3_another_sample_grid_to_use/ */ const tests_reddit = [ "12.......*..", "+.........34", ".......-12..", "..78........", "..*....60...", "78..........", ".......23...", "....90*12...", "............", "2.2......12.", ".*.........*", "1.1.......56", ]; const input = await LoadInput(3); // Build the find const schematics = BuildSchematicsMap(input); // Find all possible parts let engineParts = ParseSchematics(schematics); // Validate those parts engineParts = ValidateEngineParts(engineParts, schematics); // Find all potential gears let gears = FindPotentialGears(schematics); // Validate those gears gears = ValidateGears(gears, engineParts); // Get our output const sumOfGearRatios = gears.reduce((accumulator, gear) => accumulator += gear.GearRatio, 0); console.log(`The sum of Gear Ratios is: ${sumOfGearRatios}`); /** * Find all potential gears within a set of schematics * * A gear is any * symbol. A valid gear is any gear thats adjacent to * exactly two part numbers. * * @param {string[][]} schematicsMap A 2D grid of characters making up a schematic * @returns {SchematicsGear[]} A list of potential gears */ function FindPotentialGears(schematicsMap: string[][]): SchematicsGear[] { const gearsInSchematics: SchematicsGear[] = []; for (let y = 0; y < schematicsMap.length; y++) { for (let x = 0; x < schematicsMap[y].length; x++) { // Test if the current cell is a gear if (/[*]/.test(schematicsMap[y][x])) { gearsInSchematics.push({ Symbol: "*", Coordinates: { X: x, Y: y, }, GearRatio: -1, }); } } } return gearsInSchematics; } /** * Validate a list of gears * * A valid gear is any gear thats adjacent to exactly two part numbers. * * @param {SchematicsGear[]} potentialGears A list of potential gears to validate * @param {EnginePart[]} engineParts A list of valid Engine Parts from the schematics * @returns {SchematicsGear[]} A list of valid gears */ function ValidateGears(potentialGears: SchematicsGear[], engineParts: EnginePart[]): SchematicsGear[] { const validGears: SchematicsGear[] = []; for (const gear of potentialGears) { // Clone the parts list so we can freely modify it let partsClone = DeepClone(engineParts) as EnginePart[]; // Filter for parts that are on the same Y level as the gear, or 1 above or below the gear partsClone = partsClone.filter((part) => { return part.Coordinates[0].Y == gear.Coordinates.Y || part.Coordinates[0].Y - 1 == gear.Coordinates.Y || part.Coordinates[0].Y + 1 == gear.Coordinates.Y; }); // If there aren't at least two left, then it's definitely not valid if (partsClone.length < 2) { continue; } // Filter for parts that are on the same X-level, or 1 to the right or left of the gear partsClone = partsClone.filter((part) => { for(const coord of part.Coordinates) { if(gear.Coordinates.X == coord.X || gear.Coordinates.X + 1 == coord.X || gear.Coordinates.X - 1 == coord.X) { return true; } } return false; }); // If there are exactly two left, it's valid if (partsClone.length == 2 ) { gear.GearRatio = partsClone.reduce((accumulator, part) => accumulator *= part.PartNumber, 1); validGears.push(gear); } } return validGears; }