Skip to content

Commit

Permalink
feat: solved 2023 day 12 part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexAegis committed Dec 13, 2023
1 parent e75a7bf commit 9d32174
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 63 deletions.
1 change: 1 addition & 0 deletions resources/2023/12/example.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#??????.??.??? 4,1,1,1
25 changes: 2 additions & 23 deletions solutions/typescript/2023/12/src/p1.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,17 @@
import { cartesianCombinations, task } from '@alexaegis/advent-of-code-lib';
import packageJson from '../package.json';

interface SprintLog {
/**
* `?` unknown
* `.` operational
* `#` damaged
*/
log: string;
criteria: number[];
criteriaRegExp: RegExp;
}

export const criteriaToRegExp = (criteria: number[]): RegExp =>
new RegExp('^\\.*' + criteria.map((brokenCount) => `#{${brokenCount}}`).join('\\.+') + '\\.*$');
import { parse } from './parse.js';

function generateVariations(damagedLog: string, criteria: RegExp): string[] {
const variants = [...damagedLog].map((entry) => (entry === '?' ? ['#', '.'] : [entry]));
// const variationCount = variants.map((ev) => ev.length).product();
return cartesianCombinations(...variants)
.map((combination) => combination.join(''))
.filter((combination) => criteria.test(combination));
}

export const p1 = (input: string): number =>
input
.lines(false)
.map((line) => {
const [log, critRaw] = line.splitIntoStringPair(' ');
const crit = critRaw.splitToInt({ delimiter: /,/ });
return { log, criteria: crit, criteriaRegExp: criteriaToRegExp(crit) } as SprintLog;
})
parse(input)
.map((spring) => generateVariations(spring.log, spring.criteriaRegExp).length)

.sum();

await task(p1, packageJson.aoc); // 7118 ~0ms
179 changes: 139 additions & 40 deletions solutions/typescript/2023/12/src/p2.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,150 @@
import { task } from '@alexaegis/advent-of-code-lib';
import packageJson from '../package.json';
import { parse, type SpringLog } from './parse.js';

interface SprintLog {
/**
* `?` unknown
* `.` operational
* `#` damaged
*/
log: string;
criteria: number[];
criteriaRegExp: RegExp;
interface State {
rebuiltLog: string;
currentCriteria: number;
currentOriginalCriteria: number;
remainingDamagedLog: string;
remainingCriteria: number[];
}

export const criteriaToRegExp = (criteria: number[]): RegExp =>
new RegExp('^\\.*' + criteria.map((brokenCount) => `#{${brokenCount}}`).join('\\.+') + '\\.*$');
const calculateVariations = (state: State, cache: Map<string, number>): number => {
const key =
state.remainingCriteria.join(',') +
';' +
state.remainingDamagedLog +
';' +
state.currentOriginalCriteria +
';' +
state.currentCriteria;

function calculateVariations(damagedLog: string, criteria: number[]): number {
const totalVariationCount = [...damagedLog].map((entry) => (entry === '?' ? 2 : 1)).product();
const minlen = criteria.sum() + criteria.length - 1;
// const variationCount = variants.map((ev) => ev.length).product();
return totalVariationCount ^ minlen;
}
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;
};

const unfoldEntry =
(times: number) =>
(entry: SpringLog): SpringLog => ({
...entry,
log: Array.from({ length: times })
.map(() => entry.log)
.join('?'),
criteria: Array.from({ length: times }).flatMap(() => entry.criteria),
});

export const p2 = (input: string): number =>
input
.lines(false)

.map((line) => {
console.log(line);
const [log, critRaw] = line.splitIntoStringPair(' ');
const crit = critRaw.splitToInt({ delimiter: /,/ });
//crit = Array.from({ length: 5 }).flatMap(() => crit);
const multipliedLog = log;
//multipliedLog = Array.from({ length: 5 })
// .map(() => log)
// .join('?');
return {
log: multipliedLog,
criteria: crit,
criteriaRegExp: criteriaToRegExp(crit),
} as SprintLog;
parse(input)
.map(unfoldEntry(5))
.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,
},
new Map(),
);
})
.tap((a) => {
console.log(a);
})
.map((spring) => calculateVariations(spring.log, spring.criteria))

.sum();

await task(p2, packageJson.aoc); // 7118 ~0ms
await task(p2, packageJson.aoc); // 7030194981795 ~0ms
25 changes: 25 additions & 0 deletions solutions/typescript/2023/12/src/parse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
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<SpringLog>((line) => {
const [log, critRaw] = line.splitIntoStringPair(' ');
const crit = critRaw.splitToInt({ delimiter: /,/ });
return {
log,
criteria: crit,
criteriaRegExp: criteriaToRegExp(crit),
};
});
};

0 comments on commit 9d32174

Please sign in to comment.