From a1e1b4350700e800507725370c3c962fe422feaa Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Mon, 23 Dec 2024 10:29:23 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20Advent=20of=20PBT,=20Day=2023=20?= =?UTF-8?q?(#5553)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AdventOfTheDay.tsx | 82 +++++++++++++++++++ .../2024-12-23-advent-of-pbt-day-23/buggy.mjs | 35 ++++++++ .../2024-12-23-advent-of-pbt-day-23/index.md | 51 ++++++++++++ .../social.png | 3 + 4 files changed, 171 insertions(+) create mode 100644 website/blog/2024-12-23-advent-of-pbt-day-23/AdventOfTheDay.tsx create mode 100644 website/blog/2024-12-23-advent-of-pbt-day-23/buggy.mjs create mode 100644 website/blog/2024-12-23-advent-of-pbt-day-23/index.md create mode 100644 website/blog/2024-12-23-advent-of-pbt-day-23/social.png 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 new file mode 100644 index 00000000000..dff724170b4 --- /dev/null +++ b/website/blog/2024-12-23-advent-of-pbt-day-23/AdventOfTheDay.tsx @@ -0,0 +1,82 @@ +import adventBuggy from './buggy.mjs'; +import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; + +const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ + day: 23, + buildBuggyAdvent: adventBuggy, + referenceAdvent: () => true, + buggyAdventSurcharged: (...args: Parameters>) => { + const expected = payslipContentFor(...args); + const out = adventBuggy()(...args); + const [availableCoins, amountToBePaid] = args; + if (out === null) { + return expected === null ? true : 'not supposed to find anything'; + } + if (out.reduce((acc, v) => acc + v, 0) !== amountToBePaid) { + return 'bad amount'; + } + const coins = [...availableCoins]; + for (const coinValue of out) { + const index = coins.indexOf(coinValue); + if (index === -1) { + return 'no such coin'; + } + coins.splice(index, 1); + } + return true; + }, + parser, + placeholderForm: '7?\n1,1,2,5,10', + functionName: 'payslipContentFor', + signature: 'payslipContentFor(availableCoins: Coin[], amountToBePaid: number): Coin[] | null;', + signatureExtras: ['type Coin = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;'], +}); + +export { AdventPlaygroundOfTheDay, FormOfTheDay }; + +// Reference implementation + +function payslipContentFor(availableCoins, amountToBePaid) { + const coins = [...availableCoins].sort((a, b) => b - a); + 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]; + } + return helper(target, index + 1); + } + return helper(amountToBePaid, 0); +} + +// Inputs parser + +function parser(answer: string): unknown[] | undefined { + const lines = answer.split('\n'); + if (lines.length !== 2) { + throw new Error('Expected to receive two lines one for the amount to be paid, another one for the coins'); + } + if (lines[0].at(-1) !== '?') { + throw new Error(`First line must end by ?, got: ${lines[0]}.`); + } + const amount = Number(lines[0].slice(0, -1)); + if (!Number.isInteger(amount) || amount < 0 || amount > 2 ** 31 - 1) { + throw new Error(`Invalid amount received, must be a positive integer value below 2**31-1, got: ${lines[0]}.`); + } + const coins: number[] = + lines[1] === '' + ? [] + : lines[1].split(',').map((v) => { + const n = Number(v); + if (!Number.isInteger(n) || n < 1 || n > 10) { + throw new Error(`Invalid coin value received, got: ${v}.`); + } + return n; + }); + return [coins, amount]; +} 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 new file mode 100644 index 00000000000..8d67782ce40 --- /dev/null +++ b/website/blog/2024-12-23-advent-of-pbt-day-23/buggy.mjs @@ -0,0 +1,35 @@ +// @ts-check + +export default function advent() { + /** @typedef {1|2|3|4|5|6|7|8|9|10} Coin */ + + /** + * @param {Coin[]} availableCoins + * @param {number} amountToBePaid + * @returns {Coin[] | null} + */ + return function payslipContentFor(availableCoins, amountToBePaid) { + const coins = [...availableCoins].sort((a, b) => b - a); + const memo = Array.from({ length: coins.length }, () => undefined); + 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; + } + return helper(amountToBePaid, 0); + }; +} diff --git a/website/blog/2024-12-23-advent-of-pbt-day-23/index.md b/website/blog/2024-12-23-advent-of-pbt-day-23/index.md new file mode 100644 index 00000000000..61776983362 --- /dev/null +++ b/website/blog/2024-12-23-advent-of-pbt-day-23/index.md @@ -0,0 +1,51 @@ +--- +title: Advent of PBT 2024 · Day 23 +authors: [dubzzz] +tags: [advent-of-pbt, advent-of-pbt-2024] +image: ./social.png +--- + +import {AdventPlaygroundOfTheDay,FormOfTheDay} from './AdventOfTheDay'; +import BlueskyComments from '../2024-12-01-advent-of-pbt-day-1/BlueskyComments'; + +Christmas is at risk! In their rush to meet tight deadlines, Santa’s elves accidentally introduced bugs into critical algorithms. If these issues aren’t discovered in time, Christmas could be delayed for everyone worldwide! + +Your mission is to troubleshoot these black-box algorithms using the power of fast-check. + +The clock is ticking. This time, it’s not Santa but Emma who needs your help: she suspects her coin-payment algorithm might have bugs. Can you uncover any issues and ensure every elf gets paid on time for years to come? 🎄✨ + + + +## Money day + +In Santa's world, money works just like in ours. At the end of each month, elves and other inhabitants receive a payslip reflecting their contributions. However, Santa has a habit of delaying payments in December, claiming he needs to withdraw coins since he only pays in cash. + +Last year’s drama prompted Emma, one of the elves, to take action. She designed an algorithm to ensure smooth payouts. Her algorithm works like this: + +- **Input:** A list of coins Santa has in hand and the exact amount he needs to pay a given elf. +- **Output:** Either an array containing the values of the coins that sum up to the exact amount or `null` if it’s impossible to make the payment. + +For example: + +- If Santa has coins `[1, 1, 2, 5, 10]` and needs to pay `7`, the algorithm might return `[2, 5]`. +- If Santa has coins `[3, 4, 5]` and needs to pay `2`, it would return `null`. + +## Hands on + +This time, it’s not Santa calling you — it’s Emma. She’s concerned her algorithm might contain errors. If the payouts are delayed again this year, it could damage elf morale and break their motivation for next year. + +Emma shared one important detail before leaving you to test her algorithm: in Santa’s world, the coins available are always `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]`. + +Your mission: Use property-based testing to check Emma’s algorithm for bugs and ensure it works flawlessly. + +You’ve already saved Christmas this year; now it’s time to ensure smooth payouts for years to come! 🎄✨ + + + +## Your answer + + + +## Comments + + diff --git a/website/blog/2024-12-23-advent-of-pbt-day-23/social.png b/website/blog/2024-12-23-advent-of-pbt-day-23/social.png new file mode 100644 index 00000000000..ebdaecea8be --- /dev/null +++ b/website/blog/2024-12-23-advent-of-pbt-day-23/social.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f51386ba52d709197056b86523085af4c00e2d9bfbb60bfc28c7123f839617c +size 235228