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. }
  114. return validParts;
  115. }
  116. /**
  117. * Check if a point on the schematics map borders a symbol
  118. *
  119. * A symbol is any character other than a digit or a period.
  120. *
  121. * @param point A point on the schematics map
  122. * @param schematicsMap A 2D grid of characters making up a schematic
  123. * @returns {boolean} Whether the point borders a symbol or not
  124. */
  125. export function BordersASymbol(point: Vector2, schematicsMap: string[][]): boolean {
  126. // Initialize all border chars as NOPs
  127. let northernBorder = ".";
  128. let easternBorder = ".";
  129. let southernBorder = ".";
  130. let westernBorder = ".";
  131. let northEastBorder = ".";
  132. let northWestBorder = ".";
  133. let southEastBorder = ".";
  134. let southWestBorder = ".";
  135. // Check what borders exist
  136. const northExists = !!schematicsMap[point.Y + Directions.NORTH.Y];
  137. const southExists = !!schematicsMap[point.Y + Directions.SOUTH.Y];
  138. const eastExists = !!schematicsMap[point.Y][point.X + Directions.EAST.X];
  139. const westExists = !!schematicsMap[point.Y][point.X + Directions.WEST.X];
  140. // Find the border chars
  141. if(northExists) {
  142. northernBorder = schematicsMap[point.Y + Directions.NORTH.Y][point.X + Directions.NORTH.X];
  143. if(eastExists) {
  144. northEastBorder = schematicsMap[point.Y + Directions.NORTH_EAST.Y][point.X + Directions.NORTH_EAST.X];
  145. }
  146. if(westExists) {
  147. northWestBorder = schematicsMap[point.Y + Directions.NORTH_WEST.Y][point.X + Directions.NORTH_WEST.X];
  148. }
  149. }
  150. if(southExists) {
  151. southernBorder = schematicsMap[point.Y + Directions.SOUTH.Y][point.X + Directions.SOUTH.X];
  152. if(eastExists) {
  153. southEastBorder = schematicsMap[point.Y + Directions.SOUTH_EAST.Y][point.X + Directions.SOUTH_EAST.X];
  154. }
  155. if(westExists) {
  156. southWestBorder = schematicsMap[point.Y + Directions.SOUTH_WEST.Y][point.X + Directions.SOUTH_WEST.X];
  157. }
  158. }
  159. if (eastExists) {
  160. easternBorder = schematicsMap[point.Y + Directions.EAST.Y][point.X + Directions.EAST.X];
  161. }
  162. if (westExists) {
  163. westernBorder = schematicsMap[point.Y + Directions.WEST.Y][point.X + Directions.WEST.X];
  164. }
  165. // Combine all bordering characters into one long string
  166. const allBorders = northernBorder + easternBorder + southernBorder + westernBorder + northEastBorder + northWestBorder + southEastBorder + southWestBorder;
  167. return /[^0-9\.]/.test(allBorders);
  168. }
  169. /**
  170. * Split the schematics into a 2D grid of characters
  171. *
  172. * @param {string[]} schematics The array of lines from the schematics
  173. * @returns {string[][]} A 2D grid of characters
  174. */
  175. export function BuildSchematicsMap(schematics: string[]): string[][] {
  176. return schematics.map((schematicsLine) => schematicsLine.split(''));
  177. }
  178. /**
  179. * An engine part
  180. */
  181. export type EnginePart = {
  182. PartNumber: number,
  183. Coordinates: Vector2[],
  184. };
  185. /**
  186. * A gear on the schematics map
  187. *
  188. * A gear is any `*` symbol from the schematics map that borders
  189. * exactly two engine part numbers.
  190. */
  191. export type SchematicsGear = {
  192. Symbol: string,
  193. Coordinates: Vector2,
  194. GearRatio: number
  195. };
  196. /**
  197. * A position within a 2D grid
  198. */
  199. export type Vector2 = {
  200. X: number,
  201. Y: number,
  202. };