|
@@ -1,4 +1,5 @@
|
|
|
-import { ExtractNumbers, Inspect, LoadInput } from "../common.ts";
|
|
|
+import { LoadInput } from "../common.ts";
|
|
|
+import { ParseInput, MapSeed, isSeedMapArray } from "./5_common.ts"
|
|
|
|
|
|
const tests = [
|
|
|
"seeds: 79 14 55 13",
|
|
@@ -53,218 +54,3 @@ if (isSeedMapArray(seeds)) {
|
|
|
}
|
|
|
|
|
|
console.log(`The lowest location ID found is: ${closestLocationID}`);
|
|
|
-
|
|
|
-function ParseInput(almanac: string[]): RangeMaps {
|
|
|
- /** An empty initializer for our output */
|
|
|
- const output: RangeMaps = {
|
|
|
- Seeds: [],
|
|
|
- SeedToSoil: [],
|
|
|
- SoilToFertilizer: [],
|
|
|
- FertilizerToWater: [],
|
|
|
- WaterToLight: [],
|
|
|
- LightToTemperature: [],
|
|
|
- TemperatureToHumidity: [],
|
|
|
- HumidityToLocation: [],
|
|
|
- };
|
|
|
-
|
|
|
- for (let i = 0; i < almanac.length; i++) {
|
|
|
- let line = almanac[i];
|
|
|
-
|
|
|
- // Parse the seed ID's
|
|
|
- if (/^seeds:/.test(line)) {
|
|
|
- // Extract all numbers from the line
|
|
|
- const seedIDs = ExtractNumbers(line);
|
|
|
- // Add to the output's Seeds array a new Seed for each number
|
|
|
- seedIDs.forEach((id) => { output.Seeds.push(id); });
|
|
|
- // Skip the next blank
|
|
|
- i++;
|
|
|
- }
|
|
|
- // Parse the Seed to Soil ranges
|
|
|
- else if (/^seed\-to/.test(line)) {
|
|
|
- output.SeedToSoil = ParseSourceDestinationRange(++i, almanac);
|
|
|
- }
|
|
|
- // Parse the Soil to Fertilizer ranges
|
|
|
- else if (/^soil\-to/.test(line)) {
|
|
|
- output.SoilToFertilizer = ParseSourceDestinationRange(++i, almanac);
|
|
|
- }
|
|
|
- // Parse the Fertilizer to Water ranges
|
|
|
- else if (/^fertilizer\-to/.test(line)) {
|
|
|
- output.FertilizerToWater = ParseSourceDestinationRange(++i, almanac);
|
|
|
- }
|
|
|
- // Parse the Water to Light ranges
|
|
|
- else if (/^water\-to/.test(line)) {
|
|
|
- output.WaterToLight = ParseSourceDestinationRange(++i, almanac);
|
|
|
- }
|
|
|
- // Parse the Light to Temperature ranges
|
|
|
- else if (/^light\-to/.test(line)) {
|
|
|
- output.LightToTemperature = ParseSourceDestinationRange(++i, almanac);
|
|
|
- }
|
|
|
- // Parse the Temperature to Humidity ranges
|
|
|
- else if (/^temperature\-to/.test(line)) {
|
|
|
- output.TemperatureToHumidity = ParseSourceDestinationRange(++i, almanac);
|
|
|
- }
|
|
|
- // Parse the Humidity to Location ranges
|
|
|
- else if (/^humidity\-to/.test(line)) {
|
|
|
- output.HumidityToLocation = ParseSourceDestinationRange(++i, almanac);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return output;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Helper function to parse the ranges for each mappable section
|
|
|
- *
|
|
|
- * @param {number} lineNumber The line number to begin parsing from
|
|
|
- * @param {string[]} almanac The complete almanac
|
|
|
- * @returns {RangeMap[]} A list of mapped indices
|
|
|
- */
|
|
|
-function ParseSourceDestinationRange(lineNumber: number, almanac: string[]): RangeMap[] {
|
|
|
- const rangeMap: RangeMap[] = [];
|
|
|
- do {
|
|
|
- // Get the values out of the line
|
|
|
- const [destinationRangeStart, sourceRangeStart, rangeLength] = ExtractNumbers(almanac[lineNumber]);
|
|
|
-
|
|
|
- rangeMap.push({
|
|
|
- SourceRanges: {
|
|
|
- Start: sourceRangeStart,
|
|
|
- End: sourceRangeStart + rangeLength - 1,
|
|
|
- },
|
|
|
- DestinationRange: {
|
|
|
- Start: destinationRangeStart,
|
|
|
- End: destinationRangeStart + rangeLength - 1,
|
|
|
- }
|
|
|
- });
|
|
|
- } while(almanac[++lineNumber]);
|
|
|
-
|
|
|
- return rangeMap;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Maps one object to another based on ID ranges
|
|
|
- *
|
|
|
- * If the ID given doesn't fit in any of the ranges given, then the
|
|
|
- * input is returned
|
|
|
- *
|
|
|
- * @param {number} needle The ID number to find
|
|
|
- * @param {RangeMap[]} haystack The list of ranges to find the ID in
|
|
|
- * @returns {number} The ID of the mapped object
|
|
|
- */
|
|
|
-function MapObjectByID(needle: number, haystack: RangeMap[]): number {
|
|
|
- for (const range of haystack) {
|
|
|
- if (range.SourceRanges.Start <= needle && range.SourceRanges.End >= needle) {
|
|
|
- return range.DestinationRange.Start + (needle - range.SourceRanges.Start);
|
|
|
- }
|
|
|
- }
|
|
|
- return needle;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Map a Seed ID to all of its other attributes
|
|
|
- *
|
|
|
- * Given a seed ID, finds its soil type, fertilizer type, water type,
|
|
|
- * light type, temperature type, humidity type, and location ID.
|
|
|
- *
|
|
|
- * @param {number} seedID
|
|
|
- * @param {RangeMaps} rangeMaps A parsed map of ranges
|
|
|
- * @returns
|
|
|
- */
|
|
|
-function MapSeed(seedID: number, rangeMaps: RangeMaps): SeedMap|undefined {
|
|
|
- // Make sure the seed with that ID exists before continueing
|
|
|
- if(!rangeMaps.Seeds.includes(seedID)) { return undefined; }
|
|
|
-
|
|
|
- // Initialize our seed map object
|
|
|
- const seed: SeedMap = {
|
|
|
- ID: seedID,
|
|
|
- SoilID: 0,
|
|
|
- FertilizerID: 0,
|
|
|
- WaterID: 0,
|
|
|
- LightID: 0,
|
|
|
- TemperatureID: 0,
|
|
|
- HumidityID: 0,
|
|
|
- LocationID: 0,
|
|
|
- };
|
|
|
-
|
|
|
- seed.SoilID = MapObjectByID(seedID, rangeMaps.SeedToSoil);
|
|
|
- seed.FertilizerID = MapObjectByID(seed.SoilID, rangeMaps.SoilToFertilizer);
|
|
|
- seed.WaterID = MapObjectByID(seed.FertilizerID, rangeMaps.FertilizerToWater);
|
|
|
- seed.LightID = MapObjectByID(seed.WaterID, rangeMaps.WaterToLight);
|
|
|
- seed.TemperatureID = MapObjectByID(seed.LightID, rangeMaps.LightToTemperature);
|
|
|
- seed.HumidityID = MapObjectByID(seed.TemperatureID, rangeMaps.TemperatureToHumidity);
|
|
|
- seed.LocationID = MapObjectByID(seed.HumidityID, rangeMaps.HumidityToLocation);
|
|
|
-
|
|
|
- return seed;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Type guard function to ensure an array is an array of SeedMaps
|
|
|
- *
|
|
|
- * @param {any[]} valueArray The array to check
|
|
|
- * @returns {boolean} Whether the input is an array of SeedMaps or not
|
|
|
- */
|
|
|
-function isSeedMapArray(valueArray: any[]): valueArray is SeedMap[] {
|
|
|
- const value = valueArray.shift();
|
|
|
-
|
|
|
- if (!value || typeof value !== "object") { return false; }
|
|
|
-
|
|
|
- return Object.hasOwn(value, "ID")
|
|
|
- && Object.hasOwn(value, "SoilID")
|
|
|
- && Object.hasOwn(value, "FertilizerID")
|
|
|
- && Object.hasOwn(value, "WaterID")
|
|
|
- && Object.hasOwn(value, "LightID")
|
|
|
- && Object.hasOwn(value, "TemperatureID")
|
|
|
- && Object.hasOwn(value, "HumidityID")
|
|
|
- && Object.hasOwn(value, "LocationID");
|
|
|
-}
|
|
|
-
|
|
|
-type IDRange = {
|
|
|
- Start: number,
|
|
|
- End: number,
|
|
|
-};
|
|
|
-
|
|
|
-/** An IdentifiableObject that points to another IdentifiableObject */
|
|
|
-type RangeMap = {
|
|
|
- /** The ID that this object points to */
|
|
|
- SourceRanges: IDRange,
|
|
|
- DestinationRange: IDRange,
|
|
|
-};
|
|
|
-
|
|
|
-/** A complete map of all object IDs and where they point to */
|
|
|
-type RangeMaps = {
|
|
|
- /** The list of seed IDs */
|
|
|
- Seeds: number[],
|
|
|
- /** The map of seed to soil IDs */
|
|
|
- SeedToSoil: RangeMap[],
|
|
|
- /** The map of soil to fertilizer IDs */
|
|
|
- SoilToFertilizer: RangeMap[],
|
|
|
- /** The map of fertilizer to water IDs */
|
|
|
- FertilizerToWater: RangeMap[],
|
|
|
- /** The map of water to light IDs */
|
|
|
- WaterToLight: RangeMap[],
|
|
|
- /** The map of light to temperature IDs */
|
|
|
- LightToTemperature: RangeMap[],
|
|
|
- /** The map of temperature to humidity IDs */
|
|
|
- TemperatureToHumidity: RangeMap[],
|
|
|
- /** The map of humidity to location IDs */
|
|
|
- HumidityToLocation: RangeMap[],
|
|
|
-}
|
|
|
-
|
|
|
-/** A completed map of a seed's property IDs */
|
|
|
-type SeedMap = {
|
|
|
- /** The ID of the seed */
|
|
|
- ID: number,
|
|
|
- /** The ID of the soil type the seed needs planted in */
|
|
|
- SoilID: number,
|
|
|
- /** The ID of the fertilizer type the soil needs */
|
|
|
- FertilizerID: number,
|
|
|
- /** The ID of the water type the fertilizer needs */
|
|
|
- WaterID: number,
|
|
|
- /** The ID of the light type the water needs */
|
|
|
- LightID: number,
|
|
|
- /** The ID of the temperature type the light needs */
|
|
|
- TemperatureID: number,
|
|
|
- /** The ID of the humidity type the temperature needs */
|
|
|
- HumidityID: number,
|
|
|
- /** The ID of the location the humidity needs */
|
|
|
- LocationID: number,
|
|
|
-}
|