UNPKG

aoc-copilot

Version:

Advent of Code automatic runner for examples and inputs

205 lines 10.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.defaultSearchStrategy = defaultSearchStrategy; exports.getExamples = getExamples; const promises_1 = require("node:fs/promises"); const node_path_1 = require("node:path"); const fx = __importStar(require("./fx")); async function getExamples(year, day, part1only, $, addDb, addTests = []) { const examples = []; let internalError = false; try { const egdbFilename = (0, node_path_1.normalize)((0, node_path_1.join)(__dirname, '..', 'egdb', year.toString(), `${day}.json`)); const egdb = addDb || JSON.parse(await (0, promises_1.readFile)(egdbFilename, { encoding: 'utf-8' })); // User-supplied DB takes precedence if (egdb.inputs.indexes.length !== egdb.answers.indexesOrLiterals.length) { internalError = true; throw new Error(`Inconsistency detected in egdb: lengths of inputs.indexes and answers.indexes differs for year ${year} day ${day}`); } if (egdb.additionalInfos && egdb.inputs.indexes.length !== egdb.additionalInfos.indexes.length) { internalError = true; throw new Error(`Inconsistency detected in egdb: lengths of inputs.indexes and additionalInfos.indexes differs for year ${year} day ${day}`); } const inputs = $(egdb.inputs.selector); const answers = egdb.answers.selector === egdb.inputs.selector ? inputs : $(egdb.answers.selector); const additionalInfos = egdb.additionalInfos ? egdb.additionalInfos.selector === egdb.inputs.selector ? inputs : egdb.additionalInfos.selector === egdb.answers.selector ? answers : $(egdb.additionalInfos.selector) : undefined; egdb.inputs.indexes.filter((v, i) => !part1only || i < egdb.part1length).forEach((inputIndex, i) => { examples.push({ part: i < egdb.part1length ? 1 : 2, inputs: (() => { let result = []; result = typeof inputIndex === 'number' ? inputs.eq(inputIndex).html()?.includes('<br') ? inputs.eq(inputIndex).html().split('<br>') : inputs.eq(inputIndex).text().split('\n') : inputIndex.reduce((pv, cv) => (pv.push(...inputs.eq(cv).text().split('\n')), pv), []); const transform = egdb.inputs.transforms?.find(tx => tx.appliesTo.includes(i)); if (!!transform) { const output = fx.interpolate(result, transform.functions); if (!(Array.isArray(output) && output.every(row => typeof row === 'string'))) { internalError = true; throw new Error('Transformation did not return a string[]'); } result = output; } return result; })(), answer: (() => { let result = ''; if (typeof egdb.answers.indexesOrLiterals[i] === 'string') { result = egdb.answers.indexesOrLiterals[i]; } else { let interim = typeof egdb.answers.indexesOrLiterals[i] === 'number' ? answers.eq(egdb.answers.indexesOrLiterals[i]).text() : egdb.answers.indexesOrLiterals[i].reduce((pv, cv) => { pv.push(answers.eq(cv).text()); return pv; }, []); const transform = egdb.answers.transforms?.find(tx => tx.appliesTo.includes(i)); if (!!transform) { const output = fx.interpolate(interim, transform.functions); if (typeof output !== 'string') { internalError = true; throw new Error('Transformation did not return a string'); } result = output; } else if (typeof interim === 'string') result = interim; else { internalError = true; throw new Error(`No tranformations specified but answers.indexesOrLiterals is an array`); } } if (result.includes('\n')) { const answers = result.split('\n'); while (answers.at(-1) == '') answers.pop(); return answers.join('\n'); } else return result; })(), ...(egdb.additionalInfos) && { additionalInfo: { [egdb.additionalInfos.key]: (() => { let result = additionalInfos.eq(egdb.additionalInfos.indexes[i]).text(); const transform = egdb.additionalInfos.transforms?.find(tx => tx.appliesTo.includes(i)); if (!!transform) { const output = fx.interpolate(result, transform.functions); if (typeof output !== 'string') { internalError = true; throw new Error('Transformation did not return a string'); } result = output; } return result; })() } } }); }); } catch (err) { // Look for "no-examples" file which is useful when the default search strategy works fine // for part 1 but there is no part 2 example. let noExamples = []; try { const egdbFilename = (0, node_path_1.normalize)((0, node_path_1.join)(__dirname, '..', 'egdb', year.toString(), `no-examples.json`)); noExamples = JSON.parse(await (0, promises_1.readFile)(egdbFilename, { encoding: 'utf-8' }))[day.toString()] ?? []; } catch { } if (internalError) throw err; const dss = defaultSearchStrategy($); const inputs = dss.inputs.split('\n'); if (noExamples.indexOf(1) === -1 && dss.answer1 !== '') { examples.push({ part: 1, inputs, answer: dss.answer1 }); } if (day != 25 && !part1only && noExamples.indexOf(2) === -1 && dss.answer2 !== '') { examples.push({ part: 2, inputs, answer: dss.answer2 }); } } for (const test of addTests) examples.push(test); for (const eg of examples) while (eg.inputs.length > 1 && eg.inputs.at(-1) === '') eg.inputs.pop(); return examples; } function defaultSearchStrategy($) { // Try to find the example input, checking first for a "larger example" or a "complex example". // larger example: 2019: 5, 18, 20; 2020: 10, 24; 2021: 8, 12, 18, 22; 2022: 9, 18; 2023: 10; 2024: 10, 12, 24 // complex example: 2019: 18; 2022: 24 let inputElements; const larger = $("p:contains('larger example'),p:contains('complex example') + pre code"); for (let i = 0; i < larger.length; ++i) { if (larger.eq(i).parent().prev('p').text().endsWith('larger example:') || larger.eq(i).parent().prev('p').text().endsWith('complex example:')) { inputElements = larger.eq(i); break; } } // If no larger/complex example was found then look next for the <pre><code> block that follows "xample". if (inputElements === undefined) inputElements = $("p:contains('xample'):contains(':')").nextAll("pre").find('code'); // If no example input was still found then just return the first <pre><code> block. if (inputElements.length === 0) inputElements = $('article pre code').first(); const br = inputElements.eq(0).html()?.includes('<br'); const inputs = br ? inputElements.eq(0).html().replaceAll('<br>', '\n') : inputElements.eq(0).text(); // Return the highest scoring potential answer element: // +1 if it's all numeric // +1 if the previous element is null const bestAnswer = (elements) => { return elements.length < 2 ? elements : elements.eq([...Array(elements.length).keys()].reduce((pv, cv) => { const bestScore = (/^\d+$/.test(elements.eq(pv).text()) ? 1 : 0) + (elements[pv].prev === null ? 1 : 0); const thisScore = (/^\d+$/.test(elements.eq(cv).text()) ? 1 : 0) + (elements[cv].prev === null ? 1 : 0); return thisScore >= bestScore ? cv : pv; })); }; const answer1 = bestAnswer($(`article:first p:has(em,code)`).find("em,code").find("em,code")).text(); const answer2 = bestAnswer($(`article:last p:has(em,code)`).find("em,code").find("em,code")).text(); return { inputs, answer1, answer2 }; } //# sourceMappingURL=examples.js.map