|
@@ -0,0 +1,130 @@
|
|
|
+import { LoadInput } from "../common.ts";
|
|
|
+import * as util from "util";
|
|
|
+
|
|
|
+const tests = [
|
|
|
+ "467..114..",
|
|
|
+ "...*......",
|
|
|
+ "..35..633.",
|
|
|
+ "......#...",
|
|
|
+ "617*......",
|
|
|
+ ".....+.58.",
|
|
|
+ "..592.....",
|
|
|
+ "......755.",
|
|
|
+ "...$.*....",
|
|
|
+ ".664.598..",
|
|
|
+];
|
|
|
+
|
|
|
+const testMap = BuildSchematicsMap(tests);
|
|
|
+let testParts = ParseSchematics(testMap);
|
|
|
+testParts = ValiditeEngineParts(testParts);
|
|
|
+
|
|
|
+console.log(util.inspect(testParts, { breakLength: Infinity, depth: 256, colors: true }));
|
|
|
+
|
|
|
+/**
|
|
|
+ * Parse the schematics to find all possible Engine Parts
|
|
|
+ * @param {string[][]} schematicsMap A 2D grid of characters making up a schematic
|
|
|
+ * @returns {EnginePart[]} An array of potential Engine Parts
|
|
|
+ */
|
|
|
+function ParseSchematics(schematicsMap: string[][]): EnginePart[] {
|
|
|
+ const engineParts: EnginePart[] = [];
|
|
|
+
|
|
|
+ /** The current pointer within the schematics' 2D array */
|
|
|
+ const schematicsPointer: Vector2 = {
|
|
|
+ X: 0,
|
|
|
+ Y: 0,
|
|
|
+ };
|
|
|
+
|
|
|
+ for(; schematicsPointer.Y < schematicsMap.length; schematicsPointer.Y++) {
|
|
|
+ // Reset the X pointer each line
|
|
|
+ schematicsPointer.X = 0;
|
|
|
+ /** A line from the total schematics */
|
|
|
+ const schematicsLine = schematicsMap[schematicsPointer.Y];
|
|
|
+
|
|
|
+ // Create a reusable EnginePart store
|
|
|
+ let part: EnginePart|null = null;
|
|
|
+
|
|
|
+ for(; schematicsPointer.X < schematicsMap[schematicsPointer.Y].length; schematicsPointer.X++) {
|
|
|
+ const char = schematicsMap[schematicsPointer.Y][schematicsPointer.X];
|
|
|
+
|
|
|
+ // Check if the current character is a number
|
|
|
+ if (/\d/.test(char)) {
|
|
|
+ // Check if our EnginePart store is initialized
|
|
|
+ if (part == null) { part = { PartNumber: 0, Coordinates: [] }; }
|
|
|
+
|
|
|
+ if (part.PartNumber) {
|
|
|
+ // Concatenate the number to the existing part of the Part Number
|
|
|
+ part.PartNumber = Number("" + part.PartNumber + char);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // Start the new Part Number
|
|
|
+ part.PartNumber = Number(char);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Push the coordinates
|
|
|
+ part.Coordinates.push({
|
|
|
+ X: schematicsPointer.X,
|
|
|
+ Y: schematicsPointer.Y,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ // If it's not
|
|
|
+ else {
|
|
|
+ // Check if the EnginePart store has a value
|
|
|
+ if(part != null) {
|
|
|
+ // If it does, store it and empty it
|
|
|
+ engineParts.push(part);
|
|
|
+ part = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return engineParts;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Validates all previously parsed Engine Parts
|
|
|
+ *
|
|
|
+ * An Engine Part is only valid if it's connected, even diagnally, to a
|
|
|
+ * symbol other than `.`.
|
|
|
+ *
|
|
|
+ * @param {EnginePart[]} potentialParts All potential Engine Parts
|
|
|
+ * @returns {EnginePart[]} A filtered list of all valid Engine Parts
|
|
|
+ */
|
|
|
+function ValiditeEngineParts(potentialParts: EnginePart[]): EnginePart[] {
|
|
|
+
|
|
|
+ return [];
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Split the schematics into a 2D grid of characters
|
|
|
+ *
|
|
|
+ * @param {string[]} schematics The array of lines from the schematics
|
|
|
+ * @returns {string[][]} A 2D grid of characters
|
|
|
+ */
|
|
|
+function BuildSchematicsMap(schematics: string[]): string[][] {
|
|
|
+ return schematics.map((schematicsLine) => schematicsLine.split(''));
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * An engine part
|
|
|
+ */
|
|
|
+type EnginePart = {
|
|
|
+ PartNumber: number,
|
|
|
+ Coordinates: Vector2[],
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * A symbol on the schematics map
|
|
|
+ */
|
|
|
+type SchematicsSymbol = {
|
|
|
+ Symbol: string,
|
|
|
+ Coordinates: Vector2,
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * A position within a 2D grid
|
|
|
+ */
|
|
|
+type Vector2 = {
|
|
|
+ X: number,
|
|
|
+ Y: number,
|
|
|
+};
|