-
-
Notifications
You must be signed in to change notification settings - Fork 185
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
171 additions
and
0 deletions.
There are no files selected for viewing
82 changes: 82 additions & 0 deletions
82
website/blog/2024-12-23-advent-of-pbt-day-23/AdventOfTheDay.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<ReturnType<typeof adventBuggy>>) => { | ||
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]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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? 🎄✨ | ||
|
||
<!--truncate--> | ||
|
||
## 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! 🎄✨ | ||
|
||
<AdventPlaygroundOfTheDay /> | ||
|
||
## Your answer | ||
|
||
<FormOfTheDay /> | ||
|
||
## Comments | ||
|
||
<BlueskyComments url="" /> |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.