5_common.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import { ExtractNumbers } from "../common.ts";
  2. export function ParseInput(almanac: string[]): RangeMaps {
  3. /** An empty initializer for our output */
  4. const output: RangeMaps = {
  5. Seeds: [],
  6. SeedToSoil: [],
  7. SoilToFertilizer: [],
  8. FertilizerToWater: [],
  9. WaterToLight: [],
  10. LightToTemperature: [],
  11. TemperatureToHumidity: [],
  12. HumidityToLocation: [],
  13. };
  14. for (let i = 0; i < almanac.length; i++) {
  15. let line = almanac[i];
  16. // Parse the seed ID's
  17. if (/^seeds:/.test(line)) {
  18. // Extract all numbers from the line
  19. const seedIDs = ExtractNumbers(line);
  20. // Add to the output's Seeds array a new Seed for each number
  21. seedIDs.forEach((id) => { output.Seeds.push(id); });
  22. // Skip the next blank
  23. i++;
  24. }
  25. // Parse the Seed to Soil ranges
  26. else if (/^seed\-to/.test(line)) {
  27. output.SeedToSoil = ParseSourceDestinationRange(++i, almanac);
  28. }
  29. // Parse the Soil to Fertilizer ranges
  30. else if (/^soil\-to/.test(line)) {
  31. output.SoilToFertilizer = ParseSourceDestinationRange(++i, almanac);
  32. }
  33. // Parse the Fertilizer to Water ranges
  34. else if (/^fertilizer\-to/.test(line)) {
  35. output.FertilizerToWater = ParseSourceDestinationRange(++i, almanac);
  36. }
  37. // Parse the Water to Light ranges
  38. else if (/^water\-to/.test(line)) {
  39. output.WaterToLight = ParseSourceDestinationRange(++i, almanac);
  40. }
  41. // Parse the Light to Temperature ranges
  42. else if (/^light\-to/.test(line)) {
  43. output.LightToTemperature = ParseSourceDestinationRange(++i, almanac);
  44. }
  45. // Parse the Temperature to Humidity ranges
  46. else if (/^temperature\-to/.test(line)) {
  47. output.TemperatureToHumidity = ParseSourceDestinationRange(++i, almanac);
  48. }
  49. // Parse the Humidity to Location ranges
  50. else if (/^humidity\-to/.test(line)) {
  51. output.HumidityToLocation = ParseSourceDestinationRange(++i, almanac);
  52. }
  53. }
  54. return output;
  55. }
  56. /**
  57. * Helper function to parse the ranges for each mappable section
  58. *
  59. * @param {number} lineNumber The line number to begin parsing from
  60. * @param {string[]} almanac The complete almanac
  61. * @returns {RangeMap[]} A list of mapped indices
  62. */
  63. export function ParseSourceDestinationRange(lineNumber: number, almanac: string[]): RangeMap[] {
  64. const rangeMap: RangeMap[] = [];
  65. do {
  66. // Get the values out of the line
  67. const [destinationRangeStart, sourceRangeStart, rangeLength] = ExtractNumbers(almanac[lineNumber]);
  68. rangeMap.push({
  69. SourceRanges: {
  70. Start: sourceRangeStart,
  71. End: sourceRangeStart + rangeLength - 1,
  72. },
  73. DestinationRange: {
  74. Start: destinationRangeStart,
  75. End: destinationRangeStart + rangeLength - 1,
  76. }
  77. });
  78. } while(almanac[++lineNumber]);
  79. return rangeMap;
  80. }
  81. /**
  82. * Maps one object to another based on ID ranges
  83. *
  84. * If the ID given doesn't fit in any of the ranges given, then the
  85. * input is returned
  86. *
  87. * @param {number} needle The ID number to find
  88. * @param {RangeMap[]} haystack The list of ranges to find the ID in
  89. * @returns {number} The ID of the mapped object
  90. */
  91. export function MapObjectByID(needle: number, haystack: RangeMap[]): number {
  92. for (const range of haystack) {
  93. if (range.SourceRanges.Start <= needle && range.SourceRanges.End >= needle) {
  94. return range.DestinationRange.Start + (needle - range.SourceRanges.Start);
  95. }
  96. }
  97. return needle;
  98. }
  99. /**
  100. * Map a Seed ID to all of its other attributes
  101. *
  102. * Given a seed ID, finds its soil type, fertilizer type, water type,
  103. * light type, temperature type, humidity type, and location ID.
  104. *
  105. * @param {number} seedID
  106. * @param {RangeMaps} rangeMaps A parsed map of ranges
  107. * @returns
  108. */
  109. export function MapSeed(seedID: number, rangeMaps: RangeMaps): SeedMap|undefined {
  110. // Initialize our seed map object
  111. const seed: SeedMap = {
  112. ID: seedID,
  113. SoilID: 0,
  114. FertilizerID: 0,
  115. WaterID: 0,
  116. LightID: 0,
  117. TemperatureID: 0,
  118. HumidityID: 0,
  119. LocationID: 0,
  120. };
  121. seed.SoilID = MapObjectByID(seedID, rangeMaps.SeedToSoil);
  122. seed.FertilizerID = MapObjectByID(seed.SoilID, rangeMaps.SoilToFertilizer);
  123. seed.WaterID = MapObjectByID(seed.FertilizerID, rangeMaps.FertilizerToWater);
  124. seed.LightID = MapObjectByID(seed.WaterID, rangeMaps.WaterToLight);
  125. seed.TemperatureID = MapObjectByID(seed.LightID, rangeMaps.LightToTemperature);
  126. seed.HumidityID = MapObjectByID(seed.TemperatureID, rangeMaps.TemperatureToHumidity);
  127. seed.LocationID = MapObjectByID(seed.HumidityID, rangeMaps.HumidityToLocation);
  128. return seed;
  129. }
  130. /**
  131. * Type guard function to ensure an array is an array of SeedMaps
  132. *
  133. * @param {any[]} valueArray The array to check
  134. * @returns {boolean} Whether the input is an array of SeedMaps or not
  135. */
  136. export function isSeedMapArray(valueArray: any[]): valueArray is SeedMap[] {
  137. const value = valueArray.shift();
  138. if (!value || typeof value !== "object") { return false; }
  139. return Object.hasOwn(value, "ID")
  140. && Object.hasOwn(value, "SoilID")
  141. && Object.hasOwn(value, "FertilizerID")
  142. && Object.hasOwn(value, "WaterID")
  143. && Object.hasOwn(value, "LightID")
  144. && Object.hasOwn(value, "TemperatureID")
  145. && Object.hasOwn(value, "HumidityID")
  146. && Object.hasOwn(value, "LocationID");
  147. }
  148. export type IDRange = {
  149. Start: number,
  150. End: number,
  151. };
  152. /** An IdentifiableObject that points to another IdentifiableObject */
  153. export type RangeMap = {
  154. /** The ID that this object points to */
  155. SourceRanges: IDRange,
  156. DestinationRange: IDRange,
  157. };
  158. /** A complete map of all object IDs and where they point to */
  159. export type RangeMaps = {
  160. /** The list of seed IDs */
  161. Seeds: number[],
  162. /** The map of seed to soil IDs */
  163. SeedToSoil: RangeMap[],
  164. /** The map of soil to fertilizer IDs */
  165. SoilToFertilizer: RangeMap[],
  166. /** The map of fertilizer to water IDs */
  167. FertilizerToWater: RangeMap[],
  168. /** The map of water to light IDs */
  169. WaterToLight: RangeMap[],
  170. /** The map of light to temperature IDs */
  171. LightToTemperature: RangeMap[],
  172. /** The map of temperature to humidity IDs */
  173. TemperatureToHumidity: RangeMap[],
  174. /** The map of humidity to location IDs */
  175. HumidityToLocation: RangeMap[],
  176. }
  177. /** A completed map of a seed's property IDs */
  178. export type SeedMap = {
  179. /** The ID of the seed */
  180. ID: number,
  181. /** The ID of the soil type the seed needs planted in */
  182. SoilID: number,
  183. /** The ID of the fertilizer type the soil needs */
  184. FertilizerID: number,
  185. /** The ID of the water type the fertilizer needs */
  186. WaterID: number,
  187. /** The ID of the light type the water needs */
  188. LightID: number,
  189. /** The ID of the temperature type the light needs */
  190. TemperatureID: number,
  191. /** The ID of the humidity type the temperature needs */
  192. HumidityID: number,
  193. /** The ID of the location the humidity needs */
  194. LocationID: number,
  195. }