|
@@ -1,40 +1,5 @@
|
|
|
-import { Inspect, LoadInput } from "../common.ts";
|
|
|
-
|
|
|
-/* Directions */
|
|
|
-const Directions = {
|
|
|
- NORTH: {
|
|
|
- X: 0,
|
|
|
- Y: -1,
|
|
|
- },
|
|
|
- SOUTH: {
|
|
|
- X: 0,
|
|
|
- Y: 1,
|
|
|
- },
|
|
|
- EAST: {
|
|
|
- X: 1,
|
|
|
- Y: 0,
|
|
|
- },
|
|
|
- WEST: {
|
|
|
- X: -1,
|
|
|
- Y: 0,
|
|
|
- },
|
|
|
- NORTH_EAST: {
|
|
|
- X: 1,
|
|
|
- Y: -1,
|
|
|
- },
|
|
|
- NORTH_WEST: {
|
|
|
- X: -1,
|
|
|
- Y: -1,
|
|
|
- },
|
|
|
- SOUTH_EAST: {
|
|
|
- X: 1,
|
|
|
- Y: 1,
|
|
|
- },
|
|
|
- SOUTH_WEST: {
|
|
|
- X: -1,
|
|
|
- Y: 1,
|
|
|
- },
|
|
|
-}
|
|
|
+import { LoadInput } from "../common.ts";
|
|
|
+import { BuildSchematicsMap, ParseSchematics, ValidateEngineParts } from "./3_common.ts";
|
|
|
|
|
|
const tests = [
|
|
|
"467..114..",
|
|
@@ -81,186 +46,5 @@ engineParts = ValidateEngineParts(engineParts, schematics);
|
|
|
const sumOfPartNumbers = engineParts.reduce((accumulator, part) => accumulator += part.PartNumber, 0);
|
|
|
console.log(`The sum of part numbers is: ${sumOfPartNumbers}`);
|
|
|
|
|
|
-/**
|
|
|
- * 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;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if(part !== null) {
|
|
|
- engineParts.push(part);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return engineParts;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Validates all previously parsed Engine Parts
|
|
|
- *
|
|
|
- * An Engine Part is only valid if it's connected, even diagonally, to a
|
|
|
- * symbol other than `.`.
|
|
|
- *
|
|
|
- * @param {EnginePart[]} potentialParts All potential Engine Parts
|
|
|
- * @param {string[][]} schematicsMap A 2D grid of characters making up a schematic
|
|
|
- * @returns {EnginePart[]} A filtered list of all valid Engine Parts
|
|
|
- */
|
|
|
-function ValidateEngineParts(potentialParts: EnginePart[], schematicsMap: string[][]): EnginePart[] {
|
|
|
- const validParts: EnginePart[] = [];
|
|
|
-
|
|
|
- for (const part of potentialParts) {
|
|
|
- let symbolFound = false;
|
|
|
- for (const point of part.Coordinates) {
|
|
|
- if (BordersASymbol(point, schematicsMap)) {
|
|
|
- symbolFound = true;
|
|
|
- validParts.push(part);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if (!symbolFound) {
|
|
|
- console.log("No symbol found for the following part:");
|
|
|
- Inspect(part);
|
|
|
- }
|
|
|
- }
|
|
|
- return validParts;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Check if a point on the schematics map borders a symbol
|
|
|
- *
|
|
|
- * A symbol is any character other than a digit or a period.
|
|
|
- *
|
|
|
- * @param point A point on the schematics map
|
|
|
- * @param schematicsMap A 2D grid of characters making up a schematic
|
|
|
- * @returns {boolean} Whether the point borders a symbol or not
|
|
|
- */
|
|
|
-function BordersASymbol(point: Vector2, schematicsMap: string[][]): boolean {
|
|
|
- // Initialize all border chars as NOPs
|
|
|
- let northernBorder = ".";
|
|
|
- let easternBorder = ".";
|
|
|
- let southernBorder = ".";
|
|
|
- let westernBorder = ".";
|
|
|
- let northEastBorder = ".";
|
|
|
- let northWestBorder = ".";
|
|
|
- let southEastBorder = ".";
|
|
|
- let southWestBorder = ".";
|
|
|
- // Check what borders exist
|
|
|
- const northExists = !!schematicsMap[point.Y + Directions.NORTH.Y];
|
|
|
- const southExists = !!schematicsMap[point.Y + Directions.SOUTH.Y];
|
|
|
- const eastExists = !!schematicsMap[point.Y][point.X + Directions.EAST.X];
|
|
|
- const westExists = !!schematicsMap[point.Y][point.X + Directions.WEST.X];
|
|
|
- // Find the border chars
|
|
|
- if(northExists) {
|
|
|
- northernBorder = schematicsMap[point.Y + Directions.NORTH.Y][point.X + Directions.NORTH.X];
|
|
|
- if(eastExists) {
|
|
|
- northEastBorder = schematicsMap[point.Y + Directions.NORTH_EAST.Y][point.X + Directions.NORTH_EAST.X];
|
|
|
- }
|
|
|
- if(westExists) {
|
|
|
- northWestBorder = schematicsMap[point.Y + Directions.NORTH_WEST.Y][point.X + Directions.NORTH_WEST.X];
|
|
|
- }
|
|
|
- }
|
|
|
- if(southExists) {
|
|
|
- southernBorder = schematicsMap[point.Y + Directions.SOUTH.Y][point.X + Directions.SOUTH.X];
|
|
|
- if(eastExists) {
|
|
|
- southEastBorder = schematicsMap[point.Y + Directions.SOUTH_EAST.Y][point.X + Directions.SOUTH_EAST.X];
|
|
|
- }
|
|
|
- if(westExists) {
|
|
|
- southWestBorder = schematicsMap[point.Y + Directions.SOUTH_WEST.Y][point.X + Directions.SOUTH_WEST.X];
|
|
|
- }
|
|
|
- }
|
|
|
- if (eastExists) {
|
|
|
- easternBorder = schematicsMap[point.Y + Directions.EAST.Y][point.X + Directions.EAST.X];
|
|
|
- }
|
|
|
- if (westExists) {
|
|
|
- westernBorder = schematicsMap[point.Y + Directions.WEST.Y][point.X + Directions.WEST.X];
|
|
|
- }
|
|
|
- // Combine all bordering characters into one long string
|
|
|
- const allBorders = northernBorder + easternBorder + southernBorder + westernBorder + northEastBorder + northWestBorder + southEastBorder + southWestBorder;
|
|
|
-
|
|
|
- return /[^0-9\.]/.test(allBorders);
|
|
|
-}
|
|
|
|
|
|
-/**
|
|
|
- * 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,
|
|
|
-};
|