diff --git a/.github/badges/typescript/2023.json b/.github/badges/typescript/2023.json index 0c72321dc..63e376822 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": "11/25", + "message": "12/25", "color": "orange" } diff --git a/solutions/typescript/2023/12/src/p1.ts b/solutions/typescript/2023/12/src/p1.ts index 1a5303e35..c25f18297 100644 --- a/solutions/typescript/2023/12/src/p1.ts +++ b/solutions/typescript/2023/12/src/p1.ts @@ -1,17 +1,22 @@ -import { cartesianCombinations, task } from '@alexaegis/advent-of-code-lib'; +import { task } from '@alexaegis/advent-of-code-lib'; import packageJson from '../package.json'; -import { parse } from './parse.js'; - -function generateVariations(damagedLog: string, criteria: RegExp): string[] { - const variants = [...damagedLog].map((entry) => (entry === '?' ? ['#', '.'] : [entry])); - return cartesianCombinations(...variants) - .map((combination) => combination.join('')) - .filter((combination) => criteria.test(combination)); -} +import { calculateVariations, parse } from './parse.js'; export const p1 = (input: string): number => parse(input) - .map((spring) => generateVariations(spring.log, spring.criteriaRegExp).length) + .map((entry) => { + const [currentCriteria, ...remainingCriteria] = entry.criteria; + if (!currentCriteria) { + throw new Error('no criteria for line'); + } + return calculateVariations({ + currentCriteria, + currentOriginalCriteria: currentCriteria, + rebuiltLog: '', + remainingCriteria, + remainingDamagedLog: entry.log, + }); + }) .sum(); -await task(p1, packageJson.aoc); // 7118 ~0ms +await task(p1, packageJson.aoc); // 7118 ~30.83ms diff --git a/solutions/typescript/2023/12/src/p2.ts b/solutions/typescript/2023/12/src/p2.ts index 730bca95f..48a0b01d4 100644 --- a/solutions/typescript/2023/12/src/p2.ts +++ b/solutions/typescript/2023/12/src/p2.ts @@ -1,120 +1,6 @@ import { task } from '@alexaegis/advent-of-code-lib'; import packageJson from '../package.json'; -import { parse, type SpringLog } from './parse.js'; - -interface State { - rebuiltLog: string; - currentCriteria: number; - currentOriginalCriteria: number; - remainingDamagedLog: string; - remainingCriteria: number[]; -} - -const calculateVariations = (state: State, cache: 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; -}; +import { calculateVariations, parse, type SpringLog } from './parse.js'; const unfoldEntry = (times: number) => @@ -134,17 +20,14 @@ export const p2 = (input: string): number => if (!currentCriteria) { throw new Error('no criteria for line'); } - return calculateVariations( - { - currentCriteria, - currentOriginalCriteria: currentCriteria, - rebuiltLog: '', - remainingCriteria, - remainingDamagedLog: entry.log, - }, - new Map(), - ); + return calculateVariations({ + currentCriteria, + currentOriginalCriteria: currentCriteria, + rebuiltLog: '', + remainingCriteria, + remainingDamagedLog: entry.log, + }); }) .sum(); -await task(p2, packageJson.aoc); // 7030194981795 ~0ms +await task(p2, packageJson.aoc); // 7030194981795 ~539.06ms diff --git a/solutions/typescript/2023/12/src/parse.ts b/solutions/typescript/2023/12/src/parse.ts index 221557c5e..fa8f13208 100644 --- a/solutions/typescript/2023/12/src/parse.ts +++ b/solutions/typescript/2023/12/src/parse.ts @@ -1,25 +1,132 @@ export interface SpringLog { - /** - * `?` unknown - * `.` operational - * `#` damaged - */ log: string; criteria: number[]; - criteriaRegExp: RegExp; } -export const criteriaToRegExp = (criteria: number[]): RegExp => - new RegExp('^\\.*' + criteria.map((brokenCount) => `#{${brokenCount}}`).join('\\.+') + '\\.*$'); - export const parse = (input: string): SpringLog[] => { return input.lines(false).map((line) => { const [log, critRaw] = line.splitIntoStringPair(' '); - const crit = critRaw.splitToInt({ delimiter: /,/ }); + const criteria = critRaw.splitToInt({ delimiter: /,/ }); return { log, - criteria: crit, - criteriaRegExp: criteriaToRegExp(crit), + 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/readme.md b/solutions/typescript/readme.md index 3a87968e8..8286e5ad7 100644 --- a/solutions/typescript/readme.md +++ b/solutions/typescript/readme.md @@ -19,7 +19,7 @@ | [Day 9](/solutions/typescript/2023/09/) | [1.57ms](/solutions/typescript/2023/09/src/p1.ts) | [1.62ms](/solutions/typescript/2023/09/src/p2.ts) | | [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 | - | - | +| [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 | - | - | | Day 14 | - | - | | Day 15 | - | - |