-
-
Notifications
You must be signed in to change notification settings - Fork 186
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
3 changed files
with
208 additions
and
0 deletions.
There are no files selected for viewing
105 changes: 105 additions & 0 deletions
105
website/blog/2024-12-12-advent-of-pbt-day-12/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,105 @@ | ||
import adventBuggy from './buggy.mjs'; | ||
import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder'; | ||
|
||
const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({ | ||
day: 12, | ||
buildBuggyAdvent: adventBuggy, | ||
referenceAdvent: planFastTravel, | ||
postAdvent: (route: Track[] | undefined) => | ||
route !== undefined ? route.reduce((acc, track) => acc + track.distance, 0) : undefined, | ||
parser, | ||
placeholderForm: 'a>b=6\nb>c=8\nc>b=6\na>c?', | ||
functionName: 'planFastTravel', | ||
signature: 'planFastTravel(departure: string, destination: string, tracks: Track[]): Track[] | undefined;', | ||
signatureExtras: ['type Track = { from: string; to: string; distance: number };'], | ||
}); | ||
|
||
export { AdventPlaygroundOfTheDay, FormOfTheDay }; | ||
|
||
// Reference implementation | ||
|
||
type Track = { from: string; to: string; distance: number }; | ||
|
||
function planFastTravel(departure: string, destination: string, tracks: Track[]): Track[] | undefined { | ||
const distanceToNode = new Map<string, { distance: number; edges: Track[] }>( | ||
[departure, destination, ...tracks.map((t) => t.from), ...tracks.map((t) => t.to)].map((node) => [ | ||
node, | ||
{ distance: Number.POSITIVE_INFINITY, edges: [] }, | ||
]), | ||
); | ||
if (distanceToNode.has(departure)) { | ||
distanceToNode.set(departure, { distance: 0, edges: [] }); | ||
} | ||
while (true) { | ||
const nextNode = findRemainingNodeWithMinimalDistance(distanceToNode); | ||
if (nextNode === undefined) { | ||
return undefined; // no path found | ||
} | ||
const data = distanceToNode.get(nextNode)!; | ||
if (nextNode === destination) { | ||
return data.edges; | ||
} | ||
distanceToNode.delete(nextNode); | ||
for (const e of tracks) { | ||
if ( | ||
e.from === nextNode && | ||
distanceToNode.has(e.to) && | ||
distanceToNode.get(e.to)!.distance > data.distance + e.distance | ||
) { | ||
distanceToNode.set(e.to, { | ||
distance: data.distance + e.distance, | ||
edges: [...data.edges, e], | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
|
||
function findRemainingNodeWithMinimalDistance( | ||
distanceToNode: Map<string, { distance: number; edges: Track[] }>, | ||
): string | undefined { | ||
let minNode: string | undefined = undefined; | ||
let minDistance = Number.POSITIVE_INFINITY; | ||
for (const [node, { distance }] of distanceToNode) { | ||
if (distance < minDistance) { | ||
minNode = node; | ||
minDistance = distance; | ||
} | ||
} | ||
return minNode; | ||
} | ||
|
||
// Inputs parser | ||
|
||
const routeRegex = /^([a-z])>([a-z])=(\d+)$/; | ||
const queryRegex = /^([a-z])>([a-z])\?$/; | ||
|
||
function parser(answer: string): unknown[] | undefined { | ||
const lines = answer.trim().split('\n'); | ||
if (lines.length < 1) { | ||
throw new Error(`Your answer should be made of at least one line`); | ||
} | ||
const tracks: Track[] = []; | ||
for (let i = 0; i < lines.length - 1; ++i) { | ||
const m = routeRegex.exec(lines[i]); | ||
if (m === null) { | ||
throw new Error( | ||
`All lines except the last one should declare edges of the form: a>b=distance. Received: ${lines[i]}.`, | ||
); | ||
} | ||
const distance = Number(m[3]); | ||
if (distance <= 0 || distance > 2 ** 31 - 1 || Number.isNaN(distance) || !Number.isInteger(distance)) { | ||
throw new Error( | ||
`All lines except the last one should declare edges with distance in [1, 2**31-1]. Received: ${lines[i]}.`, | ||
); | ||
} | ||
tracks.push({ from: m[1], to: m[2], distance }); | ||
} | ||
const m = queryRegex.exec(lines[lines.length - 1]); | ||
if (m === null) { | ||
throw new Error( | ||
`The last line must be a query for a route of the form: a>b?. Received: ${lines[lines.length - 1]}.`, | ||
); | ||
} | ||
return [m[1], m[2], tracks]; | ||
} |
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 @@ | ||
// @ts-check | ||
|
||
export default function advent() { | ||
/** @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) { | ||
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 | ||
} | ||
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] }; | ||
} | ||
} | ||
} | ||
}; | ||
|
||
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; | ||
} | ||
} |
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,52 @@ | ||
--- | ||
title: Advent of PBT 2024 · Day 12 | ||
authors: [dubzzz] | ||
tags: [advent-of-pbt, advent-of-pbt-2024] | ||
--- | ||
|
||
import {AdventPlaygroundOfTheDay,FormOfTheDay} from './AdventOfTheDay'; | ||
|
||
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. Santa just pinged you with your next challenge: the elves’ newly released routing system for the sleigh might have issues. Can you uncover potential flaws and ensure Santa reaches his destination in record time? 🎄✨ | ||
|
||
<!--truncate--> | ||
|
||
## Fast Travel Planner | ||
|
||
_"Tomorrow is the big day!"_ Santa announces with excitement. | ||
|
||
_"What big day?"_ you wonder, slightly puzzled. | ||
|
||
It turns out Santa has been eagerly awaiting React Day Berlin! A fan of cutting-edge dev techniques and knowledge sharing, he’s been counting down to this conference since last year. It’s his ideal last-minute break to explore the latest React trends before the Christmas rush. | ||
|
||
This morning, Santa’s elves proudly unveiled the latest iteration of the sleigh’s routing system — a sophisticated algorithm designed to calculate the fastest route for delivering gifts to children worldwide. But Santa has another plan: to test the system before Christmas during his trip to the conference. | ||
|
||
The algorithm operates on a list of all known routes in Santa’s network, with each route defined as a directed edge:: | ||
|
||
- `from`: The starting point of the route, represented as a string consisting of a single lowercase letter (`a` to `z`). | ||
- `to`: The destination of the route, represented as a string consisting of a single lowercase letter (`a` to `z`). | ||
- `distance`: The time it takes to travel, expressed as a strictly positive integer being less than `2**31-1`. | ||
|
||
Given a starting location and a desired destination, the algorithm either: | ||
|
||
- Returns the routes to take for the fastest journey, or | ||
- Indicates that no path exists to the destination. | ||
|
||
## Hands on | ||
|
||
There’s just one problem. While the algorithm was released this morning, it’s still in its testing phase. Santa, however, insists on using it now and absolutely cannot be late for React Day Berlin. | ||
|
||
That’s where you come in! | ||
|
||
Your mission: rigorously test the sleigh’s routing algorithm to ensure it finds the most efficient path from start to destination. | ||
|
||
Christmas — and Santa’s conference dreams — are counting on you! Don’t let him down. 🎄✨ | ||
|
||
<AdventPlaygroundOfTheDay /> | ||
|
||
## Your answer | ||
|
||
<FormOfTheDay /> |