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; +}