From ee83f34aef6eed15d574e4815b643a8d2a336945 Mon Sep 17 00:00:00 2001 From: AlexAegis Date: Tue, 5 Dec 2023 08:56:29 +0100 Subject: [PATCH] feat: initial refraction idea --- solutions/typescript/2023/05/src/p1.spec.ts | 4 +- solutions/typescript/2023/05/src/p2.naive.ts | 27 ++++++ solutions/typescript/2023/05/src/p2.spec.ts | 57 +++++++++++- solutions/typescript/2023/05/src/p2.ts | 93 ++++++++++++++++--- .../libs/lib/src/array/array.polyfill.ts | 5 +- .../src/array/groups/slide-window.function.ts | 3 +- 6 files changed, 171 insertions(+), 18 deletions(-) create mode 100644 solutions/typescript/2023/05/src/p2.naive.ts diff --git a/solutions/typescript/2023/05/src/p1.spec.ts b/solutions/typescript/2023/05/src/p1.spec.ts index ef7c59b7d..6810d0424 100644 --- a/solutions/typescript/2023/05/src/p1.spec.ts +++ b/solutions/typescript/2023/05/src/p1.spec.ts @@ -7,14 +7,14 @@ describe('2023 05 p1', () => { describe('the input', () => { it('should solve the input', async () => { const resources = await loadTaskResources(packageJson.aoc); - expect(p1(resources.input)).toEqual(0); + expect(p1(resources.input)).toEqual(84_470_622); }); }); describe('example 1', () => { it('should be solved', async () => { const resources = await loadTaskResources(packageJson.aoc, 'example.1.txt'); - expect(p1(resources.input)).toEqual(0); + expect(p1(resources.input)).toEqual(35); }); }); }); diff --git a/solutions/typescript/2023/05/src/p2.naive.ts b/solutions/typescript/2023/05/src/p2.naive.ts new file mode 100644 index 000000000..537bcc964 --- /dev/null +++ b/solutions/typescript/2023/05/src/p2.naive.ts @@ -0,0 +1,27 @@ +import { task } from '@alexaegis/advent-of-code-lib'; +import packageJson from '../package.json'; +import { findRange, parse } from './parse.js'; + +// Will never terminate for the actual input +export const p2Naive = (input: string): number => { + const data = parse(input); + return data.seeds + .slideWindow(2, 2) + .flatMap(([seedStart, seedCount]) => { + const locations: number[] = []; + for (let seed = seedStart; seed < seedStart + seedCount; seed++) { + const soil = findRange(seed, data.seedToSoilMap); + const fertilizer = findRange(soil, data.soilToFertilizerMap); + const water = findRange(fertilizer, data.fertilizerToWaterMap); + const light = findRange(water, data.waterToLightMap); + const temperature = findRange(light, data.lightToTemperatureMap); + const humidity = findRange(temperature, data.temperatureToHumidityMap); + const location = findRange(humidity, data.humidityToLocationMap); + locations.push(location); + } + return locations; + }) + .min(); +}; + +await task(p2Naive, packageJson.aoc, 'example.1.txt'); // 46 diff --git a/solutions/typescript/2023/05/src/p2.spec.ts b/solutions/typescript/2023/05/src/p2.spec.ts index 02ddc2c23..c43d2fdb6 100644 --- a/solutions/typescript/2023/05/src/p2.spec.ts +++ b/solutions/typescript/2023/05/src/p2.spec.ts @@ -1,7 +1,8 @@ import { loadTaskResources } from '@alexaegis/advent-of-code-lib'; import { describe, expect, it } from 'vitest'; import packageJson from '../package.json'; -import { p2 } from './p2.js'; +import { p2, refract } from './p2.js'; +import type { Range } from './parse.js'; describe('2023 05 p2', () => { describe('the input', () => { @@ -17,4 +18,58 @@ describe('2023 05 p2', () => { expect(p2(input)).toEqual(0); }); }); + + describe('refraction', () => { + it('should refract into 3 ranges when b target is enveloped into a target', () => { + const a: Range = { + sourceRangeStart: 2, + destinationRange: 2, + rangeLength: 4, + }; + const b: Range = { + sourceRangeStart: 3, + destinationRange: 7, + rangeLength: 2, + }; + const result = refract(a, b); + + expect(result).toContainEqual({ + sourceRangeStart: 5, + destinationRange: 5, + rangeLength: 1, + }); + expect(result).toContainEqual({ + sourceRangeStart: 3, + destinationRange: 7, + rangeLength: 2, + }); + expect(result).toContainEqual({ + sourceRangeStart: 2, + destinationRange: 2, + rangeLength: 1, + }); + expect(result.length).toEqual(3); + }); + + it('should refract into 1 range when a target is enveloped into b source', () => { + const a: Range = { + sourceRangeStart: 2, + destinationRange: 2, + rangeLength: 2, + }; + const b: Range = { + sourceRangeStart: 0, + destinationRange: 1, + rangeLength: 6, + }; + const result = refract(a, b); + + expect(result).toContainEqual({ + sourceRangeStart: 2, + destinationRange: 3, + rangeLength: 2, + }); + expect(result.length).toEqual(1); + }); + }); }); diff --git a/solutions/typescript/2023/05/src/p2.ts b/solutions/typescript/2023/05/src/p2.ts index c98ddee85..4ba73f2f9 100644 --- a/solutions/typescript/2023/05/src/p2.ts +++ b/solutions/typescript/2023/05/src/p2.ts @@ -1,21 +1,90 @@ -import { task } from '@alexaegis/advent-of-code-lib'; +import { Interval, task } from '@alexaegis/advent-of-code-lib'; import packageJson from '../package.json'; -import { findRange, parse } from './parse.js'; +import { parse, type Range } from './parse.js'; + +export const getRangeEnd = (range: Range): number => { + return range.destinationRange + range.rangeLength; +}; + +export const refract = (a: Range, b: Range): Range[] => { + const aTarget = Interval.closed(a.destinationRange, a.destinationRange + a.rangeLength); + const bSource = Interval.closed(b.sourceRangeStart, b.sourceRangeStart + b.rangeLength); + const bTarget = Interval.closed(b.destinationRange, b.destinationRange + b.rangeLength); + + const result: Range[] = []; + + if (aTarget.high > bSource.high) { + const topRefraction: Range = { + sourceRangeStart: bSource.high, + destinationRange: bSource.high, + rangeLength: Math.min(aTarget.high - bSource.high, a.rangeLength), + }; + result.push(topRefraction); + } + + if (aTarget.low < bSource.low) { + const bottomRefraction: Range = { + sourceRangeStart: aTarget.low, + destinationRange: aTarget.low, + rangeLength: Math.min(bSource.low - aTarget.low, a.rangeLength), + }; + result.push(bottomRefraction); + } + + aTarget.intersects(bSource); + // Only if there's an itersection + if (aTarget.intersects(bSource)) { + const middleRefractionStartLow = Math.max(aTarget.low, bSource.low); + const middleRefractionStartHigh = Math.min(aTarget.high, bSource.high); + const mappingDelta = bTarget.low - bSource.low; + // Only this one is mapping + const middleRefraction: Range = { + sourceRangeStart: middleRefractionStartLow, + rangeLength: middleRefractionStartHigh - middleRefractionStartLow, + destinationRange: middleRefractionStartLow + mappingDelta, + }; + result.push(middleRefraction); + } + + return result; +}; export const p2 = (input: string): number => { const data = parse(input); return data.seeds - .map((seed) => { - const soil = findRange(seed, data.seedToSoilMap); - const fertilizer = findRange(soil, data.soilToFertilizerMap); - const water = findRange(fertilizer, data.fertilizerToWaterMap); - const light = findRange(water, data.waterToLightMap); - const temperature = findRange(light, data.lightToTemperatureMap); - const humidity = findRange(temperature, data.temperatureToHumidityMap); - const location = findRange(humidity, data.humidityToLocationMap); - return location; + .slideWindow(2, 2) + .flatMap(([seedStart, seedCount]) => { + const firstRange: Range = { + sourceRangeStart: seedStart, + destinationRange: seedStart, + rangeLength: seedCount, + }; + + const seedToSoilRefraction = data.seedToSoilMap.flatMap((range) => + refract(firstRange, range), + ); + const soilToFertilizerRefraction = seedToSoilRefraction.flatMap((a) => + data.soilToFertilizerMap.flatMap((range) => refract(a, range)), + ); + const fertilizerToWaterRefraction = soilToFertilizerRefraction.flatMap((a) => + data.fertilizerToWaterMap.flatMap((range) => refract(a, range)), + ); + const waterToLightRefraction = fertilizerToWaterRefraction.flatMap((a) => + data.waterToLightMap.flatMap((range) => refract(a, range)), + ); + const lightToTemperatureRefraction = waterToLightRefraction.flatMap((a) => + data.lightToTemperatureMap.flatMap((range) => refract(a, range)), + ); + const temperatureToHumidityRefraction = lightToTemperatureRefraction.flatMap((a) => + data.temperatureToHumidityMap.flatMap((range) => refract(a, range)), + ); + const humidityToLocationRefraction = temperatureToHumidityRefraction.flatMap((a) => + data.humidityToLocationMap.flatMap((range) => refract(a, range)), + ); + + return humidityToLocationRefraction.map((range) => range.sourceRangeStart).min(); }) .min(); }; -await task(p2, packageJson.aoc); // 84470622 ~4.36ms +await task(p2, packageJson.aoc, 'example.1.txt'); // 84470622 ~4.36ms diff --git a/solutions/typescript/libs/lib/src/array/array.polyfill.ts b/solutions/typescript/libs/lib/src/array/array.polyfill.ts index 4d0c8323a..82df6bcee 100644 --- a/solutions/typescript/libs/lib/src/array/array.polyfill.ts +++ b/solutions/typescript/libs/lib/src/array/array.polyfill.ts @@ -77,7 +77,7 @@ declare global { filterMap(mapFn: (t: T) => V | undefined): V[]; partition(partitioner: (a: T) => boolean): [T[], T[]]; pairwise(callback: (a: T, b: T) => void): void; - slideWindow(windowSize?: N): SizedTuple[]; + slideWindow(windowSize?: N, stepSize?: number): SizedTuple[]; bubbleFindPair(comparator: (a: T, b: T) => boolean): [T, T]; walkPairs(): Generator<[T, T]>; pairs(): [T, T][]; @@ -228,8 +228,9 @@ Array.prototype.count = function (this: T[], predicate: (t: T) => boolean): n Array.prototype.slideWindow = function ( windowSize: N = 2 as N, + stepSize = 1, ): SizedTuple[] { - return slideWindow(this, windowSize); + return slideWindow(this, windowSize, stepSize); }; Array.prototype.pairwise = function (this: T[], callback: (a: T, b: T) => void): void { diff --git a/solutions/typescript/libs/lib/src/array/groups/slide-window.function.ts b/solutions/typescript/libs/lib/src/array/groups/slide-window.function.ts index 8770e39d2..a760b6aa7 100644 --- a/solutions/typescript/libs/lib/src/array/groups/slide-window.function.ts +++ b/solutions/typescript/libs/lib/src/array/groups/slide-window.function.ts @@ -3,6 +3,7 @@ import type { SizedTuple } from '../../model/index.js'; export const slideWindow = ( array: T[], windowSize: N = 2 as N, + stepSize = 1, ): SizedTuple[] => { const result: SizedTuple[] = []; const window = []; @@ -10,7 +11,7 @@ export const slideWindow = ( window.push(element); if (window.length === windowSize) { result.push([...window] as SizedTuple); - window.shift(); + window.splice(0, stepSize); } } return result;