From 671bf8a814e079718a71fa6f1d2409d4a8a65cb1 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Thu, 9 Apr 2026 21:50:41 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20Switch=20to=20`=3Fraw`=20imports?= =?UTF-8?q?=20for=20advents=20(#6830)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the function-returning-function pattern in buggy.mjs files with direct exports, and use ?raw imports to get the source code as text for StackBlitz sandboxes and downloads. This eliminates the need for the String(fn) + regex extraction workaround in AdventOfTheDayBuilder. https://claude.ai/code/session_016asJDuBocZ76J4KQrxy8A9 --------- Co-authored-by: Claude Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Nicolas DUBIEN --- .../AdventOfTheDay.tsx | 4 +- .../AdventOfTheDayBuilder.tsx | 26 +-- .../AdventPlayground.tsx | 4 +- .../2024-12-01-advent-of-pbt-day-1/buggy.mjs | 22 ++- .../AdventOfTheDay.tsx | 4 +- .../2024-12-02-advent-of-pbt-day-2/buggy.mjs | 32 ++-- .../AdventOfTheDay.tsx | 4 +- .../2024-12-03-advent-of-pbt-day-3/buggy.mjs | 106 ++++++------- .../AdventOfTheDay.tsx | 4 +- .../2024-12-04-advent-of-pbt-day-4/buggy.mjs | 86 +++++----- .../AdventOfTheDay.tsx | 4 +- .../2024-12-05-advent-of-pbt-day-5/buggy.mjs | 30 ++-- .../AdventOfTheDay.tsx | 4 +- .../2024-12-06-advent-of-pbt-day-6/buggy.mjs | 148 +++++++++--------- .../AdventOfTheDay.tsx | 4 +- .../2024-12-07-advent-of-pbt-day-7/buggy.mjs | 70 ++++----- .../AdventOfTheDay.tsx | 4 +- .../2024-12-08-advent-of-pbt-day-8/buggy.mjs | 62 ++++---- .../AdventOfTheDay.tsx | 4 +- .../2024-12-09-advent-of-pbt-day-9/buggy.mjs | 26 ++- .../AdventOfTheDay.tsx | 4 +- .../2024-12-10-advent-of-pbt-day-10/buggy.mjs | 14 +- .../AdventOfTheDay.tsx | 4 +- .../2024-12-11-advent-of-pbt-day-11/buggy.mjs | 104 ++++++------ .../AdventOfTheDay.tsx | 4 +- .../2024-12-12-advent-of-pbt-day-12/buggy.mjs | 90 ++++++----- .../AdventOfTheDay.tsx | 8 +- .../2024-12-13-advent-of-pbt-day-13/buggy.mjs | 44 +++--- .../AdventOfTheDay.tsx | 8 +- .../2024-12-14-advent-of-pbt-day-14/buggy.mjs | 84 +++++----- .../AdventOfTheDay.tsx | 6 +- .../2024-12-15-advent-of-pbt-day-15/buggy.mjs | 66 ++++---- .../AdventOfTheDay.tsx | 8 +- .../2024-12-16-advent-of-pbt-day-16/buggy.mjs | 14 +- .../AdventOfTheDay.tsx | 4 +- .../2024-12-17-advent-of-pbt-day-17/buggy.mjs | 16 +- .../AdventOfTheDay.tsx | 4 +- .../2024-12-18-advent-of-pbt-day-18/buggy.mjs | 66 ++++---- .../AdventOfTheDay.tsx | 4 +- .../2024-12-19-advent-of-pbt-day-19/buggy.mjs | 40 +++-- .../AdventOfTheDay.tsx | 4 +- .../2024-12-20-advent-of-pbt-day-20/buggy.mjs | 58 ++++--- .../AdventOfTheDay.tsx | 4 +- .../2024-12-21-advent-of-pbt-day-21/buggy.mjs | 110 +++++++------ .../AdventOfTheDay.tsx | 4 +- .../2024-12-22-advent-of-pbt-day-22/buggy.mjs | 48 +++--- .../AdventOfTheDay.tsx | 8 +- .../2024-12-23-advent-of-pbt-day-23/buggy.mjs | 64 ++++---- .../AdventOfTheDay.tsx | 8 +- .../2024-12-24-advent-of-pbt-day-24/buggy.mjs | 84 +++++----- website/src/types/raw.d.ts | 4 + 51 files changed, 816 insertions(+), 820 deletions(-) create mode 100644 website/src/types/raw.d.ts diff --git a/website/blog/2024-12-01-advent-of-pbt-day-1/AdventOfTheDay.tsx b/website/blog/2024-12-01-advent-of-pbt-day-1/AdventOfTheDay.tsx index 78972e52..a4ce54c8 100644 --- a/website/blog/2024-12-01-advent-of-pbt-day-1/AdventOfTheDay.tsx +++ b/website/blog/2024-12-01-advent-of-pbt-day-1/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from './AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 1, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: sortLetters, parser, placeholderForm: 'Anne=15\nPaul=38\nElena=81', diff --git a/website/blog/2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder.tsx b/website/blog/2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder.tsx index 568f552c..b743628d 100644 --- a/website/blog/2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder.tsx +++ b/website/blog/2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder.tsx @@ -7,7 +7,8 @@ const answerFieldName = 'answer'; type Options = { day: number; - buildBuggyAdvent: () => (...args: any[]) => unknown; + buggyAdvent: (...args: any[]) => unknown; + snippet: string; buggyAdventSurcharged?: (...args: any[]) => unknown; referenceAdvent: (...args: any[]) => unknown; postAdvent?: (adventOutput: any) => unknown; @@ -18,13 +19,11 @@ type Options = { signatureExtras?: string[]; }; -// When minified for publish, the value of String(buildBuggyAdvent) is: "function(){return function(e){return[...e].sort(((e,t)=>e.age-t.age||e.name.codePointAt(0)-t.name.codePointAt(0)))}}" -const coreCodeExtractorRegex = /^function(\s+[^(]+)?\([^)]*\)\s*{(.*)}$/ms; - export function buildAdventOfTheDay(options: Options) { const { day, - buildBuggyAdvent, + buggyAdvent, + snippet, buggyAdventSurcharged, referenceAdvent, postAdvent = (v) => v, @@ -34,15 +33,6 @@ export function buildAdventOfTheDay(options: Options) { signature, signatureExtras, } = options; - - const originalSource = String(buildBuggyAdvent).trim(); - const m = coreCodeExtractorRegex.exec(originalSource); - if (m === null) { - throw new Error( - `Unable to parse the snippet for the advent of code properly, original source code being:\n\n${JSON.stringify(originalSource)}`, - ); - } - const snippet = m[2].replace(/return /, 'export default '); function AdventPlaygroundOfTheDay() { return ( String.fromCharCode(byte)).join(''), + )}`} > here diff --git a/website/blog/2024-12-01-advent-of-pbt-day-1/buggy.mjs b/website/blog/2024-12-01-advent-of-pbt-day-1/buggy.mjs index 5ca04979..b98f9777 100644 --- a/website/blog/2024-12-01-advent-of-pbt-day-1/buggy.mjs +++ b/website/blog/2024-12-01-advent-of-pbt-day-1/buggy.mjs @@ -1,16 +1,14 @@ // @ts-check -export default function advent() { - /** @typedef {{name: string; age: number;}} Letter */ +/** @typedef {{name: string; age: number;}} Letter */ - /** - * @param {Letter[]} letters - * @returns {Letter[]} - */ - return function sortLetters(letters) { - const clonedLetters = [...letters]; - return clonedLetters.sort( - (la, lb) => la.age - lb.age || (la.name.codePointAt(0) ?? 0) - (lb.name.codePointAt(0) ?? 0), - ); - }; +/** + * @param {Letter[]} letters + * @returns {Letter[]} + */ +export default function sortLetters(letters) { + const clonedLetters = [...letters]; + return clonedLetters.sort( + (la, lb) => la.age - lb.age || (la.name.codePointAt(0) ?? 0) - (lb.name.codePointAt(0) ?? 0), + ); } diff --git a/website/blog/2024-12-02-advent-of-pbt-day-2/AdventOfTheDay.tsx b/website/blog/2024-12-02-advent-of-pbt-day-2/AdventOfTheDay.tsx index cf9496e1..7799c8ac 100644 --- a/website/blog/2024-12-02-advent-of-pbt-day-2/AdventOfTheDay.tsx +++ b/website/blog/2024-12-02-advent-of-pbt-day-2/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 2, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: dropLettersFromDuplicatedSenders, parser, placeholderForm: '"first-id"\n"second-id-with\\xA0fancy\\ncharacters\\u{1f431}"\n"with escaped \\" double quotes"', diff --git a/website/blog/2024-12-02-advent-of-pbt-day-2/buggy.mjs b/website/blog/2024-12-02-advent-of-pbt-day-2/buggy.mjs index 85394dd4..aa6bafaa 100644 --- a/website/blog/2024-12-02-advent-of-pbt-day-2/buggy.mjs +++ b/website/blog/2024-12-02-advent-of-pbt-day-2/buggy.mjs @@ -1,21 +1,19 @@ // @ts-check -export default function advent() { - /** @typedef {{id:string;}} Letter */ +/** @typedef {{id:string;}} Letter */ - /** - * @param {Letter[]} letters - * @returns {Letter[]} - */ - return function dropLettersFromDuplicatedSenders(letters) { - /** @type {Record} */ - const alreadySeenIds = {}; - return letters.filter((letter) => { - if (alreadySeenIds[letter.id]) { - return false; - } - alreadySeenIds[letter.id] = true; - return true; - }); - }; +/** + * @param {Letter[]} letters + * @returns {Letter[]} + */ +export default function dropLettersFromDuplicatedSenders(letters) { + /** @type {Record} */ + const alreadySeenIds = {}; + return letters.filter((letter) => { + if (alreadySeenIds[letter.id]) { + return false; + } + alreadySeenIds[letter.id] = true; + return true; + }); } diff --git a/website/blog/2024-12-03-advent-of-pbt-day-3/AdventOfTheDay.tsx b/website/blog/2024-12-03-advent-of-pbt-day-3/AdventOfTheDay.tsx index 84db590f..f7c1073f 100644 --- a/website/blog/2024-12-03-advent-of-pbt-day-3/AdventOfTheDay.tsx +++ b/website/blog/2024-12-03-advent-of-pbt-day-3/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 3, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: isWordIncludedInLetter, parser, placeholderForm: '"content of the letter"\n"word"', diff --git a/website/blog/2024-12-03-advent-of-pbt-day-3/buggy.mjs b/website/blog/2024-12-03-advent-of-pbt-day-3/buggy.mjs index cd9c4edc..b8d949fe 100644 --- a/website/blog/2024-12-03-advent-of-pbt-day-3/buggy.mjs +++ b/website/blog/2024-12-03-advent-of-pbt-day-3/buggy.mjs @@ -1,64 +1,62 @@ // @ts-check -export default function advent() { - // Implementation copied from https://github.com/trekhleb/javascript-algorithms/pull/110/ - const PRIME = 97; +// Implementation copied from https://github.com/trekhleb/javascript-algorithms/pull/110/ +const PRIME = 97; - /** - * @param {string} letterContent - * @param {string} word - * @return {boolean} - */ - return function isWordIncludedInLetter(letterContent, word) { - const wordHash = hashWord(word); - let prevSegment = null; - let currentSegmentHash = null; - for (let charIndex = 0; charIndex <= letterContent.length - word.length; charIndex += 1) { - const currentSegment = letterContent.substring(charIndex, charIndex + word.length); - if (currentSegmentHash === null) { - currentSegmentHash = hashWord(currentSegment); - } else { - currentSegmentHash = reHashWord(currentSegmentHash, prevSegment ?? '', currentSegment); - } - prevSegment = currentSegment; - if (wordHash === currentSegmentHash) { - let numberOfMatches = 0; - for (let deepCharIndex = 0; deepCharIndex < word.length; deepCharIndex += 1) { - if (word[deepCharIndex] === letterContent[charIndex + deepCharIndex]) { - numberOfMatches += 1; - } - } - if (numberOfMatches === word.length) { - return true; +/** + * @param {string} letterContent + * @param {string} word + * @return {boolean} + */ +export default function isWordIncludedInLetter(letterContent, word) { + const wordHash = hashWord(word); + let prevSegment = null; + let currentSegmentHash = null; + for (let charIndex = 0; charIndex <= letterContent.length - word.length; charIndex += 1) { + const currentSegment = letterContent.substring(charIndex, charIndex + word.length); + if (currentSegmentHash === null) { + currentSegmentHash = hashWord(currentSegment); + } else { + currentSegmentHash = reHashWord(currentSegmentHash, prevSegment ?? '', currentSegment); + } + prevSegment = currentSegment; + if (wordHash === currentSegmentHash) { + let numberOfMatches = 0; + for (let deepCharIndex = 0; deepCharIndex < word.length; deepCharIndex += 1) { + if (word[deepCharIndex] === letterContent[charIndex + deepCharIndex]) { + numberOfMatches += 1; } } + if (numberOfMatches === word.length) { + return true; + } } - return false; - }; - - /** - * @param {string} word - * @return {number} - */ - function hashWord(word) { - let hash = 0; - for (let charIndex = 0; charIndex < word.length; charIndex += 1) { - hash += word[charIndex].charCodeAt(0) * PRIME ** charIndex; - } - return hash; } + return false; +} - /** - * @param {number} prevHash - * @param {string} prevWord - * @param {string} newWord - * @return {number} - */ - function reHashWord(prevHash, prevWord, newWord) { - const newWordLastIndex = newWord.length - 1; - let newHash = prevHash - prevWord[0].charCodeAt(0); - newHash /= PRIME; - newHash += newWord[newWordLastIndex].charCodeAt(0) * PRIME ** newWordLastIndex; - return newHash; +/** + * @param {string} word + * @return {number} + */ +function hashWord(word) { + let hash = 0; + for (let charIndex = 0; charIndex < word.length; charIndex += 1) { + hash += word[charIndex].charCodeAt(0) * PRIME ** charIndex; } + return hash; +} + +/** + * @param {number} prevHash + * @param {string} prevWord + * @param {string} newWord + * @return {number} + */ +function reHashWord(prevHash, prevWord, newWord) { + const newWordLastIndex = newWord.length - 1; + let newHash = prevHash - prevWord[0].charCodeAt(0); + newHash /= PRIME; + newHash += newWord[newWordLastIndex].charCodeAt(0) * PRIME ** newWordLastIndex; + return newHash; } diff --git a/website/blog/2024-12-04-advent-of-pbt-day-4/AdventOfTheDay.tsx b/website/blog/2024-12-04-advent-of-pbt-day-4/AdventOfTheDay.tsx index bc2f5b77..1f664ff3 100644 --- a/website/blog/2024-12-04-advent-of-pbt-day-4/AdventOfTheDay.tsx +++ b/website/blog/2024-12-04-advent-of-pbt-day-4/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 4, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: fastPostOfficeFinderEmulator, postAdvent: (value) => Number(value) >= 0 && Number(value) <= 14, parser, diff --git a/website/blog/2024-12-04-advent-of-pbt-day-4/buggy.mjs b/website/blog/2024-12-04-advent-of-pbt-day-4/buggy.mjs index 1c54fbd2..94b5e416 100644 --- a/website/blog/2024-12-04-advent-of-pbt-day-4/buggy.mjs +++ b/website/blog/2024-12-04-advent-of-pbt-day-4/buggy.mjs @@ -1,51 +1,49 @@ // @ts-check -export default function advent() { - /** @typedef {{x: number; y: number;}} Position */ +/** @typedef {{x: number; y: number;}} Position */ - const SizeX = 10000; - const SizeY = 1000; +const SizeX = 10000; +const SizeY = 1000; - /** - * @param {Position} initialPosition - * @param {Position} targetPosition - * @returns {number} Number of moves to find the targetPosition - */ - return function fastPostOfficeFinderEmulator(initialPosition, targetPosition) { - let xMin = 0; - let xMax = SizeX; - let yMin = 0; - let yMax = SizeY; - let x = initialPosition.x; - let y = initialPosition.y; - let numMoves = 0; - while (x !== targetPosition.x || y !== targetPosition.y) { - if (xMin >= xMax || yMin >= yMax) { - return Number.POSITIVE_INFINITY; // error - } - const prevX = x; - const prevY = y; - if (targetPosition.y < y) { - yMax = y - 1; - y = Math.floor((yMax + yMin) / 2); - } else if (targetPosition.y > y) { - yMin = y + 1; - y = Math.floor((yMax + yMin) / 2); - } - if (targetPosition.x < x) { - xMax = x - 1; - x = Math.floor((xMax + xMin) / 2); - } else if (targetPosition.x > x) { - xMin = x + 1; - x = Math.floor((xMax + xMin) / 2); - } - if (prevX !== x || prevY !== y) { - ++numMoves; - if (numMoves > 1000) { - return Number.POSITIVE_INFINITY; // probably an error somewhere - } +/** + * @param {Position} initialPosition + * @param {Position} targetPosition + * @returns {number} Number of moves to find the targetPosition + */ +export default function fastPostOfficeFinderEmulator(initialPosition, targetPosition) { + let xMin = 0; + let xMax = SizeX; + let yMin = 0; + let yMax = SizeY; + let x = initialPosition.x; + let y = initialPosition.y; + let numMoves = 0; + while (x !== targetPosition.x || y !== targetPosition.y) { + if (xMin >= xMax || yMin >= yMax) { + return Number.POSITIVE_INFINITY; // error + } + const prevX = x; + const prevY = y; + if (targetPosition.y < y) { + yMax = y - 1; + y = Math.floor((yMax + yMin) / 2); + } else if (targetPosition.y > y) { + yMin = y + 1; + y = Math.floor((yMax + yMin) / 2); + } + if (targetPosition.x < x) { + xMax = x - 1; + x = Math.floor((xMax + xMin) / 2); + } else if (targetPosition.x > x) { + xMin = x + 1; + x = Math.floor((xMax + xMin) / 2); + } + if (prevX !== x || prevY !== y) { + ++numMoves; + if (numMoves > 1000) { + return Number.POSITIVE_INFINITY; // probably an error somewhere } } - return numMoves; - }; + } + return numMoves; } diff --git a/website/blog/2024-12-05-advent-of-pbt-day-5/AdventOfTheDay.tsx b/website/blog/2024-12-05-advent-of-pbt-day-5/AdventOfTheDay.tsx index fad05ece..5f49c488 100644 --- a/website/blog/2024-12-05-advent-of-pbt-day-5/AdventOfTheDay.tsx +++ b/website/blog/2024-12-05-advent-of-pbt-day-5/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 5, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: isSecurityKey, parser, placeholderForm: '6', diff --git a/website/blog/2024-12-05-advent-of-pbt-day-5/buggy.mjs b/website/blog/2024-12-05-advent-of-pbt-day-5/buggy.mjs index 1fb67ef6..1226dd5d 100644 --- a/website/blog/2024-12-05-advent-of-pbt-day-5/buggy.mjs +++ b/website/blog/2024-12-05-advent-of-pbt-day-5/buggy.mjs @@ -1,21 +1,19 @@ // @ts-check -export default function advent() { - /** - * @param {number} potentialSecurityKey - * @returns {boolean} - */ - return function isSecurityKey(potentialSecurityKey) { - let key = potentialSecurityKey; - const sqrtKey = Math.floor(Math.sqrt(key)); +/** + * @param {number} potentialSecurityKey + * @returns {boolean} + */ +export default function isSecurityKey(potentialSecurityKey) { + let key = potentialSecurityKey; + const sqrtKey = Math.floor(Math.sqrt(key)); - let numFactors = 0; - for (let i = 2; i <= sqrtKey; ++i) { - if (key % i === 0) { - ++numFactors; - key /= i; - } + let numFactors = 0; + for (let i = 2; i <= sqrtKey; ++i) { + if (key % i === 0) { + ++numFactors; + key /= i; } - return numFactors === 1 && key * key !== potentialSecurityKey; - }; + } + return numFactors === 1 && key * key !== potentialSecurityKey; } diff --git a/website/blog/2024-12-06-advent-of-pbt-day-6/AdventOfTheDay.tsx b/website/blog/2024-12-06-advent-of-pbt-day-6/AdventOfTheDay.tsx index f3cacae3..5d05a0f6 100644 --- a/website/blog/2024-12-06-advent-of-pbt-day-6/AdventOfTheDay.tsx +++ b/website/blog/2024-12-06-advent-of-pbt-day-6/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 6, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: nextBarcode, // same as adventBuggy but with unitPerNumerical=10, not buggy as 25**10 is far from MAX_SAFE_INTEGER parser, placeholderForm: '๐Ÿงบ๐ŸŽ', diff --git a/website/blog/2024-12-06-advent-of-pbt-day-6/buggy.mjs b/website/blog/2024-12-06-advent-of-pbt-day-6/buggy.mjs index c0b5546c..c73057bc 100644 --- a/website/blog/2024-12-06-advent-of-pbt-day-6/buggy.mjs +++ b/website/blog/2024-12-06-advent-of-pbt-day-6/buggy.mjs @@ -1,85 +1,83 @@ // @ts-check -export default function advent() { - /** - * @typedef {'\u{2709}\u{fe0f}'|'\u{1f9fa}'|'\u{1f384}'|'\u{1f514}'|'\u{1f56f}\u{fe0f}'|'\u{2b50}'|'\u{1f98c}'|'\u{26c4}'|'\u{1f6f7}'|'\u{2744}\u{fe0f}'|'\u{1f3bf}'|'\u{2728}'|'\u{1f929}'|'\u{1f973}'|'\u{1f388}'|'\u{1fa80}'|'\u{1f3ae}'|'\u{1f3b2}'|'\u{265f}\u{fe0f}'|'\u{1f49d}'|'\u{1f380}'|'\u{1f9e6}'|'\u{1f385}'|'\u{1f936}'|'\u{1f381}'} Unit - */ - /** - * @param {Unit[]} barcode - * @returns {Unit[]} - */ - return function nextBarcode(barcode) { - /** @type {Unit[]} */ - const units = [ - '\u{2709}\u{fe0f}', - '\u{1f9fa}', - '\u{1f384}', - '\u{1f514}', - '\u{1f56f}\u{fe0f}', - '\u{2b50}', - '\u{1f98c}', - '\u{26c4}', - '\u{1f6f7}', - '\u{2744}\u{fe0f}', - '\u{1f3bf}', - '\u{2728}', - '\u{1f929}', - '\u{1f973}', - '\u{1f388}', - '\u{1fa80}', - '\u{1f3ae}', - '\u{1f3b2}', - '\u{265f}\u{fe0f}', - '\u{1f49d}', - '\u{1f380}', - '\u{1f9e6}', - '\u{1f385}', - '\u{1f936}', - '\u{1f381}', - ]; - const base25 = '0123456789abcdefghijklmno'; +/** + * @typedef {'โœ‰๏ธ'|'๐Ÿงบ'|'๐ŸŽ„'|'๐Ÿ””'|'๐Ÿ•ฏ๏ธ'|'โญ'|'๐ŸฆŒ'|'โ›„'|'๐Ÿ›ท'|'โ„๏ธ'|'๐ŸŽฟ'|'โœจ'|'๐Ÿคฉ'|'๐Ÿฅณ'|'๐ŸŽˆ'|'๐Ÿช€'|'๐ŸŽฎ'|'๐ŸŽฒ'|'โ™Ÿ๏ธ'|'๐Ÿ’'|'๐ŸŽ€'|'๐Ÿงฆ'|'๐ŸŽ…'|'๐Ÿคถ'|'๐ŸŽ'} Unit + */ +/** + * @param {Unit[]} barcode + * @returns {Unit[]} + */ +export default function nextBarcode(barcode) { + /** @type {Unit[]} */ + const units = [ + 'โœ‰๏ธ', + '๐Ÿงบ', + '๐ŸŽ„', + '๐Ÿ””', + '๐Ÿ•ฏ๏ธ', + 'โญ', + '๐ŸฆŒ', + 'โ›„', + '๐Ÿ›ท', + 'โ„๏ธ', + '๐ŸŽฟ', + 'โœจ', + '๐Ÿคฉ', + '๐Ÿฅณ', + '๐ŸŽˆ', + '๐Ÿช€', + '๐ŸŽฎ', + '๐ŸŽฒ', + 'โ™Ÿ๏ธ', + '๐Ÿ’', + '๐ŸŽ€', + '๐Ÿงฆ', + '๐ŸŽ…', + '๐Ÿคถ', + '๐ŸŽ', + ]; + const base25 = '0123456789abcdefghijklmno'; - const unitPerNumerical = 12; - const maxForNumerical = units.length ** unitPerNumerical; - const numericalVersion = []; + const unitPerNumerical = 12; + const maxForNumerical = units.length ** unitPerNumerical; + const numericalVersion = []; - // Create numerical value for current - for (let i = barcode.length; i > 0; i -= unitPerNumerical) { - const unitsForNumerical = barcode.slice(Math.max(0, i - unitPerNumerical), i); - let numerical = 0; - for (const unit of unitsForNumerical) { - numerical *= units.length; - numerical += units.indexOf(unit); - } - numericalVersion.push(numerical); + // Create numerical value for current + for (let i = barcode.length; i > 0; i -= unitPerNumerical) { + const unitsForNumerical = barcode.slice(Math.max(0, i - unitPerNumerical), i); + let numerical = 0; + for (const unit of unitsForNumerical) { + numerical *= units.length; + numerical += units.indexOf(unit); } + numericalVersion.push(numerical); + } - // Compute next numerical value - let nextNumericalVersion = [...numericalVersion, 0]; - let cursorInNext = 0; + // Compute next numerical value + let nextNumericalVersion = [...numericalVersion, 0]; + let cursorInNext = 0; + nextNumericalVersion[cursorInNext] += 1; + while (nextNumericalVersion[cursorInNext] >= maxForNumerical) { + nextNumericalVersion[cursorInNext] = 0; + cursorInNext += 1; nextNumericalVersion[cursorInNext] += 1; - while (nextNumericalVersion[cursorInNext] >= maxForNumerical) { - nextNumericalVersion[cursorInNext] = 0; - cursorInNext += 1; - nextNumericalVersion[cursorInNext] += 1; - } - if (nextNumericalVersion[nextNumericalVersion.length - 1] === 0) { - nextNumericalVersion = nextNumericalVersion.slice(0, nextNumericalVersion.length - 1); - } - nextNumericalVersion.reverse(); + } + if (nextNumericalVersion[nextNumericalVersion.length - 1] === 0) { + nextNumericalVersion = nextNumericalVersion.slice(0, nextNumericalVersion.length - 1); + } + nextNumericalVersion.reverse(); - // Translate next numerical value into a barcode - /** @type {Unit[]} */ - const next = []; - for (let numericalIndex = 0; numericalIndex !== nextNumericalVersion.length; ++numericalIndex) { - let numericalBase25 = nextNumericalVersion[numericalIndex].toString(25); - if (numericalIndex !== 0) { - numericalBase25 = numericalBase25.padStart(unitPerNumerical, '0'); - } - for (const in25 of numericalBase25) { - next.push(units[base25.indexOf(in25)]); - } + // Translate next numerical value into a barcode + /** @type {Unit[]} */ + const next = []; + for (let numericalIndex = 0; numericalIndex !== nextNumericalVersion.length; ++numericalIndex) { + let numericalBase25 = nextNumericalVersion[numericalIndex].toString(25); + if (numericalIndex !== 0) { + numericalBase25 = numericalBase25.padStart(unitPerNumerical, '0'); + } + for (const in25 of numericalBase25) { + next.push(units[base25.indexOf(in25)]); } - return next; - }; + } + return next; } diff --git a/website/blog/2024-12-07-advent-of-pbt-day-7/AdventOfTheDay.tsx b/website/blog/2024-12-07-advent-of-pbt-day-7/AdventOfTheDay.tsx index 90568fbd..f6cadf77 100644 --- a/website/blog/2024-12-07-advent-of-pbt-day-7/AdventOfTheDay.tsx +++ b/website/blog/2024-12-07-advent-of-pbt-day-7/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 7, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: simplifyLocation, parser, placeholderForm: '/123//456/789', diff --git a/website/blog/2024-12-07-advent-of-pbt-day-7/buggy.mjs b/website/blog/2024-12-07-advent-of-pbt-day-7/buggy.mjs index a6163750..9f13422b 100644 --- a/website/blog/2024-12-07-advent-of-pbt-day-7/buggy.mjs +++ b/website/blog/2024-12-07-advent-of-pbt-day-7/buggy.mjs @@ -1,42 +1,40 @@ // @ts-check -export default function advent() { - /** - * @param {string} sourceLocation - * @returns {string} - */ - return function simplifyLocation(sourceLocation) { - const components = sourceLocation.replace(/\/$/, '').split('/'); +/** + * @param {string} sourceLocation + * @returns {string} + */ +export default function simplifyLocation(sourceLocation) { + const components = sourceLocation.replace(/\/$/, '').split('/'); - let numMovesHigher = 0; - const stack = []; - for (const component of components) { - if (component === '.' || component === '') { - if (numMovesHigher > stack.length) { - return sourceLocation; - } - for (let i = 0; i !== numMovesHigher; ++i) { - stack.pop(); - } - } else if (component === '..') { - ++numMovesHigher; - } else { - if (numMovesHigher > stack.length) { - return sourceLocation; - } - for (let i = 0; i !== numMovesHigher; ++i) { - stack.pop(); - } - numMovesHigher = 0; - stack.push(component); + let numMovesHigher = 0; + const stack = []; + for (const component of components) { + if (component === '.' || component === '') { + if (numMovesHigher > stack.length) { + return sourceLocation; } + for (let i = 0; i !== numMovesHigher; ++i) { + stack.pop(); + } + } else if (component === '..') { + ++numMovesHigher; + } else { + if (numMovesHigher > stack.length) { + return sourceLocation; + } + for (let i = 0; i !== numMovesHigher; ++i) { + stack.pop(); + } + numMovesHigher = 0; + stack.push(component); } - if (numMovesHigher >= stack.length) { - return sourceLocation; - } - for (let i = 0; i !== numMovesHigher; ++i) { - stack.pop(); - } - return '/' + stack.join('/'); - }; + } + if (numMovesHigher >= stack.length) { + return sourceLocation; + } + for (let i = 0; i !== numMovesHigher; ++i) { + stack.pop(); + } + return '/' + stack.join('/'); } diff --git a/website/blog/2024-12-08-advent-of-pbt-day-8/AdventOfTheDay.tsx b/website/blog/2024-12-08-advent-of-pbt-day-8/AdventOfTheDay.tsx index 98a201ba..839b4c27 100644 --- a/website/blog/2024-12-08-advent-of-pbt-day-8/AdventOfTheDay.tsx +++ b/website/blog/2024-12-08-advent-of-pbt-day-8/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 8, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: respace, parser, placeholderForm: 'messagewithoutanyspace\nmessage\nspace\nnothing\nempty\nwithout\nany', diff --git a/website/blog/2024-12-08-advent-of-pbt-day-8/buggy.mjs b/website/blog/2024-12-08-advent-of-pbt-day-8/buggy.mjs index 9a5cc6f1..64f0b85b 100644 --- a/website/blog/2024-12-08-advent-of-pbt-day-8/buggy.mjs +++ b/website/blog/2024-12-08-advent-of-pbt-day-8/buggy.mjs @@ -1,39 +1,37 @@ // @ts-check -export default function advent() { - /** - * @param {string} spacelessMessage - * @param {string[]} words - * @returns {string} - */ - return function respace(spacelessMessage, words) { - const match = respaceInternal(spacelessMessage, words, 0); - if (match === undefined) { - return spacelessMessage; - } - return match.join(' '); - }; +/** + * @param {string} spacelessMessage + * @param {string[]} words + * @returns {string} + */ +export default function respace(spacelessMessage, words) { + const match = respaceInternal(spacelessMessage, words, 0); + if (match === undefined) { + return spacelessMessage; + } + return match.join(' '); +} - /** - * @param {string} spacelessMessage - * @param {string[]} words - * @param {number} startIndex - * @returns {string[] | undefined} - */ - function respaceInternal(spacelessMessage, words, startIndex) { - if (startIndex === spacelessMessage.length) { - return []; - } - for (const word of words) { - if (spacelessMessage.startsWith(word, startIndex)) { - const subMatch = respaceInternal(spacelessMessage, words, startIndex + word.length); - if (subMatch !== undefined) { - return [word, ...subMatch]; - } else { - return undefined; - } +/** + * @param {string} spacelessMessage + * @param {string[]} words + * @param {number} startIndex + * @returns {string[] | undefined} + */ +function respaceInternal(spacelessMessage, words, startIndex) { + if (startIndex === spacelessMessage.length) { + return []; + } + for (const word of words) { + if (spacelessMessage.startsWith(word, startIndex)) { + const subMatch = respaceInternal(spacelessMessage, words, startIndex + word.length); + if (subMatch !== undefined) { + return [word, ...subMatch]; + } else { + return undefined; } } - return undefined; } + return undefined; } diff --git a/website/blog/2024-12-09-advent-of-pbt-day-9/AdventOfTheDay.tsx b/website/blog/2024-12-09-advent-of-pbt-day-9/AdventOfTheDay.tsx index eddcb778..87b7ce71 100644 --- a/website/blog/2024-12-09-advent-of-pbt-day-9/AdventOfTheDay.tsx +++ b/website/blog/2024-12-09-advent-of-pbt-day-9/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 9, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: isProbablyEnchantedWord, parser, placeholderForm: 'any set of characters as long as it fits on one line', diff --git a/website/blog/2024-12-09-advent-of-pbt-day-9/buggy.mjs b/website/blog/2024-12-09-advent-of-pbt-day-9/buggy.mjs index ba8f6087..bd11eaa0 100644 --- a/website/blog/2024-12-09-advent-of-pbt-day-9/buggy.mjs +++ b/website/blog/2024-12-09-advent-of-pbt-day-9/buggy.mjs @@ -1,18 +1,16 @@ // @ts-check -export default function advent() { - /** - * @param {string} word - * @returns {boolean} - */ - return function isProbablyEnchantedWord(word) { - const lastIndex = word.length - 1; - const lastScannedIndex = Math.floor(lastIndex / 2); - for (let i = 0; i < lastScannedIndex; ++i) { - if (word[i] !== word[lastIndex - i]) { - return false; - } +/** + * @param {string} word + * @returns {boolean} + */ +export default function isProbablyEnchantedWord(word) { + const lastIndex = word.length - 1; + const lastScannedIndex = Math.floor(lastIndex / 2); + for (let i = 0; i < lastScannedIndex; ++i) { + if (word[i] !== word[lastIndex - i]) { + return false; } - return true; - }; + } + return true; } diff --git a/website/blog/2024-12-10-advent-of-pbt-day-10/AdventOfTheDay.tsx b/website/blog/2024-12-10-advent-of-pbt-day-10/AdventOfTheDay.tsx index 5ee4650f..86028bfb 100644 --- a/website/blog/2024-12-10-advent-of-pbt-day-10/AdventOfTheDay.tsx +++ b/website/blog/2024-12-10-advent-of-pbt-day-10/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 10, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: isProbablyEnchantedWordV2, parser, placeholderForm: 'any set of characters as long as it fits on one line', diff --git a/website/blog/2024-12-10-advent-of-pbt-day-10/buggy.mjs b/website/blog/2024-12-10-advent-of-pbt-day-10/buggy.mjs index ff578c66..6ab25393 100644 --- a/website/blog/2024-12-10-advent-of-pbt-day-10/buggy.mjs +++ b/website/blog/2024-12-10-advent-of-pbt-day-10/buggy.mjs @@ -1,11 +1,9 @@ // @ts-check -export default function advent() { - /** - * @param {string} word - * @returns {boolean} - */ - return function isProbablyEnchantedWordV2(word) { - return word.split('').reverse().join('') === word; - }; +/** + * @param {string} word + * @returns {boolean} + */ +export default function isProbablyEnchantedWordV2(word) { + return word.split('').reverse().join('') === word; } diff --git a/website/blog/2024-12-11-advent-of-pbt-day-11/AdventOfTheDay.tsx b/website/blog/2024-12-11-advent-of-pbt-day-11/AdventOfTheDay.tsx index 5d834aee..68e8f635 100644 --- a/website/blog/2024-12-11-advent-of-pbt-day-11/AdventOfTheDay.tsx +++ b/website/blog/2024-12-11-advent-of-pbt-day-11/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 11, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: findPlaceForSanta, postAdvent: (answer) => answer !== undefined, parser, diff --git a/website/blog/2024-12-11-advent-of-pbt-day-11/buggy.mjs b/website/blog/2024-12-11-advent-of-pbt-day-11/buggy.mjs index 61c9bc60..6e239c6a 100644 --- a/website/blog/2024-12-11-advent-of-pbt-day-11/buggy.mjs +++ b/website/blog/2024-12-11-advent-of-pbt-day-11/buggy.mjs @@ -1,62 +1,60 @@ // @ts-check -export default function advent() { - /** - * For the Christmas market, Santa is looking for a place. - * - * For each market of the world, he has to check if it can land with - * his 8 reindeers and his sleigh. This algorithm check whether there - * is an area of consecutive true that can contain requestedArea and return - * its upper-left corner. - * - * @param {boolean[][]} map - Indexed by map[y][x] - * @param {{ width: number; height: number }} requestedArea - * - * map.length corresponds to the height of the map - * map[0].length corresponds to the width of the map - * - * @returns {{ x: number; y: number } | undefined} - * - the upper-left corner of the area, - * whenever there is one place in the map having with - * rectangular width x height surface with only true - * - undefined if no such area exists - */ - return function findPlaceForSanta(map, requestedArea) { - for (let y = 0; y !== map.length; ++y) { - for (let x = 0; x !== map[0].length; ++x) { - const location = { x, y }; - const placeIsValid = isValidPlace(map, location, requestedArea); - if (placeIsValid) { - return location; - } +/** + * For the Christmas market, Santa is looking for a place. + * + * For each market of the world, he has to check if it can land with + * his 8 reindeers and his sleigh. This algorithm check whether there + * is an area of consecutive true that can contain requestedArea and return + * its upper-left corner. + * + * @param {boolean[][]} map - Indexed by map[y][x] + * @param {{ width: number; height: number }} requestedArea + * + * map.length corresponds to the height of the map + * map[0].length corresponds to the width of the map + * + * @returns {{ x: number; y: number } | undefined} + * - the upper-left corner of the area, + * whenever there is one place in the map having with + * rectangular width x height surface with only true + * - undefined if no such area exists + */ +export default function findPlaceForSanta(map, requestedArea) { + for (let y = 0; y !== map.length; ++y) { + for (let x = 0; x !== map[0].length; ++x) { + const location = { x, y }; + const placeIsValid = isValidPlace(map, location, requestedArea); + if (placeIsValid) { + return location; } } - return undefined; - }; + } + return undefined; +} - /** - * @param {boolean[][]} map - * @param {{ x: number; y: number }} start - * @param {{ width: number; height: number }} requestedArea - * @returns {boolean} - */ - function isValidPlace(map, start, requestedArea) { - for (let dy = 0; dy !== requestedArea.height; ++dy) { - if (!map[start.y + dy]?.[start.x]) { - return false; - } - if (!map[start.y + dy]?.[start.x + requestedArea.width - 1]) { - return false; - } +/** + * @param {boolean[][]} map + * @param {{ x: number; y: number }} start + * @param {{ width: number; height: number }} requestedArea + * @returns {boolean} + */ +function isValidPlace(map, start, requestedArea) { + for (let dy = 0; dy !== requestedArea.height; ++dy) { + if (!map[start.y + dy]?.[start.x]) { + return false; } - for (let dx = 0; dx !== requestedArea.width; ++dx) { - if (!map[start.y]?.[start.x + dx]) { - return false; - } - if (!map[start.y + requestedArea.height - 1]?.[start.x + dx]) { - return false; - } + if (!map[start.y + dy]?.[start.x + requestedArea.width - 1]) { + return false; + } + } + for (let dx = 0; dx !== requestedArea.width; ++dx) { + if (!map[start.y]?.[start.x + dx]) { + return false; + } + if (!map[start.y + requestedArea.height - 1]?.[start.x + dx]) { + return false; } - return true; } + return true; } diff --git a/website/blog/2024-12-12-advent-of-pbt-day-12/AdventOfTheDay.tsx b/website/blog/2024-12-12-advent-of-pbt-day-12/AdventOfTheDay.tsx index 4df11228..b03a5f2a 100644 --- a/website/blog/2024-12-12-advent-of-pbt-day-12/AdventOfTheDay.tsx +++ b/website/blog/2024-12-12-advent-of-pbt-day-12/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 12, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: planFastTravel, postAdvent: (route: Track[] | undefined) => route !== undefined ? route.reduce((acc, track) => acc + track.distance, 0) : undefined, diff --git a/website/blog/2024-12-12-advent-of-pbt-day-12/buggy.mjs b/website/blog/2024-12-12-advent-of-pbt-day-12/buggy.mjs index e2266898..52b1d1f0 100644 --- a/website/blog/2024-12-12-advent-of-pbt-day-12/buggy.mjs +++ b/website/blog/2024-12-12-advent-of-pbt-day-12/buggy.mjs @@ -1,56 +1,54 @@ // @ts-check -export default function advent() { - /** @typedef {{ from: string; to: string; distance: number }} Track */ +/** @typedef {{ from: string; to: string; distance: number }} Track */ - /** - * @param {string} departure - * @param {string} destination - * @param {Track[]} tracks - * @returns {Track[]|undefined} - */ - return function planFastTravel(departure, destination, tracks) { - /** @type {Record} */ - const distanceToNode = Object.fromEntries( - [departure, destination, ...tracks.map((t) => t.from), ...tracks.map((t) => t.to)].map((node) => [ - node, - { distance: Number.POSITIVE_INFINITY, edges: [] }, - ]), - ); - if (distanceToNode[departure]) { - distanceToNode[departure] = { distance: 0, edges: [] }; +/** + * @param {string} departure + * @param {string} destination + * @param {Track[]} tracks + * @returns {Track[]|undefined} + */ +export default function planFastTravel(departure, destination, tracks) { + /** @type {Record} */ + const distanceToNode = Object.fromEntries( + [departure, destination, ...tracks.map((t) => t.from), ...tracks.map((t) => t.to)].map((node) => [ + node, + { distance: Number.POSITIVE_INFINITY, edges: [] }, + ]), + ); + if (distanceToNode[departure]) { + distanceToNode[departure] = { distance: 0, edges: [] }; + } + while (true) { + const nextNode = findRemainingNodeWithMinimalDistance(distanceToNode); + if (nextNode === undefined) { + return undefined; // no path found } - while (true) { - const nextNode = findRemainingNodeWithMinimalDistance(distanceToNode); - if (nextNode === undefined) { - return undefined; // no path found - } - const data = distanceToNode[nextNode]; - if (nextNode === destination) { - return data.edges; - } - delete distanceToNode[nextNode]; - for (const e of tracks) { - if (e.from === nextNode && distanceToNode[e.to]) { - distanceToNode[e.to] = { distance: data.distance + e.distance, edges: [...data.edges, e] }; - } + const data = distanceToNode[nextNode]; + if (nextNode === destination) { + return data.edges; + } + delete distanceToNode[nextNode]; + for (const e of tracks) { + if (e.from === nextNode && distanceToNode[e.to]) { + distanceToNode[e.to] = { distance: data.distance + e.distance, edges: [...data.edges, e] }; } } - }; + } +} - /** - * @param {Record} distanceToNode - * @returns {string | undefined} - */ - function findRemainingNodeWithMinimalDistance(distanceToNode) { - let minNode = undefined; - let minDistance = Number.POSITIVE_INFINITY; - for (const [node, { distance }] of Object.entries(distanceToNode)) { - if (distance < minDistance) { - minNode = node; - minDistance = distance; - } +/** + * @param {Record} distanceToNode + * @returns {string | undefined} + */ +function findRemainingNodeWithMinimalDistance(distanceToNode) { + let minNode = undefined; + let minDistance = Number.POSITIVE_INFINITY; + for (const [node, { distance }] of Object.entries(distanceToNode)) { + if (distance < minDistance) { + minNode = node; + minDistance = distance; } - return minNode; } + return minNode; } diff --git a/website/blog/2024-12-13-advent-of-pbt-day-13/AdventOfTheDay.tsx b/website/blog/2024-12-13-advent-of-pbt-day-13/AdventOfTheDay.tsx index 83cd8ee3..27772ef7 100644 --- a/website/blog/2024-12-13-advent-of-pbt-day-13/AdventOfTheDay.tsx +++ b/website/blog/2024-12-13-advent-of-pbt-day-13/AdventOfTheDay.tsx @@ -1,12 +1,14 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 13, - buildBuggyAdvent: adventBuggy, - buggyAdventSurcharged: (...args: Parameters>) => { + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, + buggyAdventSurcharged: (...args: Parameters) => { try { - return adventBuggy()(...args); + return adventBuggy(...args); } catch (err) { return err; } diff --git a/website/blog/2024-12-13-advent-of-pbt-day-13/buggy.mjs b/website/blog/2024-12-13-advent-of-pbt-day-13/buggy.mjs index 0eecf030..7f1b3998 100644 --- a/website/blog/2024-12-13-advent-of-pbt-day-13/buggy.mjs +++ b/website/blog/2024-12-13-advent-of-pbt-day-13/buggy.mjs @@ -1,26 +1,24 @@ // @ts-check -export default function advent() { - /** - * @param {string} firstName - * @param {string} lastName - * @param {number} birthDateTimestamp - * @returns {string} - */ - return function buildSantaURLOfChild(firstName, lastName, birthDateTimestamp) { - /** @type {(i: number) => number} */ - const table = (i) => Array.from({ length: 8 }).reduce((c) => (c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1), i); - let str = String(birthDateTimestamp); - for (let i = 0; i !== Math.max(firstName.length, lastName.length); ++i) { - str += (firstName[i] ?? '') + (lastName[i] ?? ''); - } - str = encodeURIComponent(str); - let digest = 0 ^ -1; - for (let i = 0; i < str.length; i++) { - const byte = str.charCodeAt(i); - digest = (digest >>> 8) ^ table((digest ^ byte) & 0xff); - } - digest = (digest ^ -1) >>> 0; - return `https://my-history.santa-web/${encodeURIComponent(firstName)}-${encodeURIComponent(lastName)}-${digest.toString(16)}`; - }; +/** + * @param {string} firstName + * @param {string} lastName + * @param {number} birthDateTimestamp + * @returns {string} + */ +export default function buildSantaURLOfChild(firstName, lastName, birthDateTimestamp) { + /** @type {(i: number) => number} */ + const table = (i) => Array.from({ length: 8 }).reduce((c) => (c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1), i); + let str = String(birthDateTimestamp); + for (let i = 0; i !== Math.max(firstName.length, lastName.length); ++i) { + str += (firstName[i] ?? '') + (lastName[i] ?? ''); + } + str = encodeURIComponent(str); + let digest = 0 ^ -1; + for (let i = 0; i < str.length; i++) { + const byte = str.charCodeAt(i); + digest = (digest >>> 8) ^ table((digest ^ byte) & 0xff); + } + digest = (digest ^ -1) >>> 0; + return `https://my-history.santa-web/${encodeURIComponent(firstName)}-${encodeURIComponent(lastName)}-${digest.toString(16)}`; } diff --git a/website/blog/2024-12-14-advent-of-pbt-day-14/AdventOfTheDay.tsx b/website/blog/2024-12-14-advent-of-pbt-day-14/AdventOfTheDay.tsx index 48570a83..bdce738c 100644 --- a/website/blog/2024-12-14-advent-of-pbt-day-14/AdventOfTheDay.tsx +++ b/website/blog/2024-12-14-advent-of-pbt-day-14/AdventOfTheDay.tsx @@ -1,11 +1,13 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 14, - buildBuggyAdvent: adventBuggy, - buggyAdventSurcharged: (...args: Parameters>['compress']>) => { - const buggy = adventBuggy()(); + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, + buggyAdventSurcharged: (...args: Parameters['compress']>) => { + const buggy = adventBuggy(); return buggy.decompress(buggy.compress(...args)); }, referenceAdvent: (text) => text, diff --git a/website/blog/2024-12-14-advent-of-pbt-day-14/buggy.mjs b/website/blog/2024-12-14-advent-of-pbt-day-14/buggy.mjs index b4b2dc01..3a191770 100644 --- a/website/blog/2024-12-14-advent-of-pbt-day-14/buggy.mjs +++ b/website/blog/2024-12-14-advent-of-pbt-day-14/buggy.mjs @@ -1,52 +1,50 @@ // @ts-check -export default function advent() { - /** @typedef {{compress:(text:string)=>string, decompress:(compressed:string)=>string}} Compressor */ +/** @typedef {{compress:(text:string)=>string, decompress:(compressed:string)=>string}} Compressor */ +/** + * @returns {Compressor} + */ +export default function buildCompressor() { /** - * @returns {Compressor} + * @param {string} text + * @returns {string} */ - return function buildCompressor() { - /** - * @param {string} text - * @returns {string} - */ - function compress(text) { - const chars = [...text]; - if (chars.length === 0) { - return ''; - } - let compressed = ''; - let countOfPrevious = 1; - let previous = chars[0]; - for (let i = 1; i < chars.length; ++i) { - if (chars[i] === previous) { - countOfPrevious += 1; - } else { - compressed += `${countOfPrevious}${previous}`; - previous = chars[i]; - countOfPrevious = 1; - } - } - compressed += `${countOfPrevious}${previous}`; - return compressed; + function compress(text) { + const chars = [...text]; + if (chars.length === 0) { + return ''; } - /** - * @param {string} compressed - * @returns {string} - */ - function decompress(compressed) { - const regex = /(\d+)(.)/gmu; - let m = null; - let text = ''; - while ((m = regex.exec(compressed))) { - const charsInChunk = [...m[0]]; - const count = Number(charsInChunk.slice(0, -1)); - const char = charsInChunk.at(-1) ?? ''; - text += char.repeat(count); + let compressed = ''; + let countOfPrevious = 1; + let previous = chars[0]; + for (let i = 1; i < chars.length; ++i) { + if (chars[i] === previous) { + countOfPrevious += 1; + } else { + compressed += `${countOfPrevious}${previous}`; + previous = chars[i]; + countOfPrevious = 1; } - return text; } - return { compress, decompress }; - }; + compressed += `${countOfPrevious}${previous}`; + return compressed; + } + /** + * @param {string} compressed + * @returns {string} + */ + function decompress(compressed) { + const regex = /(\d+)(.)/gmu; + let m = null; + let text = ''; + while ((m = regex.exec(compressed))) { + const charsInChunk = [...m[0]]; + const count = Number(charsInChunk.slice(0, -1)); + const char = charsInChunk.at(-1) ?? ''; + text += char.repeat(count); + } + return text; + } + return { compress, decompress }; } diff --git a/website/blog/2024-12-15-advent-of-pbt-day-15/AdventOfTheDay.tsx b/website/blog/2024-12-15-advent-of-pbt-day-15/AdventOfTheDay.tsx index 8bdbc8f4..358f34f9 100644 --- a/website/blog/2024-12-15-advent-of-pbt-day-15/AdventOfTheDay.tsx +++ b/website/blog/2024-12-15-advent-of-pbt-day-15/AdventOfTheDay.tsx @@ -1,12 +1,14 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 15, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, buggyAdventSurcharged: (actions: ('put' | 'pop' | 'isEmpty')[]) => { const shelfSize = 5; - const shelf = adventBuggy()(); + const shelf = adventBuggy(); let shelfUsed = 0; for (const action of actions) { switch (action) { diff --git a/website/blog/2024-12-15-advent-of-pbt-day-15/buggy.mjs b/website/blog/2024-12-15-advent-of-pbt-day-15/buggy.mjs index fa1e432b..ece3fd4d 100644 --- a/website/blog/2024-12-15-advent-of-pbt-day-15/buggy.mjs +++ b/website/blog/2024-12-15-advent-of-pbt-day-15/buggy.mjs @@ -1,40 +1,38 @@ // @ts-check -export default function advent() { - /** @typedef {{ put: () => number; pop: () => number; isEmpty: () => boolean }} Shelf */ +/** @typedef {{ put: () => number; pop: () => number; isEmpty: () => boolean }} Shelf */ - /** - * @returns {Shelf} - */ - return function createShelf() { - const size = 5; - const data = [...Array(size)]; - const remapped = [0, 2, 1, 4, 3]; - let first = 0; - let last = 0; +/** + * @returns {Shelf} + */ +export default function createShelf() { + const size = 5; + const data = [...Array(size)]; + const remapped = [0, 2, 1, 4, 3]; + let first = 0; + let last = 0; - return { - put: () => { - const index = remapped[last]; - if (data[index] !== undefined) { - return -1; - } - data[index] = {}; - last = (last + 1) % size; - return index; - }, - pop: () => { - const index = remapped[first]; - if (data[index] === undefined) { - return -1; - } - data[index] = undefined; - first = (first + 1) % size; - return index; - }, - isEmpty: () => { - return first === last; - }, - }; + return { + put: () => { + const index = remapped[last]; + if (data[index] !== undefined) { + return -1; + } + data[index] = {}; + last = (last + 1) % size; + return index; + }, + pop: () => { + const index = remapped[first]; + if (data[index] === undefined) { + return -1; + } + data[index] = undefined; + first = (first + 1) % size; + return index; + }, + isEmpty: () => { + return first === last; + }, }; } diff --git a/website/blog/2024-12-16-advent-of-pbt-day-16/AdventOfTheDay.tsx b/website/blog/2024-12-16-advent-of-pbt-day-16/AdventOfTheDay.tsx index a708ecc8..aae3eed0 100644 --- a/website/blog/2024-12-16-advent-of-pbt-day-16/AdventOfTheDay.tsx +++ b/website/blog/2024-12-16-advent-of-pbt-day-16/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 16, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, buggyAdventSurcharged: (index: number) => { return !isBuggyIndexWithBefore(index) && !isBuggyIndexWithBefore(index + 1); }, @@ -23,9 +25,9 @@ function isBuggyIndexWithBefore(index: number) { if (index <= 0) { return false; } - const codeN = adventBuggy()(index); + const codeN = adventBuggy(index); const binaryCodeN = BigInt(codeN).toString(2); - const codeNBefore = adventBuggy()(index - 1); + const codeNBefore = adventBuggy(index - 1); const binaryCodeNBefore = BigInt(codeNBefore).toString(2).padStart(binaryCodeN.length, '0'); let diffCount = 0; for (let index = 0; index !== binaryCodeN.length; ++index) { diff --git a/website/blog/2024-12-16-advent-of-pbt-day-16/buggy.mjs b/website/blog/2024-12-16-advent-of-pbt-day-16/buggy.mjs index ed5889c8..f56538e8 100644 --- a/website/blog/2024-12-16-advent-of-pbt-day-16/buggy.mjs +++ b/website/blog/2024-12-16-advent-of-pbt-day-16/buggy.mjs @@ -1,11 +1,9 @@ // @ts-check -export default function advent() { - /** - * @param {number} n - * @returns {number} - */ - return function santaCode(n) { - return ((n * 2) ^ n) >> 1; - }; +/** + * @param {number} n + * @returns {number} + */ +export default function santaCode(n) { + return ((n * 2) ^ n) >> 1; } diff --git a/website/blog/2024-12-17-advent-of-pbt-day-17/AdventOfTheDay.tsx b/website/blog/2024-12-17-advent-of-pbt-day-17/AdventOfTheDay.tsx index d3c9797b..52475afe 100644 --- a/website/blog/2024-12-17-advent-of-pbt-day-17/AdventOfTheDay.tsx +++ b/website/blog/2024-12-17-advent-of-pbt-day-17/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 17, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: isValidEmail, parser, placeholderForm: 'something@domain.stuff', diff --git a/website/blog/2024-12-17-advent-of-pbt-day-17/buggy.mjs b/website/blog/2024-12-17-advent-of-pbt-day-17/buggy.mjs index e4d642bf..0572d4ea 100644 --- a/website/blog/2024-12-17-advent-of-pbt-day-17/buggy.mjs +++ b/website/blog/2024-12-17-advent-of-pbt-day-17/buggy.mjs @@ -1,12 +1,10 @@ // @ts-check -export default function advent() { - /** - * @param {string} emailAddress - * @returns {boolean} - */ - return function isValidEmail(emailAddress) { - const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; // GitHub Copilot said it - return emailRegex.test(emailAddress); - }; +/** + * @param {string} emailAddress + * @returns {boolean} + */ +export default function isValidEmail(emailAddress) { + const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; // GitHub Copilot said it + return emailRegex.test(emailAddress); } diff --git a/website/blog/2024-12-18-advent-of-pbt-day-18/AdventOfTheDay.tsx b/website/blog/2024-12-18-advent-of-pbt-day-18/AdventOfTheDay.tsx index 2e3506e7..6ef90555 100644 --- a/website/blog/2024-12-18-advent-of-pbt-day-18/AdventOfTheDay.tsx +++ b/website/blog/2024-12-18-advent-of-pbt-day-18/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 18, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: findOptimalJourney, postAdvent: (route: House[]) => { let totalDistance = 0; diff --git a/website/blog/2024-12-18-advent-of-pbt-day-18/buggy.mjs b/website/blog/2024-12-18-advent-of-pbt-day-18/buggy.mjs index 0d62d546..6616d720 100644 --- a/website/blog/2024-12-18-advent-of-pbt-day-18/buggy.mjs +++ b/website/blog/2024-12-18-advent-of-pbt-day-18/buggy.mjs @@ -1,41 +1,39 @@ // @ts-check -export default function advent() { - /** @typedef {{ x: number, y: number }} House */ +/** @typedef {{ x: number, y: number }} House */ - /** - * @param {House[]} houses - * @returns {House[]} - */ - return function findOptimalJourney(houses) { - const santaHouse = { x: 0, y: 0 }; - const toVisit = [...houses]; - const journey = [santaHouse]; - let lastHouse = santaHouse; - while (toVisit.length !== 0) { - let closestIndex = 0; - let closestDistance = distance(lastHouse, toVisit[0]); - for (let i = 1; i < toVisit.length; ++i) { - const currentDistance = distance(lastHouse, toVisit[i]); - if (currentDistance < closestDistance) { - closestIndex = i; - closestDistance = currentDistance; - } +/** + * @param {House[]} houses + * @returns {House[]} + */ +export default function findOptimalJourney(houses) { + const santaHouse = { x: 0, y: 0 }; + const toVisit = [...houses]; + const journey = [santaHouse]; + let lastHouse = santaHouse; + while (toVisit.length !== 0) { + let closestIndex = 0; + let closestDistance = distance(lastHouse, toVisit[0]); + for (let i = 1; i < toVisit.length; ++i) { + const currentDistance = distance(lastHouse, toVisit[i]); + if (currentDistance < closestDistance) { + closestIndex = i; + closestDistance = currentDistance; } - lastHouse = toVisit[closestIndex]; - toVisit.splice(closestIndex, 1); - journey.push(lastHouse); } - journey.push(santaHouse); - return journey; - }; - - /** - * @param {House} houseA - * @param {House} houseB - * @returns {number} - */ - function distance(houseA, houseB) { - return Math.abs(houseA.x - houseB.x) + Math.abs(houseA.y - houseB.y); + lastHouse = toVisit[closestIndex]; + toVisit.splice(closestIndex, 1); + journey.push(lastHouse); } + journey.push(santaHouse); + return journey; +} + +/** + * @param {House} houseA + * @param {House} houseB + * @returns {number} + */ +function distance(houseA, houseB) { + return Math.abs(houseA.x - houseB.x) + Math.abs(houseA.y - houseB.y); } diff --git a/website/blog/2024-12-19-advent-of-pbt-day-19/AdventOfTheDay.tsx b/website/blog/2024-12-19-advent-of-pbt-day-19/AdventOfTheDay.tsx index b5b210d2..5a824fd7 100644 --- a/website/blog/2024-12-19-advent-of-pbt-day-19/AdventOfTheDay.tsx +++ b/website/blog/2024-12-19-advent-of-pbt-day-19/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 19, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: findOptimalPacking, postAdvent: (sleights: number[][]) => sleights.length, parser, diff --git a/website/blog/2024-12-19-advent-of-pbt-day-19/buggy.mjs b/website/blog/2024-12-19-advent-of-pbt-day-19/buggy.mjs index 77f86f7a..f4191c55 100644 --- a/website/blog/2024-12-19-advent-of-pbt-day-19/buggy.mjs +++ b/website/blog/2024-12-19-advent-of-pbt-day-19/buggy.mjs @@ -1,26 +1,24 @@ // @ts-check -export default function advent() { - /** - * @param {number[]} weights - * @returns {number[][]} - */ - return function findOptimalPacking(weights) { - const sleights = []; - const sortedWeights = [...weights].sort((a, b) => b - a); - while (sortedWeights.length !== 0) { - let sleighWeight = 0; - const sleigh = []; - for (let i = 0; i < sortedWeights.length; ++i) { - if (sleighWeight + sortedWeights[i] <= 10) { - sleighWeight += sortedWeights[i]; - sleigh.push(sortedWeights[i]); - sortedWeights.splice(i, 1); - i -= 1; - } +/** + * @param {number[]} weights + * @returns {number[][]} + */ +export default function findOptimalPacking(weights) { + const sleights = []; + const sortedWeights = [...weights].sort((a, b) => b - a); + while (sortedWeights.length !== 0) { + let sleighWeight = 0; + const sleigh = []; + for (let i = 0; i < sortedWeights.length; ++i) { + if (sleighWeight + sortedWeights[i] <= 10) { + sleighWeight += sortedWeights[i]; + sleigh.push(sortedWeights[i]); + sortedWeights.splice(i, 1); + i -= 1; } - sleights.push(sleigh); } - return sleights; - }; + sleights.push(sleigh); + } + return sleights; } diff --git a/website/blog/2024-12-20-advent-of-pbt-day-20/AdventOfTheDay.tsx b/website/blog/2024-12-20-advent-of-pbt-day-20/AdventOfTheDay.tsx index 6ceb3984..536b7df7 100644 --- a/website/blog/2024-12-20-advent-of-pbt-day-20/AdventOfTheDay.tsx +++ b/website/blog/2024-12-20-advent-of-pbt-day-20/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 20, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: findStartIndex, parser, placeholderForm: '1\n2\n3\n3\n9', diff --git a/website/blog/2024-12-20-advent-of-pbt-day-20/buggy.mjs b/website/blog/2024-12-20-advent-of-pbt-day-20/buggy.mjs index 4f3b9ccd..cbe589be 100644 --- a/website/blog/2024-12-20-advent-of-pbt-day-20/buggy.mjs +++ b/website/blog/2024-12-20-advent-of-pbt-day-20/buggy.mjs @@ -1,39 +1,37 @@ // @ts-check -export default function advent() { - /** - * This solution has been provided to you by GPT-4o - * @param {number[]} partlyShuffled - * @returns {number} - */ - return function findStartIndex(partlyShuffled) { - let left = 0; - let right = partlyShuffled.length - 1; +/** + * This solution has been provided to you by GPT-4o + * @param {number[]} partlyShuffled + * @returns {number} + */ +export default function findStartIndex(partlyShuffled) { + let left = 0; + let right = partlyShuffled.length - 1; - // Handle the case where the array is not rotated - if (partlyShuffled[left] <= partlyShuffled[right]) return 0; + // Handle the case where the array is not rotated + if (partlyShuffled[left] <= partlyShuffled[right]) return 0; - while (left <= right) { - let mid = Math.floor((left + right) / 2); + while (left <= right) { + let mid = Math.floor((left + right) / 2); - // Check if mid is the rotation point - if (partlyShuffled[mid] > partlyShuffled[mid + 1]) { - return mid + 1; - } - if (partlyShuffled[mid] < partlyShuffled[mid - 1]) { - return mid; - } + // Check if mid is the rotation point + if (partlyShuffled[mid] > partlyShuffled[mid + 1]) { + return mid + 1; + } + if (partlyShuffled[mid] < partlyShuffled[mid - 1]) { + return mid; + } - // Decide which half to search next - if (partlyShuffled[mid] >= partlyShuffled[left]) { - // Rotation point is in the right half - left = mid + 1; - } else { - // Rotation point is in the left half - right = mid - 1; - } + // Decide which half to search next + if (partlyShuffled[mid] >= partlyShuffled[left]) { + // Rotation point is in the right half + left = mid + 1; + } else { + // Rotation point is in the left half + right = mid - 1; } + } - return -1; // This should never happen in a valid rotated array - }; + return -1; // This should never happen in a valid rotated array } diff --git a/website/blog/2024-12-21-advent-of-pbt-day-21/AdventOfTheDay.tsx b/website/blog/2024-12-21-advent-of-pbt-day-21/AdventOfTheDay.tsx index 3222895d..d6b0c082 100644 --- a/website/blog/2024-12-21-advent-of-pbt-day-21/AdventOfTheDay.tsx +++ b/website/blog/2024-12-21-advent-of-pbt-day-21/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 21, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: canStillWinTheGame, parser, placeholderForm: '๐ŸŽ„?\n๐ŸŽ„.๐ŸŽ„\n.๐ŸŽ„.\n๐ŸŽ.๐ŸŽ', diff --git a/website/blog/2024-12-21-advent-of-pbt-day-21/buggy.mjs b/website/blog/2024-12-21-advent-of-pbt-day-21/buggy.mjs index 0c39dca3..70475fb4 100644 --- a/website/blog/2024-12-21-advent-of-pbt-day-21/buggy.mjs +++ b/website/blog/2024-12-21-advent-of-pbt-day-21/buggy.mjs @@ -1,64 +1,62 @@ // @ts-check -export default function advent() { - const player1 = '\u{1f384}'; - const player2 = '\u{1f381}'; +const player1 = '๐ŸŽ„'; +const player2 = '๐ŸŽ'; - /** @typedef {'\u{1f384}' | '\u{1f381}'} Player */ - /** @typedef {Player | null} Cell */ +/** @typedef {'๐ŸŽ„' | '๐ŸŽ'} Player */ +/** @typedef {Player | null} Cell */ - /** - * @param {Cell[][]} board - * @param {Player} player - * @returns {boolean} - */ - return function canStillWinTheGame(board, player) { - assertLegalBoard(board); - return ( - hasWon(board, player) || - hasWon( - board.map((row) => row.map((cell) => cell ?? player)), - player, - ) - ); - }; +/** + * @param {Cell[][]} board + * @param {Player} player + * @returns {boolean} + */ +export default function canStillWinTheGame(board, player) { + assertLegalBoard(board); + return ( + hasWon(board, player) || + hasWon( + board.map((row) => row.map((cell) => cell ?? player)), + player, + ) + ); +} - /** - * @param {Cell[][]} board - * @param {Player} player - * @returns {boolean} - */ - function hasWon(board, player) { - const lines = [...Array(3)].map((_, i) => `${board[i][0]}${board[i][1]}${board[i][2]}`); - const columns = [...Array(3)].map((_, i) => `${board[0][i]}${board[1][i]}${board[2][i]}`); - const diags = [`${board[0][0]}${board[1][1]}${board[2][2]}`, `${board[2][0]}${board[1][1]}${board[0][2]}`]; - return [...lines, ...columns, ...diags].includes(`${player}${player}${player}`); - } +/** + * @param {Cell[][]} board + * @param {Player} player + * @returns {boolean} + */ +function hasWon(board, player) { + const lines = [...Array(3)].map((_, i) => `${board[i][0]}${board[i][1]}${board[i][2]}`); + const columns = [...Array(3)].map((_, i) => `${board[0][i]}${board[1][i]}${board[2][i]}`); + const diags = [`${board[0][0]}${board[1][1]}${board[2][2]}`, `${board[2][0]}${board[1][1]}${board[0][2]}`]; + return [...lines, ...columns, ...diags].includes(`${player}${player}${player}`); +} - /** - * @param {Cell[][]} board - * @returns {void} - */ - function assertLegalBoard(board) { - if (board.length !== 3) { - throw new Error('The grid should be 3x3, received too many rows'); - } - if (board.some((row) => row.length !== 3)) { - throw new Error('The grid should be 3x3, received too many columns in one of the rows'); - } - if (!board.every((row) => row.every((cell) => cell === null || cell === player1 || cell === player2))) { - throw new Error('The grid should only be made of valid symbols or null (for empty)'); - } - const count1 = board.flat().reduce((acc, player) => (player === player1 ? acc + 1 : acc), 0); - const count2 = board.flat().reduce((acc, player) => (player === player2 ? acc + 1 : acc), 0); - if (count1 < count2) { - throw new Error('Player 1 is supposed to play first, there are less symbols for 1 than for 2'); - } - if (count1 > count2 + 1) { - throw new Error('Players are supposed to one after the other, player 1 played a bit too many times'); - } - if (count1 === count2 && hasWon(board, player1)) { - throw new Error('Player 2 played while player 1 already won the game'); - } +/** + * @param {Cell[][]} board + * @returns {void} + */ +function assertLegalBoard(board) { + if (board.length !== 3) { + throw new Error('The grid should be 3x3, received too many rows'); + } + if (board.some((row) => row.length !== 3)) { + throw new Error('The grid should be 3x3, received too many columns in one of the rows'); + } + if (!board.every((row) => row.every((cell) => cell === null || cell === player1 || cell === player2))) { + throw new Error('The grid should only be made of valid symbols or null (for empty)'); + } + const count1 = board.flat().reduce((acc, player) => (player === player1 ? acc + 1 : acc), 0); + const count2 = board.flat().reduce((acc, player) => (player === player2 ? acc + 1 : acc), 0); + if (count1 < count2) { + throw new Error('Player 1 is supposed to play first, there are less symbols for 1 than for 2'); + } + if (count1 > count2 + 1) { + throw new Error('Players are supposed to one after the other, player 1 played a bit too many times'); + } + if (count1 === count2 && hasWon(board, player1)) { + throw new Error('Player 2 played while player 1 already won the game'); } } diff --git a/website/blog/2024-12-22-advent-of-pbt-day-22/AdventOfTheDay.tsx b/website/blog/2024-12-22-advent-of-pbt-day-22/AdventOfTheDay.tsx index 41650dbb..0781d63d 100644 --- a/website/blog/2024-12-22-advent-of-pbt-day-22/AdventOfTheDay.tsx +++ b/website/blog/2024-12-22-advent-of-pbt-day-22/AdventOfTheDay.tsx @@ -1,9 +1,11 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 22, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: computeSantaMindScore, parser, placeholderForm: '๐ŸŽ„๐ŸŽโ›„๐ŸŽˆ๐ŸŽ…\n๐ŸŽ๐ŸŽ„โ›„๐ŸŽ„๐ŸฆŒ', diff --git a/website/blog/2024-12-22-advent-of-pbt-day-22/buggy.mjs b/website/blog/2024-12-22-advent-of-pbt-day-22/buggy.mjs index 0bcd827e..fa96de9e 100644 --- a/website/blog/2024-12-22-advent-of-pbt-day-22/buggy.mjs +++ b/website/blog/2024-12-22-advent-of-pbt-day-22/buggy.mjs @@ -1,30 +1,28 @@ // @ts-check -export default function advent() { - /** @typedef {"\u{1f384}"|"\u{1f98c}"|"\u{26c4}"|"\u{1f6f7}"|"\u{1f388}"|"\u{1f380}"|"\u{1f385}"|"\u{1f381}"} Icon */ - /** @typedef {[Icon, Icon, Icon, Icon, Icon]} Sequence */ +/** @typedef {"๐ŸŽ„"|"๐ŸฆŒ"|"โ›„"|"๐Ÿ›ท"|"๐ŸŽˆ"|"๐ŸŽ€"|"๐ŸŽ…"|"๐ŸŽ"} Icon */ +/** @typedef {[Icon, Icon, Icon, Icon, Icon]} Sequence */ - /** - * @param {Sequence} secretSequence - * @param {Sequence} guessedSequence - * @returns {{goodPlacement:number; misplaced: number}} - */ - return function computeSantaMindScore(secretSequence, guessedSequence) { - let goodPlacement = 0; - let misplaced = 0; - const copiedSecretSequence = [...secretSequence]; - for (let index = 0; index !== guessedSequence.length; ++index) { - const item = guessedSequence[index]; - const indexInSecret = secretSequence.indexOf(item); - const indexInCopiedSecret = copiedSecretSequence.indexOf(item); - if (index === indexInSecret) { - ++goodPlacement; - copiedSecretSequence.splice(indexInCopiedSecret, 1); - } else if (indexInCopiedSecret !== -1) { - ++misplaced; - copiedSecretSequence.splice(indexInCopiedSecret, 1); - } +/** + * @param {Sequence} secretSequence + * @param {Sequence} guessedSequence + * @returns {{goodPlacement:number; misplaced: number}} + */ +export default function computeSantaMindScore(secretSequence, guessedSequence) { + let goodPlacement = 0; + let misplaced = 0; + const copiedSecretSequence = [...secretSequence]; + for (let index = 0; index !== guessedSequence.length; ++index) { + const item = guessedSequence[index]; + const indexInSecret = secretSequence.indexOf(item); + const indexInCopiedSecret = copiedSecretSequence.indexOf(item); + if (index === indexInSecret) { + ++goodPlacement; + copiedSecretSequence.splice(indexInCopiedSecret, 1); + } else if (indexInCopiedSecret !== -1) { + ++misplaced; + copiedSecretSequence.splice(indexInCopiedSecret, 1); } - return { goodPlacement, misplaced }; - }; + } + return { goodPlacement, misplaced }; } diff --git a/website/blog/2024-12-23-advent-of-pbt-day-23/AdventOfTheDay.tsx b/website/blog/2024-12-23-advent-of-pbt-day-23/AdventOfTheDay.tsx index 277e93af..295cb6e2 100644 --- a/website/blog/2024-12-23-advent-of-pbt-day-23/AdventOfTheDay.tsx +++ b/website/blog/2024-12-23-advent-of-pbt-day-23/AdventOfTheDay.tsx @@ -1,13 +1,15 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 23, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: () => true, - buggyAdventSurcharged: (...args: Parameters>) => { + buggyAdventSurcharged: (...args: Parameters) => { const expected = payslipContentFor(...args); - const out = adventBuggy()(...args); + const out = adventBuggy(...args); const [availableCoins, amountToBePaid] = args; if (out === null) { return expected === null ? true : 'not supposed to find anything'; diff --git a/website/blog/2024-12-23-advent-of-pbt-day-23/buggy.mjs b/website/blog/2024-12-23-advent-of-pbt-day-23/buggy.mjs index a5a80482..984c12b1 100644 --- a/website/blog/2024-12-23-advent-of-pbt-day-23/buggy.mjs +++ b/website/blog/2024-12-23-advent-of-pbt-day-23/buggy.mjs @@ -1,41 +1,39 @@ // @ts-check -export default function advent() { - /** @typedef {1|2|3|4|5|6|7|8|9|10} Coin */ +/** @typedef {1|2|3|4|5|6|7|8|9|10} Coin */ +/** + * @param {Coin[]} availableCoins + * @param {number} amountToBePaid + * @returns {Coin[] | null} + */ +export default function payslipContentFor(availableCoins, amountToBePaid) { + const coins = [...availableCoins].sort((a, b) => b - a); + /** @type {(Coin[] | undefined | null)[]} */ + const memo = Array.from({ length: coins.length }, () => undefined); /** - * @param {Coin[]} availableCoins - * @param {number} amountToBePaid + * @param {number} target + * @param {number} index * @returns {Coin[] | null} */ - return function payslipContentFor(availableCoins, amountToBePaid) { - const coins = [...availableCoins].sort((a, b) => b - a); - /** @type {(Coin[] | undefined | null)[]} */ - const memo = Array.from({ length: coins.length }, () => undefined); - /** - * @param {number} target - * @param {number} index - * @returns {Coin[] | null} - */ - function helper(target, index) { - if (target === 0) { - return []; - } - if (target < 0 || index >= coins.length) { - return null; - } - if (memo[index] !== undefined) { - return memo[index]; - } - const withCurrent = helper(target - coins[index], index + 1); - if (withCurrent !== null) { - memo[index] = [coins[index], ...withCurrent]; - return [coins[index], ...withCurrent]; - } - const withoutCurrent = helper(target, index + 1); - memo[index] = withoutCurrent; - return withoutCurrent; + function helper(target, index) { + if (target === 0) { + return []; } - return helper(amountToBePaid, 0); - }; + if (target < 0 || index >= coins.length) { + return null; + } + if (memo[index] !== undefined) { + return memo[index]; + } + const withCurrent = helper(target - coins[index], index + 1); + if (withCurrent !== null) { + memo[index] = [coins[index], ...withCurrent]; + return [coins[index], ...withCurrent]; + } + const withoutCurrent = helper(target, index + 1); + memo[index] = withoutCurrent; + return withoutCurrent; + } + return helper(amountToBePaid, 0); } diff --git a/website/blog/2024-12-24-advent-of-pbt-day-24/AdventOfTheDay.tsx b/website/blog/2024-12-24-advent-of-pbt-day-24/AdventOfTheDay.tsx index 4c8a4284..8585c29f 100644 --- a/website/blog/2024-12-24-advent-of-pbt-day-24/AdventOfTheDay.tsx +++ b/website/blog/2024-12-24-advent-of-pbt-day-24/AdventOfTheDay.tsx @@ -1,13 +1,15 @@ import adventBuggy from './buggy.mjs'; +import adventBuggyRaw from './buggy.mjs?raw'; import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ day: 24, - buildBuggyAdvent: adventBuggy, + buggyAdvent: adventBuggy, + snippet: adventBuggyRaw, referenceAdvent: () => true, - buggyAdventSurcharged: (...args: Parameters>) => { + buggyAdventSurcharged: (...args: Parameters) => { const expected = distributeCoins(...args); - const out = adventBuggy()(...args); + const out = adventBuggy(...args); const [availableCoins, amountsToBePaid] = args; if (out === null) { return expected === null ? true : 'not supposed to find anything'; diff --git a/website/blog/2024-12-24-advent-of-pbt-day-24/buggy.mjs b/website/blog/2024-12-24-advent-of-pbt-day-24/buggy.mjs index 4e7c56f7..84019434 100644 --- a/website/blog/2024-12-24-advent-of-pbt-day-24/buggy.mjs +++ b/website/blog/2024-12-24-advent-of-pbt-day-24/buggy.mjs @@ -1,58 +1,56 @@ // @ts-check -export default function advent() { - /** @typedef {1|2|3|4|5|6|7|8|9|10} Coin */ +/** @typedef {1|2|3|4|5|6|7|8|9|10} Coin */ +/** + * @param {Coin[]} availableCoins + * @param {number[]} amountsToBePaid + * @returns {Coin[][] | null} + */ +export default function distributeCoins(availableCoins, amountsToBePaid) { /** * @param {Coin[]} availableCoins - * @param {number[]} amountsToBePaid - * @returns {Coin[][] | null} + * @param {number} amountToBePaid + * @returns {Coin[] | null} */ - return function distributeCoins(availableCoins, amountsToBePaid) { + function payslipContentFor(availableCoins, amountToBePaid) { + const coins = [...availableCoins].sort((a, b) => b - a); /** - * @param {Coin[]} availableCoins - * @param {number} amountToBePaid + * @param {number} target + * @param {number} index * @returns {Coin[] | null} */ - function payslipContentFor(availableCoins, amountToBePaid) { - const coins = [...availableCoins].sort((a, b) => b - a); - /** - * @param {number} target - * @param {number} index - * @returns {Coin[] | null} - */ - function helper(target, index) { - if (target === 0) { - return []; - } - if (target < 0 || index >= coins.length) { - return null; - } - const withCurrent = helper(target - coins[index], index + 1); - if (withCurrent !== null) { - return [coins[index], ...withCurrent]; - } - const withoutCurrent = helper(target, index + 1); - return withoutCurrent; + function helper(target, index) { + if (target === 0) { + return []; } - return helper(amountToBePaid, 0); - } - - const remainingCoins = [...availableCoins]; - const coinsForPayslips = []; - const orderedAmountsToBePaid = amountsToBePaid - .map((amount, index) => ({ amount, index })) - .sort((a, b) => a.amount - b.amount); - for (const { index, amount } of orderedAmountsToBePaid) { - const dedicatedCoins = payslipContentFor(remainingCoins, amount); - if (dedicatedCoins === null) { + if (target < 0 || index >= coins.length) { return null; } - for (const coin of dedicatedCoins) { - remainingCoins.splice(remainingCoins.indexOf(coin), 1); + const withCurrent = helper(target - coins[index], index + 1); + if (withCurrent !== null) { + return [coins[index], ...withCurrent]; } - coinsForPayslips[index] = dedicatedCoins; + const withoutCurrent = helper(target, index + 1); + return withoutCurrent; + } + return helper(amountToBePaid, 0); + } + + const remainingCoins = [...availableCoins]; + const coinsForPayslips = []; + const orderedAmountsToBePaid = amountsToBePaid + .map((amount, index) => ({ amount, index })) + .sort((a, b) => a.amount - b.amount); + for (const { index, amount } of orderedAmountsToBePaid) { + const dedicatedCoins = payslipContentFor(remainingCoins, amount); + if (dedicatedCoins === null) { + return null; + } + for (const coin of dedicatedCoins) { + remainingCoins.splice(remainingCoins.indexOf(coin), 1); } - return coinsForPayslips; - }; + coinsForPayslips[index] = dedicatedCoins; + } + return coinsForPayslips; } diff --git a/website/src/types/raw.d.ts b/website/src/types/raw.d.ts new file mode 100644 index 00000000..9608f44a --- /dev/null +++ b/website/src/types/raw.d.ts @@ -0,0 +1,4 @@ +declare module '*.mjs?raw' { + const content: string; + export default content; +} -- 2.51.2