diff --git a/.github/badges/typescript/2023.json b/.github/badges/typescript/2023.json index dd2870b95..f53f75a8b 100644 --- a/.github/badges/typescript/2023.json +++ b/.github/badges/typescript/2023.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, "label": "Advent of TypeScript 2023", - "message": "14/25", + "message": "15/25", "color": "orange" } diff --git a/solutions/typescript/2023/13/package.json b/solutions/typescript/2023/13/package.json index 45f17eae5..8de9fa568 100644 --- a/solutions/typescript/2023/13/package.json +++ b/solutions/typescript/2023/13/package.json @@ -61,11 +61,6 @@ "import": "./dist/p2.js", "default": "./dist/p2.js" }, - "./parse": { - "types": "./src/parse.ts", - "import": "./dist/parse.js", - "default": "./dist/parse.js" - }, "./readme": "./readme.md" }, "dependencies": { diff --git a/solutions/typescript/2023/13/src/internal/matrix-reflection.spec.ts b/solutions/typescript/2023/13/src/internal/matrix-reflection.spec.ts new file mode 100644 index 000000000..ce04b6332 --- /dev/null +++ b/solutions/typescript/2023/13/src/internal/matrix-reflection.spec.ts @@ -0,0 +1,20 @@ +import { Interval } from '@alexaegis/advent-of-code-lib'; +import { describe, expect, it } from 'vitest'; +import { findReflectivePairings } from './matrix-reflection.js'; + +describe('findReflectivePairings', () => { + it('should be able to collect reflective pairs from the middle', () => { + const result = findReflectivePairings(Interval.closed(0, 8), 4); + expect(result.length).toEqual(4); + expect(result[0]).toEqual([4, 5]); + expect(result[1]).toEqual([3, 6]); + expect(result[2]).toEqual([2, 7]); + expect(result[3]).toEqual([1, 8]); + }); + + it('should be able to collect reflective pairs from the start', () => { + const result = findReflectivePairings(Interval.closed(0, 8), 0); + expect(result.length).toEqual(1); + expect(result[0]).toEqual([0, 1]); + }); +}); diff --git a/solutions/typescript/2023/13/src/internal/matrix-reflection.ts b/solutions/typescript/2023/13/src/internal/matrix-reflection.ts new file mode 100644 index 000000000..336a7faf3 --- /dev/null +++ b/solutions/typescript/2023/13/src/internal/matrix-reflection.ts @@ -0,0 +1,61 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { GridGraph, GridGraphNode, Interval, ToString } from '@alexaegis/advent-of-code-lib'; + +export const matchingNodes = < + T extends ToString = string, + N extends GridGraphNode = GridGraphNode, +>( + a: N[], + b: N[], +): number => { + return a.zip(b).filter(([an, bn]) => an.value === bn.value).length; +}; + +export const findReflectivePairings = ( + interval: Interval, + reflectionOrigin: number, +): [number, number][] => { + return reflectionOrigin + .interval(interval.high) + .collectValues() + .filterMap((right) => { + const left = reflectionOrigin - (right - reflectionOrigin); + return left >= interval.low ? [left, right + 1] : undefined; + }); +}; + +export const findReflection = < + T extends ToString = string, + N extends GridGraphNode = GridGraphNode, +>( + gg: GridGraph, + axis: 'row' | 'column', + smudges = 0, + skip?: number, +): number | undefined => { + const aabb = gg.boundingBox(); + const axisInterval = axis === 'column' ? aabb.horizontal : aabb.vertical; + const otherAxistInterval = axis === 'column' ? aabb.vertical : aabb.horizontal; + const getNodesOfAxis = (n: number) => (axis === 'column' ? gg.getColumn(n) : gg.getRow(n)); + return axisInterval.collectValues().find((index) => { + if (skip !== undefined && index === skip) { + return false; + } + + const pairs = findReflectivePairings(axisInterval, index); + + if (pairs.length === 0) { + return false; + } + + const matching = pairs + .map(([left, right]) => { + const leftNodes = getNodesOfAxis(left)!; + const rightNodes = getNodesOfAxis(right)!; + return matchingNodes(leftNodes, rightNodes); + }) + .sum(); + + return matching + smudges === pairs.length * otherAxistInterval.length; + }); +}; diff --git a/solutions/typescript/2023/13/src/p1.spec.ts b/solutions/typescript/2023/13/src/p1.spec.ts index b22b8c446..ef228c9d2 100644 --- a/solutions/typescript/2023/13/src/p1.spec.ts +++ b/solutions/typescript/2023/13/src/p1.spec.ts @@ -1,7 +1,7 @@ import { loadTaskResources } from '@alexaegis/advent-of-code-lib'; import { describe, expect, it } from 'vitest'; import packageJson from '../package.json'; -import { findReflectivePairings, p1 } from './p1.js'; +import { p1 } from './p1.js'; describe('2023 13 p1', () => { describe('the input', () => { @@ -17,23 +17,4 @@ describe('2023 13 p1', () => { expect(p1(resources.input)).toEqual(405); }); }); - - describe('findReflectivePairings', () => { - it('should be able to collect reflective pairs from the middle', () => { - const result = findReflectivePairings(0, 8, 4); - console.log(result); - expect(result.length).toEqual(4); - expect(result[0]).toEqual([4, 5]); - expect(result[1]).toEqual([3, 6]); - expect(result[2]).toEqual([2, 7]); - expect(result[3]).toEqual([1, 8]); - }); - - it('should be able to collect reflective pairs from the start', () => { - const result = findReflectivePairings(0, 8, 0); - console.log(result); - expect(result.length).toEqual(1); - expect(result[0]).toEqual([0, 1]); - }); - }); }); diff --git a/solutions/typescript/2023/13/src/p1.ts b/solutions/typescript/2023/13/src/p1.ts index 480145705..169e27de1 100644 --- a/solutions/typescript/2023/13/src/p1.ts +++ b/solutions/typescript/2023/13/src/p1.ts @@ -1,120 +1,15 @@ -import { - DOUBLE_NEWLINE, - GridGraph, - GridGraphNode, - task, - type ToString, -} from '@alexaegis/advent-of-code-lib'; +import { DOUBLE_NEWLINE, task } from '@alexaegis/advent-of-code-lib'; import packageJson from '../package.json'; +import { findReflection } from './internal/matrix-reflection.js'; -/** - * @deprecated use the one on the graph - */ -const getColumn = = GridGraphNode>( - gg: GridGraph, - column: number, -): N[] | undefined => { - const aabb = gg.boundingBox(); - return aabb.horizontal.contains(column) - ? aabb.vertical.map((row) => { - const node = gg.getNode({ x: column, y: row }); - if (!node) { - throw new Error(`problem while fetching row: ${row} column: ${column}`); - } - return node; - }) - : undefined; -}; - -/** - * @deprecated use the one on the graph - */ -const getRow = = GridGraphNode>( - gg: GridGraph, - row: number, -): N[] | undefined => { - const aabb = gg.boundingBox(); - return aabb.vertical.contains(row) - ? aabb.horizontal.map((column) => { - const node = gg.getNode({ x: column, y: row }); - if (!node) { - throw new Error(`problem while fetching row: ${row} column: ${column}`); - } - return node; - }) - : undefined; -}; - -const matchingNodes = = GridGraphNode>( - a: N[], - b: N[], -): boolean => { - return a.zip(b).every(([an, bn]) => an.value === bn.value); -}; - -export const findReflectivePairings = ( - low: number, - high: number, - reflectionOrigin: number, -): [number, number][] => { - return reflectionOrigin - .interval(high) - .collectValues() - .filterMap((right) => { - const left = reflectionOrigin - (right - reflectionOrigin); - return left >= low ? [left, right + 1] : undefined; - }); -}; - -const findColumnReflection = < - T extends ToString = string, - N extends GridGraphNode = GridGraphNode, ->( - gg: GridGraph, -): number | undefined => { - const aabb = gg.boundingBox(); - return aabb.horizontal.collectValues().find((column) => { - const pairs = findReflectivePairings(aabb.horizontal.low, aabb.horizontal.high, column); - return ( - pairs.length > 0 && - pairs.every(([left, right]) => { - const leftColumn = getColumn(gg, left); - const rightColumn = getColumn(gg, right); - return !leftColumn || !rightColumn || matchingNodes(leftColumn, rightColumn); - }) - ); - }); -}; - -const findRowReflection = < - T extends ToString = string, - N extends GridGraphNode = GridGraphNode, ->( - gg: GridGraph, -): number | undefined => { - const aabb = gg.boundingBox(); - return aabb.vertical.collectValues().find((row) => { - const pairs = findReflectivePairings(aabb.vertical.low, aabb.vertical.high, row); - return ( - pairs.length > 0 && - pairs.every(([top, bottom]) => { - const topRow = getRow(gg, top); - const bottomRow = getRow(gg, bottom); - return !topRow || !bottomRow || matchingNodes(topRow, bottomRow); - }) - ); - }); -}; - -export const p1 = (input: string): number => { - return input +export const p1 = (input: string): number => + input .split(DOUBLE_NEWLINE) .map((m) => m.toGridGraph()) - .map((gg) => { - const columnReflectionIndex = findColumnReflection(gg); - const rowReflectionIndex = findRowReflection(gg); - console.log('verticalReflectionIndex', columnReflectionIndex); - console.log('rowReflectionIndex', rowReflectionIndex); + .map((graph) => { + const columnReflectionIndex = findReflection(graph, 'column'); + const rowReflectionIndex = findReflection(graph, 'row'); + let value = 0; if (columnReflectionIndex !== undefined) { value = columnReflectionIndex + 1; @@ -125,6 +20,5 @@ export const p1 = (input: string): number => { return value; }) .sum(); -}; -await task(p1, packageJson.aoc); // 37718 ~0ms +await task(p1, packageJson.aoc); // 37718 ~371.96ms diff --git a/solutions/typescript/2023/13/src/p2.spec.ts b/solutions/typescript/2023/13/src/p2.spec.ts index 4ac50be6a..c6935fad5 100644 --- a/solutions/typescript/2023/13/src/p2.spec.ts +++ b/solutions/typescript/2023/13/src/p2.spec.ts @@ -3,18 +3,18 @@ import { describe, expect, it } from 'vitest'; import packageJson from '../package.json'; import { p2 } from './p2.js'; -describe.skip('2023 13 p2', () => { +describe('2023 13 p2', () => { describe('the input', () => { it('should solve the input', async () => { const { input } = await loadTaskResources(packageJson.aoc); - expect(p2(input)).toEqual(0); + expect(p2(input)).toEqual(40_995); }); }); describe('example 1', () => { it('should be solved', async () => { const { input } = await loadTaskResources(packageJson.aoc, 'example.1.txt'); - expect(p2(input)).toEqual(0); + expect(p2(input)).toEqual(400); }); }); }); diff --git a/solutions/typescript/2023/13/src/p2.ts b/solutions/typescript/2023/13/src/p2.ts index e3bc905e7..d33b92a01 100644 --- a/solutions/typescript/2023/13/src/p2.ts +++ b/solutions/typescript/2023/13/src/p2.ts @@ -1,189 +1,27 @@ -import { - DOUBLE_NEWLINE, - GridGraph, - GridGraphNode, - Vec2, - task, - type ToString, -} from '@alexaegis/advent-of-code-lib'; +import { DOUBLE_NEWLINE, task } from '@alexaegis/advent-of-code-lib'; import packageJson from '../package.json'; - -const getColumn = = GridGraphNode>( - gg: GridGraph, - column: number, -): string[] | undefined => { - const aabb = gg.boundingBox(); - return aabb.horizontal.contains(column) - ? aabb.vertical.map((row) => { - const node = gg.getNode({ x: column, y: row }); - if (!node) { - throw new Error(`problem while fetching row: ${row} column: ${column}`); - } - return node.value.toString(); - }) - : undefined; -}; - -const getRow = = GridGraphNode>( - gg: GridGraph, - row: number, -): string[] | undefined => { - const aabb = gg.boundingBox(); - return aabb.vertical.contains(row) - ? aabb.horizontal.map((column) => { - const node = gg.getNode({ x: column, y: row }); - if (!node) { - throw new Error(`problem while fetching row: ${row} column: ${column}`); - } - return node.value.toString(); - }) - : undefined; -}; - -const matchingNodes = (a: string[], b: string[]): boolean => { - return a.zip(b).every(([an, bn]) => an === bn); -}; - -export const findReflectivePairings = ( - low: number, - high: number, - reflectionOrigin: number, -): [number, number][] => { - return reflectionOrigin - .interval(high) - .collectValues() - .filterMap((right) => { - const left = reflectionOrigin - (right - reflectionOrigin); - return left >= low ? [left, right + 1] : undefined; - }); -}; - -const findColumnReflection = < - T extends ToString = string, - N extends GridGraphNode = GridGraphNode, ->( - gg: GridGraph, -): number | undefined => { - const aabb = gg.boundingBox(); - return aabb.horizontal.collectValues().find((column) => { - const pairs = findReflectivePairings(aabb.horizontal.low, aabb.horizontal.high, column); - return ( - pairs.length > 0 && - pairs.every(([left, right]) => { - const leftColumn = getColumn(gg, left); - const rightColumn = getColumn(gg, right); - return !leftColumn || !rightColumn || matchingNodes(leftColumn, rightColumn); - }) - ); - }); -}; - -const findRowReflection = < - T extends ToString = string, - N extends GridGraphNode = GridGraphNode, ->( - gg: GridGraph, -): number | undefined => { - const aabb = gg.boundingBox(); - return aabb.vertical.collectValues().find((row) => { - const pairs = findReflectivePairings(aabb.vertical.low, aabb.vertical.high, row); - return ( - pairs.length > 0 && - pairs.every(([top, bottom]) => { - const topRow = getRow(gg, top); - const bottomRow = getRow(gg, bottom); - return !topRow || !bottomRow || matchingNodes(topRow, bottomRow); - }) - ); - }); -}; +import { findReflection } from './internal/matrix-reflection.js'; export const p2 = (input: string): number => { return input .split(DOUBLE_NEWLINE) .map((m) => m.toGridGraph()) - .map((gg) => { - gg.print(); - console.log('----'); - let columnReflectionIndex: number | undefined = undefined; - let rowReflectionIndex: number | undefined = undefined; - - const unsmudgedRowIndex = findRowReflection(gg); - const unsmudgedColumnIndex = findColumnReflection(gg); - console.log( - 'unsmudgedRowIndex', - unsmudgedRowIndex, - 'unsmudgedColumnIndex', - unsmudgedColumnIndex, - ); - - const possibleSmudges = gg.boundingBox().renderIntoVectors(); - possibleSmudges.sort((b, a) => - a.manhattan({ x: unsmudgedColumnIndex ?? b.x, y: unsmudgedRowIndex ?? b.y }), - ); - - let smudge: Vec2 | undefined = undefined; - for (const possibleSmudge of possibleSmudges) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const smudgyNode = gg.getNode(possibleSmudge)!; - const originalNodeValue = smudgyNode.value.toString(); - smudgyNode.setValue(originalNodeValue.toString() === '.' ? '#' : '.'); - - const nextRowReflectionIndex = findRowReflection(gg); - const nextColumnReflectionIndex = findColumnReflection(gg); - - if ( - nextRowReflectionIndex === unsmudgedRowIndex && - nextColumnReflectionIndex === unsmudgedColumnIndex - ) { - smudgyNode.setValue(originalNodeValue); - - continue; - } - console.log( - 'smudge at', - possibleSmudge.toString(), - 'nextRowReflectionIndex', - nextRowReflectionIndex, - 'nextColumnReflectionIndex', - nextColumnReflectionIndex, - ); - - if ( - nextRowReflectionIndex !== undefined || - nextColumnReflectionIndex !== undefined - ) { - gg.print(); - smudge = possibleSmudge; - rowReflectionIndex = nextRowReflectionIndex; - columnReflectionIndex = nextColumnReflectionIndex; - smudgyNode.setValue(originalNodeValue); - break; - } - smudgyNode.setValue(originalNodeValue); - } - - if (!smudge) { - columnReflectionIndex = unsmudgedColumnIndex; - rowReflectionIndex = unsmudgedRowIndex; - - // throw new Error('Should be exactly 1 smudge'); - } - console.log('!!!!!!!!! Smudge found! (examp1, 0,0 and 1,4)', smudge?.toString()); + .map((graph) => { + const smudgyRowIndex = findReflection(graph, 'row'); + const smudgyColumnIndex = findReflection(graph, 'column'); + const rowIndex = findReflection(graph, 'row', 1, smudgyRowIndex); + const columnIndex = findReflection(graph, 'column', 1, smudgyColumnIndex); - console.log('verticalReflectionIndex', columnReflectionIndex); - console.log('rowReflectionIndex', rowReflectionIndex); let value = 0; - if (columnReflectionIndex !== undefined) { - value = columnReflectionIndex + 1; + if (columnIndex !== undefined) { + value = columnIndex + 1; } - if (rowReflectionIndex !== undefined) { - value = (rowReflectionIndex + 1) * 100; + if (rowIndex !== undefined) { + value = (rowIndex + 1) * 100; } return value; }) .sum(); }; -await task(p2, packageJson.aoc); // 0 ~0ms -// too high 43113 +await task(p2, packageJson.aoc); // 40995 ~658.23ms diff --git a/solutions/typescript/2023/13/src/parse.ts b/solutions/typescript/2023/13/src/parse.ts deleted file mode 100644 index fa8f13208..000000000 --- a/solutions/typescript/2023/13/src/parse.ts +++ /dev/null @@ -1,132 +0,0 @@ -export interface SpringLog { - log: string; - criteria: number[]; -} - -export const parse = (input: string): SpringLog[] => { - return input.lines(false).map((line) => { - const [log, critRaw] = line.splitIntoStringPair(' '); - const criteria = critRaw.splitToInt({ delimiter: /,/ }); - return { - log, - criteria, - }; - }); -}; - -export interface State { - rebuiltLog: string; - currentCriteria: number; - currentOriginalCriteria: number; - remainingDamagedLog: string; - remainingCriteria: number[]; -} - -export const calculateVariations = ( - state: State, - cache: Map = new Map(), -): number => { - const key = - state.remainingCriteria.join(',') + - ';' + - state.remainingDamagedLog + - ';' + - state.currentOriginalCriteria + - ';' + - state.currentCriteria; - - const cachedResult = cache.get(key); - if (cachedResult !== undefined) { - return cachedResult; - } - - let result = cachedResult ?? 0; - - while (state.currentCriteria > 0 && state.remainingDamagedLog.startsWith('#')) { - state.currentCriteria = state.currentCriteria - 1; - state.remainingDamagedLog = state.remainingDamagedLog.slice(1); - state.rebuiltLog = state.rebuiltLog + '#'; - } - - if ( - state.currentCriteria === 0 && - state.remainingCriteria.length === 0 && - !state.remainingDamagedLog.includes('#') - ) { - state.rebuiltLog += state.remainingDamagedLog.replaceAll('?', '.'); - return 1; - } - - while ( - state.currentCriteria === state.currentOriginalCriteria && - state.remainingDamagedLog.startsWith('.') - ) { - state.remainingDamagedLog = state.remainingDamagedLog.slice(1); - state.rebuiltLog = state.rebuiltLog + '.'; - } - - if (state.currentCriteria > 0 && state.remainingDamagedLog.startsWith('.')) { - return 0; - } - - if (state.currentCriteria === 0 && state.remainingDamagedLog.startsWith('#')) { - return 0; - } - - if ( - state.currentCriteria === 0 && - (state.remainingDamagedLog.startsWith('.') || state.remainingDamagedLog.startsWith('?')) - ) { - const nextCriteria = state.remainingCriteria.shift(); - - if (nextCriteria === undefined) { - state.currentOriginalCriteria = 0; - } else { - state.currentCriteria = nextCriteria; - state.currentOriginalCriteria = nextCriteria; - } - - state.rebuiltLog = state.rebuiltLog + '.'; - state.remainingDamagedLog = state.remainingDamagedLog.slice(1); - } - - while ( - state.currentCriteria === state.currentOriginalCriteria && - state.remainingDamagedLog.startsWith('.') - ) { - state.remainingDamagedLog = state.remainingDamagedLog.slice(1); - state.rebuiltLog = state.rebuiltLog + '.'; - } - - if ( - state.currentCriteria === 0 && - state.remainingDamagedLog.length === 0 && - state.remainingCriteria.length === 0 - ) { - return 1; - } - - if (state.remainingDamagedLog.startsWith('?')) { - result += calculateVariations( - { - ...state, - remainingCriteria: [...state.remainingCriteria], - remainingDamagedLog: '#' + state.remainingDamagedLog.slice(1), - }, - cache, - ); - - result += calculateVariations( - { - ...state, - remainingCriteria: [...state.remainingCriteria], - remainingDamagedLog: '.' + state.remainingDamagedLog.slice(1), - }, - cache, - ); - } else if (state.currentCriteria > 0 && state.remainingDamagedLog.length > 0) { - result += calculateVariations(state, cache); - } - cache.set(key, result); - return result; -}; diff --git a/solutions/typescript/libs/lib/src/math/common/interval.class.ts b/solutions/typescript/libs/lib/src/math/common/interval.class.ts index 5657ec8ca..e65665e31 100644 --- a/solutions/typescript/libs/lib/src/math/common/interval.class.ts +++ b/solutions/typescript/libs/lib/src/math/common/interval.class.ts @@ -745,6 +745,12 @@ export class Interval implements IntervalLike, IntervalQualifier { }); } + closestEndTo(to: number): number { + const ld = Math.abs(to - this.low); + const hd = Math.abs(this.high - to); + return ld < hd ? this.low : this.high; + } + toString(): string { return `${this.lowQualifier === INTERVAL_ENDPOINT_OPEN_QUALIFIER ? '(' : '['}${this.low},${ this.high diff --git a/solutions/typescript/libs/lib/src/model/graph/grid-graph.class.ts b/solutions/typescript/libs/lib/src/model/graph/grid-graph.class.ts index cec724d5d..ce3a38e25 100644 --- a/solutions/typescript/libs/lib/src/model/graph/grid-graph.class.ts +++ b/solutions/typescript/libs/lib/src/model/graph/grid-graph.class.ts @@ -108,7 +108,7 @@ export class GridGraph = } /** - * + * TODO: Make this lazy, make this.nodes private and only allow operations through functions, invalidate the cached bb when a nodes key is changed * @returns two Vec2s that are not necessarily point to a node but all the nodes are between */ public boundingBox(): BoundingBox { diff --git a/solutions/typescript/readme.md b/solutions/typescript/readme.md index c8b7df886..22cc598bc 100644 --- a/solutions/typescript/readme.md +++ b/solutions/typescript/readme.md @@ -20,7 +20,7 @@ | [Day 10](/solutions/typescript/2023/10/) | [68.44ms](/solutions/typescript/2023/10/src/p1.ts) | [1.4s](/solutions/typescript/2023/10/src/p2.ts) | | [Day 11](/solutions/typescript/2023/11/) | [104.54ms](/solutions/typescript/2023/11/src/p1.ts) | [104.48ms](/solutions/typescript/2023/11/src/p2.ts) | | [Day 12](/solutions/typescript/2023/12/) | [30.83ms](/solutions/typescript/2023/12/src/p1.ts) | [539.06ms](/solutions/typescript/2023/12/src/p2.ts) | -| [Day 13](/solutions/typescript/2023/13/) | [?ms](/solutions/typescript/2023/13/src/p1.ts) | [?ms](/solutions/typescript/2023/13/src/p2.ts) | +| [Day 13](/solutions/typescript/2023/13/) | [371.96ms](/solutions/typescript/2023/13/src/p1.ts) | [658.23ms](/solutions/typescript/2023/13/src/p2.ts) | | [Day 14](/solutions/typescript/2023/14/) | [?ms](/solutions/typescript/2023/14/src/p1.ts) | [?ms](/solutions/typescript/2023/14/src/p2.ts) | | [Day 15](/solutions/typescript/2023/15/) | [0.19ms](/solutions/typescript/2023/15/src/p1.ts) | [0.65ms](/solutions/typescript/2023/15/src/p2.ts) | | [Day 16](/solutions/typescript/2023/16/) | [31.37ms](/solutions/typescript/2023/16/src/p1.ts) | [\~1s](/solutions/typescript/2023/16/src/p2.ts) |