Add day 6 (Part 1 complete, Part 2 theoretically complete but needs optimization to actually finish)

Change-Id: I2ff05098d5f0dede9d4b244c7b9e70dc0dc670b9
Reviewed-on: https://git.clicks.codes/c/Coded/AoC2023/+/167
diff --git a/day 4/index.ts b/day 4/index.ts
index 8643cc2..f12ba25 100644
--- a/day 4/index.ts
+++ b/day 4/index.ts
@@ -1,5 +1,6 @@
 import { readFileSync } from "fs";
 
+const time = Date.now();
 const input = readFileSync('day 4/input.txt').toString().split('\n');
 
 interface Card {
@@ -75,4 +76,7 @@
     console.log(`(Part 2) Total Scratchcards: ${newCards.reduce((acc, curr) => {
         return acc + curr.copies;
       }, 0)}`)
-})();
\ No newline at end of file
+})();
+
+
+console.log(`Total Time (In millis): ${Date.now() - time}`)
diff --git a/day 5/index.ts b/day 5/index.ts
new file mode 100644
index 0000000..8b8d063
--- /dev/null
+++ b/day 5/index.ts
@@ -0,0 +1,166 @@
+import { readFileSync } from "fs";
+
+const input = readFileSync('day 5/input.txt').toString().split('\n\n')
+
+interface MappedRange {
+    startRange: [number, number];
+    offset: number;
+}
+
+interface Input {
+    sts: MappedRange[];
+    stf: MappedRange[];
+    ftw: MappedRange[];
+    wtl: MappedRange[];
+    ltt: MappedRange[];
+    tth: MappedRange[];
+    htl: MappedRange[];
+}
+
+
+function pointerMap(array: number[][]): MappedRange[] {
+    return array.map(arr => {    
+        const startRange: [number, number] = [arr[1], arr[1] + arr[2]];
+        const offset = arr[0] - arr[1];
+        console.log(`Made descriptor (start -> dest): ${arr[1]} -> ${arr[0]} ... ${arr[1] + offset} -> ${arr[0] + offset}`)
+        return {
+            startRange,
+            offset
+        }
+    })
+}
+
+function findSmallestLocation(seeds: number[], mappedInput: Input): {
+    seed: number;
+    location: number;
+} {
+    const locations: {seed: number, location: number}[] = [];
+    for(const seed of seeds) {
+        console.log(`Checking seed ${seed}`)
+
+        let s = mappedInput.sts.find(o => o.startRange[0] <= seed && seed <= o.startRange[1])
+        const soil = (s ? s.offset + seed : null) ?? seed
+        console.log(soil === seed ? `Continuing with default` : `Found ${soil}`)
+
+        let f = mappedInput.stf.find(o => o.startRange[0] <= soil && soil <= o.startRange[1])
+        const fertilizer = (f ? f.offset + soil : null) ?? soil
+        console.log(fertilizer === soil ? `Continuing with default` : `Found ${fertilizer}`)
+
+        let w = mappedInput.ftw.find(o => o.startRange[0] <= fertilizer && fertilizer <= o.startRange[1])
+        const water = (w ? w.offset + fertilizer : null) ?? fertilizer
+        console.log(water === fertilizer ? `Continuing with default` : `Found ${water}`)
+
+        let li = mappedInput.wtl.find(o => o.startRange[0] <= water && water <= o.startRange[1])
+        const light = (li ? li.offset + water : null) ?? water
+        console.log(light === water ? `Continuing with default` : `Found ${light}`)
+
+        let t = mappedInput.ltt.find(o => o.startRange[0] <= light && light <= o.startRange[1])
+        const temperature = (t ? t.offset + light : null) ?? light
+        console.log(temperature === light ? `Continuing with default` : `Found ${temperature}`)
+        
+        let h = mappedInput.tth.find(o => o.startRange[0] <= temperature && temperature <= o.startRange[1])
+        const humidity = (h ? h.offset + temperature : null) ?? temperature
+        console.log(humidity === temperature ? `Continuing with default` : `Found ${humidity}`)
+
+        let lo = mappedInput.htl.find(o => o.startRange[0] <= humidity && humidity <= o.startRange[1])
+        const location = (lo ? lo.offset + humidity : null) ?? humidity
+        console.log(location === humidity ? `Continuing with default` : `Found ${location}`)
+
+        locations.push({seed, location})
+    }
+
+    const sorted = locations.sort((a,b) => a.location < b.location ? -1 : 1);
+    return sorted[0]
+}
+
+const mappedInput: Input = {
+    sts: pointerMap(input[1].split(':')[1].trim().split('\n').map(i => i.split(' ').map(n => parseInt(n)))),
+    stf: pointerMap(input[2].split(':')[1].trim().split('\n').map(i => i.split(' ').map(n => parseInt(n)))),
+    ftw: pointerMap(input[3].split(':')[1].trim().split('\n').map(i => i.split(' ').map(n => parseInt(n)))),
+    wtl: pointerMap(input[4].split(':')[1].trim().split('\n').map(i => i.split(' ').map(n => parseInt(n)))),
+    ltt: pointerMap(input[5].split(':')[1].trim().split('\n').map(i => i.split(' ').map(n => parseInt(n)))),
+    tth: pointerMap(input[6].split(':')[1].trim().split('\n').map(i => i.split(' ').map(n => parseInt(n)))),
+    htl: pointerMap(input[7].split(':')[1].trim().split('\n').map(i => i.split(' ').map(n => parseInt(n))))
+};
+console.log(`Generating descriptors for seed to soil`);
+console.log(`Generating descriptors for soil to fertilizer`);
+console.log(`Generating descriptors for fertilizer to water`);
+console.log(`Generating descriptors for water to light`);
+console.log(`Generating descriptors for light to temp`);
+console.log(`Generating descriptors for temp to humidity`);
+console.log(`Generating descriptors for humidity to location`);
+
+
+// Part 1
+;(() => {
+    const seeds = input[0].split(':')[1].trim().split(' ').map(i => parseInt(i));
+    const smallest = findSmallestLocation(seeds, mappedInput)
+    
+    console.log(`(Part 1) Lowest location number: (seed: ${smallest.seed}) ${smallest.location}`)
+})();
+
+
+// Part 2
+
+function findSmallestLocationRange(seeds: number[], mappedInput: Input): {
+    seed: number;
+    location: number;
+} {
+    const locations: {seed: number, location: number}[] = [];
+    for(const seed of seeds) {
+
+        console.log(`Checking seed ${seed}`)
+
+        let s = mappedInput.sts.find(o => o.startRange[0] <= seed && seed <= o.startRange[1])
+        const soil = (s ? s.offset + seed : null) ?? seed
+        console.log(soil === seed ? `Continuing with default` : `Found ${soil}`)
+
+        let f = mappedInput.stf.find(o => o.startRange[0] <= soil && soil <= o.startRange[1])
+        const fertilizer = (f ? f.offset + soil : null) ?? soil
+        console.log(fertilizer === soil ? `Continuing with default` : `Found ${fertilizer}`)
+
+        let w = mappedInput.ftw.find(o => o.startRange[0] <= fertilizer && fertilizer <= o.startRange[1])
+        const water = (w ? w.offset + fertilizer : null) ?? fertilizer
+        console.log(water === fertilizer ? `Continuing with default` : `Found ${water}`)
+
+        let li = mappedInput.wtl.find(o => o.startRange[0] <= water && water <= o.startRange[1])
+        const light = (li ? li.offset + water : null) ?? water
+        console.log(light === water ? `Continuing with default` : `Found ${light}`)
+
+        let t = mappedInput.ltt.find(o => o.startRange[0] <= light && light <= o.startRange[1])
+        const temperature = (t ? t.offset + light : null) ?? light
+        console.log(temperature === light ? `Continuing with default` : `Found ${temperature}`)
+        
+        let h = mappedInput.tth.find(o => o.startRange[0] <= temperature && temperature <= o.startRange[1])
+        const humidity = (h ? h.offset + temperature : null) ?? temperature
+        console.log(humidity === temperature ? `Continuing with default` : `Found ${humidity}`)
+
+        let lo = mappedInput.htl.find(o => o.startRange[0] <= humidity && humidity <= o.startRange[1])
+        const location = (lo ? lo.offset + humidity : null) ?? humidity
+        console.log(location === humidity ? `Continuing with default` : `Found ${location}`)
+
+        locations.push({seed, location})
+    }
+
+    const sorted = locations.sort((a,b) => a.location < b.location ? -1 : 1);
+    return sorted[0]
+}
+
+
+
+;(() => {
+    const unformattedSeeds = input[0].split(':')[1].trim().match(/(\d+ \d+)/g)?.map(i => i.split(' ').map(n => parseInt(n.trim())))
+    const seeds: number[] = [];
+
+    if(!unformattedSeeds) throw new Error('How did this happen')
+
+    for(const US of unformattedSeeds) {
+        for(let i = 0; i < US[1]; i++) {
+            seeds.push(US[0] + i);
+        }
+    }
+
+    const smallest = findSmallestLocation(seeds, mappedInput)
+
+    console.log(`(Part 2) Lowest location number: (seed: ${smallest.seed}) ${smallest.location}`)
+})();
\ No newline at end of file
diff --git a/day 5/old.ts b/day 5/old.ts
new file mode 100644
index 0000000..7654183
--- /dev/null
+++ b/day 5/old.ts
@@ -0,0 +1,97 @@
+import { readFileSync } from "fs";
+
+const input = readFileSync('day 5/input.txt').toString().split('\n\n')
+
+interface MappedRange {
+    start: number;
+    dest: number;
+    rangeLength: number;
+    pointers: Record<number, number>;
+}
+
+interface Input {
+    seeds?: number[];
+    sts?: MappedRange[];
+    stf?: MappedRange[];
+    ftw?: MappedRange[];
+    wtl?: MappedRange[];
+    ltt?: MappedRange[];
+    tth?: MappedRange[];
+    htl?: MappedRange[];
+}
+
+
+const mappedInput: Input = {};
+
+mappedInput.seeds = input[0].split(':')[1].trim().split(' ').map(i => parseInt(i));
+const sts = input[1].split(':')[1].trim().split('\n').map(i => i.split(' ').map(n => parseInt(n)));
+const stf = input[2].split(':')[1].trim().split('\n').map(i => i.split(' ').map(n => parseInt(n)));
+const ftw = input[3].split(':')[1].trim().split('\n').map(i => i.split(' ').map(n => parseInt(n)));
+const wtl = input[4].split(':')[1].trim().split('\n').map(i => i.split(' ').map(n => parseInt(n)));
+const ltt = input[5].split(':')[1].trim().split('\n').map(i => i.split(' ').map(n => parseInt(n)));
+const tth = input[6].split(':')[1].trim().split('\n').map(i => i.split(' ').map(n => parseInt(n)));
+const htl = input[7].split(':')[1].trim().split('\n').map(i => i.split(' ').map(n => parseInt(n)));
+
+function pointerMap(array: number[][]) {
+    return array.map(arr => {
+        const pointers: Record<number, number> = {}
+        for(Object.keys(pointers).length; Object.keys(pointers).length < arr[2];) {
+            const start = arr[1] + Object.keys(pointers).length;
+            const dest = arr[0] + Object.keys(pointers).length
+            console.log(`Creating Pointer ${start} => ${dest}`)
+            pointers[start] = dest;
+        }
+    
+        return {
+            pointers,
+            rangeLength: arr[2],
+            start: arr[1],
+            dest: arr[0]
+        }
+    })
+}
+console.log(`Generating Pointers for seed to soil`)
+mappedInput.sts = pointerMap(sts);
+console.log(`Generating Pointers for soil to fertilizer`)
+mappedInput.stf = pointerMap(stf);
+console.log(`Generating Pointers for fertilizer to water`)
+mappedInput.ftw = pointerMap(ftw);
+console.log(`Generating Pointers for water to light`)
+mappedInput.wtl = pointerMap(wtl);
+console.log(`Generating Pointers for light to temp`)
+mappedInput.ltt = pointerMap(ltt);
+console.log(`Generating Pointers for temp to humidity`)
+mappedInput.tth = pointerMap(tth);
+console.log(`Generating Pointers for humidity to location`)
+mappedInput.htl = pointerMap(htl);
+
+
+// Part 1
+(() => {
+    const locations: {seed: number, location: number}[] = []
+    for(const seed of mappedInput.seeds) {
+        console.log(`Checking ${seed}`)
+
+        const soil = mappedInput.stf.find(s => Object.keys(s.pointers).includes(seed.toString()))?.pointers[seed] ?? seed
+        console.log(soil === seed ? `Continuing with default` : `Found ${soil}`)
+        const fertilizer = mappedInput.stf.find(s => Object.keys(s.pointers).includes(soil.toString()))?.pointers[soil] ?? soil
+        console.log(fertilizer === soil ? `Continuing with default` : `Found ${fertilizer}`)
+        const water = mappedInput.stf.find(s => Object.keys(s.pointers).includes(fertilizer.toString()))?.pointers[fertilizer] ?? fertilizer
+        console.log(water === fertilizer ? `Continuing with default` : `Found ${water}`)
+        const light = mappedInput.stf.find(s => Object.keys(s.pointers).includes(water.toString()))?.pointers[water] ?? water
+        console.log(light === water ? `Continuing with default` : `Found ${light}`)
+        const temperature = mappedInput.stf.find(s => Object.keys(s.pointers).includes(light.toString()))?.pointers[light] ?? light
+        console.log(temperature === light ? `Continuing with default` : `Found ${temperature}`)
+        const humidity = mappedInput.stf.find(s => Object.keys(s.pointers).includes(temperature.toString()))?.pointers[temperature] ?? temperature
+        console.log(humidity === temperature ? `Continuing with default` : `Found ${humidity}`)
+        const location = mappedInput.stf.find(s => Object.keys(s.pointers).includes(humidity.toString()))?.pointers[humidity] ?? humidity
+        console.log(location === humidity ? `Continuing with default` : `Found ${location}`)
+
+        locations.push({seed, location})
+    }
+
+    const sorted = locations.sort((a,b) => a.location < b.location ? -1 : 1);
+    console.log(sorted)
+    const smallest = sorted[0]
+    console.log(`(Part 1) Lowest location number: (seed: ${smallest.seed}) ${smallest.location}`)
+})();
\ No newline at end of file
diff --git a/day 5/testdata.txt b/day 5/testdata.txt
new file mode 100644
index 0000000..bd902a4
--- /dev/null
+++ b/day 5/testdata.txt
@@ -0,0 +1,33 @@
+seeds: 79 14 55 13
+
+seed-to-soil map:
+50 98 2
+52 50 48
+
+soil-to-fertilizer map:
+0 15 37
+37 52 2
+39 0 15
+
+fertilizer-to-water map:
+49 53 8
+0 11 42
+42 0 7
+57 7 4
+
+water-to-light map:
+88 18 7
+18 25 70
+
+light-to-temperature map:
+45 77 23
+81 45 19
+68 64 13
+
+temperature-to-humidity map:
+0 69 1
+1 0 69
+
+humidity-to-location map:
+60 56 37
+56 93 4
\ No newline at end of file