3_common.ts 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import { Inspect } from "../common.ts";
  2. /* Directions */
  3. const Directions = {
  4. NORTH: {
  5. X: 0,
  6. Y: -1,
  7. },
  8. SOUTH: {
  9. X: 0,
  10. Y: 1,
  11. },
  12. EAST: {
  13. X: 1,
  14. Y: 0,
  15. },
  16. WEST: {
  17. X: -1,
  18. Y: 0,
  19. },
  20. NORTH_EAST: {
  21. X: 1,
  22. Y: -1,
  23. },
  24. NORTH_WEST: {
  25. X: -1,
  26. Y: -1,
  27. },
  28. SOUTH_EAST: {
  29. X: 1,
  30. Y: 1,
  31. },
  32. SOUTH_WEST: {
  33. X: -1,
  34. Y: 1,
  35. },
  36. }
  37. /**
  38. * Parse the schematics to find all possible Engine Parts
  39. * @param {string[][]} schematicsMap A 2D grid of characters making up a schematic
  40. * @returns {EnginePart[]} An array of potential Engine Parts
  41. */
  42. export function ParseSchematics(schematicsMap: string[][]): EnginePart[] {
  43. const engineParts: EnginePart[] = [];
  44. /** The current pointer within the schematics' 2D array */
  45. const schematicsPointer: Vector2 = {
  46. X: 0,
  47. Y: 0,
  48. };
  49. for(; schematicsPointer.Y < schematicsMap.length; schematicsPointer.Y++) {
  50. // Reset the X pointer each line
  51. schematicsPointer.X = 0;
  52. /** A line from the total schematics */
  53. const schematicsLine = schematicsMap[schematicsPointer.Y];
  54. // Create a reusable EnginePart store
  55. let part: EnginePart|null = null;
  56. for(; schematicsPointer.X < schematicsMap[schematicsPointer.Y].length; schematicsPointer.X++) {
  57. const char = schematicsMap[schematicsPointer.Y][schematicsPointer.X];
  58. // Check if the current character is a number
  59. if (/\d/.test(char)) {
  60. // Check if our EnginePart store is initialized
  61. if (part == null) { part = { PartNumber: 0, Coordinates: [] }; }
  62. if (part.PartNumber) {
  63. // Concatenate the number to the existing part of the Part Number
  64. part.PartNumber = Number("" + part.PartNumber + char);
  65. }
  66. else {
  67. // Start the new Part Number
  68. part.PartNumber = Number(char);
  69. }
  70. // Push the coordinates
  71. part.Coordinates.push({
  72. X: schematicsPointer.X,
  73. Y: schematicsPointer.Y,
  74. });
  75. }
  76. // If it's not
  77. else {
  78. // Check if the EnginePart store has a value
  79. if(part != null) {
  80. // If it does, store it and empty it
  81. engineParts.push(part);
  82. part = null;
  83. }
  84. }
  85. }
  86. if(part !== null) {
  87. engineParts.push(part);
  88. }
  89. }
  90. return engineParts;
  91. }
  92. /**
  93. * Validates all previously parsed Engine Parts
  94. *
  95. * An Engine Part is only valid if it's connected, even diagonally, to a
  96. * symbol other than `.`.
  97. *
  98. * @param {EnginePart[]} potentialParts All potential Engine Parts
  99. * @param {string[][]} schematicsMap A 2D grid of characters making up a schematic
  100. * @returns {EnginePart[]} A filtered list of all valid Engine Parts
  101. */
  102. export function ValidateEngineParts(potentialParts: EnginePart[], schematicsMap: string[][]): EnginePart[] {
  103. const validParts: EnginePart[] = [];
  104. for (const part of potentialParts) {
  105. let symbolFound = false;
  106. for (const point of part.Coordinates) {
  107. if (BordersASymbol(point, schematicsMap)) {
  108. symbolFound = true;
  109. validParts.push(part);
  110. break;
  111. }
  112. }
  113. if (!symbolFound) {
  114. console.log("No symbol found for the following part:");
  115. Inspect(part);
  116. }
  117. }
  118. return validParts;
  119. }
  120. /**
  121. * Check if a point on the schematics map borders a symbol
  122. *
  123. * A symbol is any character other than a digit or a period.
  124. *
  125. * @param point A point on the schematics map
  126. * @param schematicsMap A 2D grid of characters making up a schematic
  127. * @returns {boolean} Whether the point borders a symbol or not
  128. */
  129. export function BordersASymbol(point: Vector2, schematicsMap: string[][]): boolean {
  130. // Initialize all border chars as NOPs
  131. let northernBorder = ".";
  132. let easternBorder = ".";
  133. let southernBorder = ".";
  134. let westernBorder = ".";
  135. let northEastBorder = ".";
  136. let northWestBorder = ".";
  137. let southEastBorder = ".";
  138. let southWestBorder = ".";
  139. // Check what borders exist
  140. const northExists = !!schematicsMap[point.Y + Directions.NORTH.Y];
  141. const southExists = !!schematicsMap[point.Y + Directions.SOUTH.Y];
  142. const eastExists = !!schematicsMap[point.Y][point.X + Directions.EAST.X];
  143. const westExists = !!schematicsMap[point.Y][point.X + Directions.WEST.X];
  144. // Find the border chars
  145. if(northExists) {
  146. northernBorder = schematicsMap[point.Y + Directions.NORTH.Y][point.X + Directions.NORTH.X];
  147. if(eastExists) {
  148. northEastBorder = schematicsMap[point.Y + Directions.NORTH_EAST.Y][point.X + Directions.NORTH_EAST.X];
  149. }
  150. if(westExists) {
  151. northWestBorder = schematicsMap[point.Y + Directions.NORTH_WEST.Y][point.X + Directions.NORTH_WEST.X];
  152. }
  153. }
  154. if(southExists) {
  155. southernBorder = schematicsMap[point.Y + Directions.SOUTH.Y][point.X + Directions.SOUTH.X];
  156. if(eastExists) {
  157. southEastBorder = schematicsMap[point.Y + Directions.SOUTH_EAST.Y][point.X + Directions.SOUTH_EAST.X];
  158. }
  159. if(westExists) {
  160. southWestBorder = schematicsMap[point.Y + Directions.SOUTH_WEST.Y][point.X + Directions.SOUTH_WEST.X];
  161. }
  162. }
  163. if (eastExists) {
  164. easternBorder = schematicsMap[point.Y + Directions.EAST.Y][point.X + Directions.EAST.X];
  165. }
  166. if (westExists) {
  167. westernBorder = schematicsMap[point.Y + Directions.WEST.Y][point.X + Directions.WEST.X];
  168. }
  169. // Combine all bordering characters into one long string
  170. const allBorders = northernBorder + easternBorder + southernBorder + westernBorder + northEastBorder + northWestBorder + southEastBorder + southWestBorder;
  171. return /[^0-9\.]/.test(allBorders);
  172. }
  173. /**
  174. * Split the schematics into a 2D grid of characters
  175. *
  176. * @param {string[]} schematics The array of lines from the schematics
  177. * @returns {string[][]} A 2D grid of characters
  178. */
  179. export function BuildSchematicsMap(schematics: string[]): string[][] {
  180. return schematics.map((schematicsLine) => schematicsLine.split(''));
  181. }
  182. /**
  183. * An engine part
  184. */
  185. export type EnginePart = {
  186. PartNumber: number,
  187. Coordinates: Vector2[],
  188. };
  189. /**
  190. * A symbol on the schematics map
  191. */
  192. export type SchematicsSymbol = {
  193. Symbol: string,
  194. Coordinates: Vector2,
  195. };
  196. /**
  197. * A position within a 2D grid
  198. */
  199. export type Vector2 = {
  200. X: number,
  201. Y: number,
  202. };