failed_5_1.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. import { ExtractNumbers, Inspect, LoadInput } from "../common.ts";
  2. /**
  3. * While this script does work for the test case, the challenge input
  4. * is too large, and causes the JS engine to run out of memory.
  5. */
  6. const tests = [
  7. "seeds: 79 14 55 13",
  8. "",
  9. "seed-to-soil map:",
  10. "50 98 2",
  11. "52 50 48",
  12. "",
  13. "soil-to-fertilizer map:",
  14. "0 15 37",
  15. "37 52 2",
  16. "39 0 15",
  17. "",
  18. "fertilizer-to-water map:",
  19. "49 53 8",
  20. "0 11 42",
  21. "42 0 7",
  22. "57 7 4",
  23. "",
  24. "water-to-light map:",
  25. "88 18 7",
  26. "18 25 70",
  27. "",
  28. "light-to-temperature map:",
  29. "45 77 23",
  30. "81 45 19",
  31. "68 64 13",
  32. "",
  33. "temperature-to-humidity map:",
  34. "0 69 1",
  35. "1 0 69",
  36. "",
  37. "humidity-to-location map:",
  38. "60 56 37",
  39. "56 93 4",
  40. ];
  41. const input = await LoadInput(5);
  42. // Parse the input
  43. const maps = ParseInput(input);
  44. // Map the seeds
  45. const seeds = maps.Seeds.map((seed) => MapSeed(seed.ID, maps));
  46. // Get the output
  47. let closestLocationID = Number.MAX_SAFE_INTEGER;
  48. if (isSeedMapArray(seeds)) {
  49. seeds.forEach((seed) => {
  50. if (seed.LocationID < closestLocationID) { closestLocationID = seed.LocationID; }
  51. });
  52. }
  53. console.log(`The lowest location ID found is: ${closestLocationID}`);
  54. function ParseInput(almanac: string[]): RangeMaps {
  55. /** An empty initializer for our output */
  56. const output: RangeMaps = {
  57. Seeds: [],
  58. SeedToSoil: [],
  59. SoilToFertilizer: [],
  60. FertilizerToWater: [],
  61. WaterToLight: [],
  62. LightToTemperature: [],
  63. TemperatureToHumidity: [],
  64. HumidityToLocation: [],
  65. };
  66. for (let i = 0; i < almanac.length; i++) {
  67. let line = almanac[i];
  68. // Parse the seed ID's
  69. if (/^seeds:/.test(line)) {
  70. // Extract all numbers from the line
  71. const seedIDs = ExtractNumbers(line);
  72. // Add to the output's Seeds array a new Seed for each number
  73. seedIDs.forEach((id) => { output.Seeds.push({ID: id}); });
  74. // Skip the next blank
  75. i++;
  76. }
  77. // Parse the Seed to Soil ranges
  78. else if (/^seed\-to/.test(line)) {
  79. output.SeedToSoil = ParseSourceDestinationRange(++i, almanac);
  80. }
  81. // Parse the Soil to Fertilizer ranges
  82. else if (/^soil\-to/.test(line)) {
  83. output.SoilToFertilizer = ParseSourceDestinationRange(++i, almanac);
  84. }
  85. // Parse the Fertilizer to Water ranges
  86. else if (/^fertilizer\-to/.test(line)) {
  87. output.FertilizerToWater = ParseSourceDestinationRange(++i, almanac);
  88. }
  89. // Parse the Water to Light ranges
  90. else if (/^water\-to/.test(line)) {
  91. output.WaterToLight = ParseSourceDestinationRange(++i, almanac);
  92. }
  93. // Parse the Light to Temperature ranges
  94. else if (/^light\-to/.test(line)) {
  95. output.LightToTemperature = ParseSourceDestinationRange(++i, almanac);
  96. }
  97. // Parse the Temperature to Humidity ranges
  98. else if (/^temperature\-to/.test(line)) {
  99. output.TemperatureToHumidity = ParseSourceDestinationRange(++i, almanac);
  100. }
  101. // Parse the Humidity to Location ranges
  102. else if (/^humidity\-to/.test(line)) {
  103. output.HumidityToLocation = ParseSourceDestinationRange(++i, almanac);
  104. }
  105. }
  106. return output;
  107. }
  108. /**
  109. * Helper function to parse the ranges for each mappable section
  110. *
  111. * @param {number} lineNumber The line number to begin parsing from
  112. * @param {string[]} almanac The complete almanac
  113. * @returns {MappableObject[]} A list of mapped indices
  114. */
  115. function ParseSourceDestinationRange(lineNumber: number, almanac: string[]): MappableObject[] {
  116. const rangeMap: MappableObject[] = [];
  117. do {
  118. // Get the values out of the line
  119. const [destinationRangeStart, sourceRangeStart, rangeLength] = ExtractNumbers(almanac[lineNumber]);
  120. // Create the ranges
  121. for (let idx = 0; idx < rangeLength; idx++) {
  122. rangeMap.push({
  123. ID: sourceRangeStart + idx,
  124. PointsTo: destinationRangeStart + idx,
  125. });
  126. }
  127. } while(almanac[++lineNumber]);
  128. return rangeMap;
  129. }
  130. /**
  131. * Find an IdentifiableObject by its ID number
  132. *
  133. * @param needle The ID number to find
  134. * @param haystack The list to find the ID in
  135. * @returns {IdentifiableObject|undefined} The object by that ID if found, or undefined
  136. */
  137. function FindObjectByID(needle: number, haystack: IdentifiableObject[]|MappableObject[]): IdentifiableObject|MappableObject|undefined {
  138. return haystack.find((obj) => obj.ID == needle);
  139. }
  140. /**
  141. * Map a Seed ID to all of its other attributes
  142. *
  143. * Given a seed ID, finds its soil type, fertilizer type, water type,
  144. * light type, temperature type, humidity type, and location ID.
  145. *
  146. * @param {number} seedID
  147. * @param {RangeMaps} rangeMaps A parsed map of ranges
  148. * @returns
  149. */
  150. function MapSeed(seedID: number, rangeMaps: RangeMaps): SeedMap|undefined {
  151. // Make sure the seed with that ID exists before continueing
  152. if(!FindObjectByID(seedID, rangeMaps.Seeds)) { return undefined; }
  153. // Initialize our seed map object
  154. const seed: SeedMap = {
  155. ID: seedID,
  156. SoilID: 0,
  157. FertilizerID: 0,
  158. WaterID: 0,
  159. LightID: 0,
  160. TemperatureID: 0,
  161. HumidityID: 0,
  162. LocationID: 0,
  163. };
  164. seed.SoilID = (FindObjectByID(seedID, rangeMaps.SeedToSoil) as MappableObject)?.PointsTo || seedID;
  165. seed.FertilizerID = (FindObjectByID(seed.SoilID, rangeMaps.SoilToFertilizer) as MappableObject)?.PointsTo || seed.SoilID;
  166. seed.WaterID = (FindObjectByID(seed.FertilizerID, rangeMaps.FertilizerToWater) as MappableObject)?.PointsTo || seed.FertilizerID;
  167. seed.LightID = (FindObjectByID(seed.WaterID, rangeMaps.WaterToLight) as MappableObject)?.PointsTo || seed.WaterID;
  168. seed.TemperatureID = (FindObjectByID(seed.LightID, rangeMaps.LightToTemperature) as MappableObject)?.PointsTo|| seed.LightID;
  169. seed.HumidityID = (FindObjectByID(seed.TemperatureID, rangeMaps.TemperatureToHumidity) as MappableObject)?.PointsTo || seed.TemperatureID;
  170. seed.LocationID = (FindObjectByID(seed.HumidityID, rangeMaps.HumidityToLocation) as MappableObject)?.PointsTo || seed.HumidityID;
  171. return seed;
  172. }
  173. /**
  174. * Type guard function to ensure an array is an array of SeedMaps
  175. *
  176. * @param {any[]} valueArray The array to check
  177. * @returns {boolean} Whether the input is an array of SeedMaps or not
  178. */
  179. function isSeedMapArray(valueArray: any[]): valueArray is SeedMap[] {
  180. const value = valueArray.shift();
  181. if (!value || typeof value !== "object") { return false; }
  182. return Object.hasOwn(value, "ID")
  183. && Object.hasOwn(value, "SoilID")
  184. && Object.hasOwn(value, "FertilizerID")
  185. && Object.hasOwn(value, "WaterID")
  186. && Object.hasOwn(value, "LightID")
  187. && Object.hasOwn(value, "TemperatureID")
  188. && Object.hasOwn(value, "HumidityID")
  189. && Object.hasOwn(value, "LocationID");
  190. }
  191. /** Any object with a unique identifier */
  192. interface IdentifiableObject {
  193. /** The ID number for this object */
  194. ID: number,
  195. };
  196. /** A seed */
  197. interface Seed extends IdentifiableObject {};
  198. /** An IdentifiableObject that points to another IdentifiableObject */
  199. interface MappableObject extends IdentifiableObject {
  200. /** The ID that this object points to */
  201. PointsTo: number,
  202. };
  203. /** A complete map of all object IDs and where they point to */
  204. type RangeMaps = {
  205. /** The list of seed IDs */
  206. Seeds: Seed[],
  207. /** The map of seed to soil IDs */
  208. SeedToSoil: MappableObject[],
  209. /** The map of soil to fertilizer IDs */
  210. SoilToFertilizer: MappableObject[],
  211. /** The map of fertilizer to water IDs */
  212. FertilizerToWater: MappableObject[],
  213. /** The map of water to light IDs */
  214. WaterToLight: MappableObject[],
  215. /** The map of light to temperature IDs */
  216. LightToTemperature: MappableObject[],
  217. /** The map of temperature to humidity IDs */
  218. TemperatureToHumidity: MappableObject[],
  219. /** The map of humidity to location IDs */
  220. HumidityToLocation: MappableObject[],
  221. }
  222. /** A completed map of a seed's property IDs */
  223. type SeedMap = {
  224. /** The ID of the seed */
  225. ID: number,
  226. /** The ID of the soil type the seed needs planted in */
  227. SoilID: number,
  228. /** The ID of the fertilizer type the soil needs */
  229. FertilizerID: number,
  230. /** The ID of the water type the fertilizer needs */
  231. WaterID: number,
  232. /** The ID of the light type the water needs */
  233. LightID: number,
  234. /** The ID of the temperature type the light needs */
  235. TemperatureID: number,
  236. /** The ID of the humidity type the temperature needs */
  237. HumidityID: number,
  238. /** The ID of the location the humidity needs */
  239. LocationID: number,
  240. }