Skip to content

Commit

Permalink
📝 Advent of PBT, day 4 (#5474)
Browse files Browse the repository at this point in the history
  • Loading branch information
dubzzz authored Dec 4, 2024
1 parent e39ff48 commit ba70ad1
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 0 deletions.
91 changes: 91 additions & 0 deletions website/blog/2024-12-04-advent-of-pbt-day-4/AdventOfTheDay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import adventBuggy from './buggy.mjs';
import { buildAdventOfTheDay } from '../2024-12-01-advent-of-pbt-day-1/AdventOfTheDayBuilder';

const { AdventPlaygroundOfTheDay, FormOfTheDay } = buildAdventOfTheDay({
day: 4,
buildBuggyAdvent: adventBuggy,
referenceAdvent: fastPostOfficeFinderEmulator,
postAdvent: (value) => Number(value) >= 0 && Number(value) <= 14,
parser,
placeholderForm: 'initial:0,0\nbox:0,0',
functionName: 'fastPostOfficeFinderEmulator',
signature: 'function fastPostOfficeFinderEmulator(initialPosition: Position, targetPosition: Position): number;',
signatureExtras: ['type Position = { x: number; y: number };'],
});

export { AdventPlaygroundOfTheDay, FormOfTheDay };

// Reference implementation

const SizeX = 10000;
const SizeY = 1000;

type Position = { x: number; y: number };

function fastPostOfficeFinderEmulator(initialPosition: Position, targetPosition: Position): number {
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;
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;
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;
}

// Inputs parser

const initialRegex = /^initial\s*:\s*(\d{1,4})\s*,\s*(\d{1,3})$/;
const boxRegex = /^box\s*:\s*(\d{1,4})\s*,\s*(\d{1,3})$/;

function parser(answer: string): unknown[] | undefined {
const lines = answer.trim().split('\n');
if (lines.length !== 2) {
throw new Error(
`Your answer should be made of two distinct lines: the first line for the initial location, the second one for the box`,
);
}
const mInit = initialRegex.exec(lines[0]);
if (mInit === null) {
throw new Error(
'The initial location should be of the form: `initial:x,y` with x between 0 and 9999 and y between 0 and 999',
);
}
const mBox = boxRegex.exec(lines[1]);
if (mBox === null) {
throw new Error(
'The box location should be of the form: `box:x,y` with x between 0 and 9999 and y between 0 and 999',
);
}
return [
{ x: Number(mInit[1]), y: Number(mInit[2]) },
{ x: Number(mBox[1]), y: Number(mBox[2]) },
];
}
51 changes: 51 additions & 0 deletions website/blog/2024-12-04-advent-of-pbt-day-4/buggy.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// @ts-check

export default function advent() {
/** @typedef {{x: number; y: number;}} Position */

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
}
}
}
return numMoves;
};
}
42 changes: 42 additions & 0 deletions website/blog/2024-12-04-advent-of-pbt-day-4/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
title: Advent of PBT 2024 · Day 4
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: he’s struggling with a slow system for locating letters on his massive grid of post office boxes. The elves proposed a faster method, but Santa suspects it might not work as intended. Can you make sure their implementation is up to the task? 🎄🔧

<!--truncate-->

## Post Office finder

Each post office has a direct connection to Santa’s land. Whenever someone sends a letter to Santa, the post office forwards it to Santa’s massive wall of boxes. This wall is a 10,000 (width) by 1,000 (height) grid, where each box corresponds to a specific post office. When a letter arrives, the corresponding box starts beeping, and Santa has to locate and open it.

Today, when a box starts beeping, Santa uses an elevator to move one box at a time across the massive grid to locate the source of the sound. Watching how painful and slow this process was, the elves stepped in with a suggestion to make it faster while keeping the elevator in place.

Instead of moving box by box, they proposed that Santa use directional arrows — <kbd>←</kbd>, <kbd>→</kbd>, <kbd>↑</kbd>, <kbd>↓</kbd>, <kbd>↖</kbd>, <kbd>↗</kbd>, <kbd>↘</kbd>, <kbd>↙</kbd> — to tell the elevator where the sound is coming from. Based on this input, the elevator would immediately jump to a new location. If it lands directly on the correct box, the process ends. Otherwise, Santa repeats the process by providing another direction until the target is reached.

## Hands on

The elves claimed that their system can reduce the search from 10,000,000 moves to at most 14 moves (not one more). However, Santa isn’t fully convinced. He asked the elves to create an emulator that simulates the process. This emulator takes:

- An initial position (x, y) where elevator starts.
- The target box position (x, y) where the beep originates.

The emulator outputs the number of moves required for the elevator to reach the beeping box. In simpler terms, the elves provided a function to compute the number of moves needed to navigate Santa’s 10,000 by 1,000 grid from one coordinate `{x, y}` to another. Santa now needs your help to test if this function works correctly—or if the elves made a mistake.

Using the property-based testing features of fast-check, your task is to find a combination of initial position and box position that breaks the elves’ implementation.

You are Santa’s last hope to ensure Christmas happens this year — don’t let him down! 🎄🔧

<AdventPlaygroundOfTheDay />

## Your answer

<FormOfTheDay />

0 comments on commit ba70ad1

Please sign in to comment.