diff --git a/client/i18n/locales/english/intro.json b/client/i18n/locales/english/intro.json index 6895f526054..e2f3a31425b 100644 --- a/client/i18n/locales/english/intro.json +++ b/client/i18n/locales/english/intro.json @@ -4922,6 +4922,12 @@ "In this lab, you will implement loops to repeat a string a specified number of times." ] }, + "workshop-festival-crowd-flow-simulator": { + "title": "Build a Festival Crowd Flow Simulator", + "intro": [ + "In this workshop, you will use JavaScript to simulate the flow of attendants at a music festival." + ] + }, "lab-missing-letter-detector": { "title": "Build a Missing Letter Detector", "intro": [ diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69baafd6d683dd9921b75db3.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69baafd6d683dd9921b75db3.md new file mode 100644 index 00000000000..1afa376a905 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69baafd6d683dd9921b75db3.md @@ -0,0 +1,75 @@ +--- +id: 69baafd6d683dd9921b75db3 +title: Step 1 +challengeType: 1 +dashedName: step-1 +--- + +# --description-- + +In this workshop, you will use JavaScript to simulate the flow of attendees at a music festival. + +You will work with two datasets that describe the festival gates during morning and night shifts: + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; +``` + +Each gate object contains the following properties: + +- `id`: A string that identifies the gate. +- `capacity`: The number of attendees the gate can process per tick. +- `queue`: An array of numbers representing how many attendees arrive at the gate during a specific tick. + +To begin, create an empty function named `initializeThroughput` that accepts a parameter named `gates`. + +# --hints-- + +You should create a function named `initializeThroughput`. + +```js +assert.isFunction(initializeThroughput); +``` + +Your `initializeThroughput` function should have a `gates` parameter. + +```js +const regex = __helpers.functionRegex('initializeThroughput', ['gates']); +assert.match(__helpers.removeJSComments(initializeThroughput.toString()), regex); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bab8292750d84cac5b52a0.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bab8292750d84cac5b52a0.md new file mode 100644 index 00000000000..ca764ac7a35 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bab8292750d84cac5b52a0.md @@ -0,0 +1,61 @@ +--- +id: 69bab8292750d84cac5b52a0 +title: Step 2 +challengeType: 1 +dashedName: step-2 +--- + +# --description-- + +In this workshop, throughput refers to the total number of attendees processed by a gate across all ticks in a single simulation (morning or night). Your `initializeThroughput` function will prepare an object to track how many attendees each gate processes. + +First, create an empty object named `summary`. + +# --hints-- + +Inside your `initializeThroughput` function, create an empty object and assign it to a variable named `summary`. + +```js +const funcStr = __helpers.removeJSComments(initializeThroughput.toString()); +assert.match( + funcStr, + /(var|let|const)\s+summary/, + "You must have at least one space between the declaration keyword (let/const) and 'summary'" +); +const cleaned = __helpers.removeWhiteSpace(__helpers.removeJSComments(initializeThroughput.toString())); +const viable_solutions = [ +"functioninitializeThroughput(gates){letsummary={};}", +"functioninitializeThroughput(gates){letsummary={}}", +"functioninitializeThroughput(gates){constsummary={};}", +"functioninitializeThroughput(gates){constsummary={}}", +"functioninitializeThroughput(gates){varsummary={};}", +"functioninitializeThroughput(gates){varsummary={}}" +]; +assert.include(viable_solutions, cleaned); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babdf3f896711c11516373.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babdf3f896711c11516373.md new file mode 100644 index 00000000000..56369bc8ebe --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babdf3f896711c11516373.md @@ -0,0 +1,68 @@ +--- +id: 69babdf3f896711c11516373 +title: Step 4 +challengeType: 1 +dashedName: step-4 +--- + +# --description-- + +During each simulation tick: + +- A certain number of attendees arrive at the gate (from its `queue`). +- The gate processes attendees based on its `capacity`. +- If more attendees arrive than the gate can handle, some will remain (overflow). + +You will now build a function that handles this logic for one gate at a single tick. + +Create an empty function named `processGateFlow` that accepts two parameters: + +- `gate`: The gate object being processed. +- `tickIndex`: The current simulation tick position in the `queue` array. + +# --hints-- + +You should create a function named `processGateFlow`. + +```js +assert.isFunction(processGateFlow); +``` + +Your function `processGateFlow` should have parameters `gate` and `tickIndex`. + +```js +const regex = __helpers.functionRegex('processGateFlow', ['gate', 'tickIndex']); +assert.match(__helpers.removeJSComments(processGateFlow.toString()), regex); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babea99725b43a01adb6d1.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babea99725b43a01adb6d1.md new file mode 100644 index 00000000000..3efe960980e --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babea99725b43a01adb6d1.md @@ -0,0 +1,65 @@ +--- +id: 69babea99725b43a01adb6d1 +title: Step 5 +challengeType: 1 +dashedName: step-5 +--- + +# --description-- + +First, you need to get the number of attendees arriving during the current tick. Access the value in the input gate's `queue` array at index `tickIndex`, and assign it to a variable named `currentTickQueue`. + +# --hints-- + +You should assign the element at `gate.queue[tickIndex]` to a variable named `currentTickQueue`. + +```js +const rawFuncStr = __helpers.removeJSComments(processGateFlow.toString()); +assert.match( + rawFuncStr, + /(var|const|let)\s+currentTickQueue/, + "You must have at least one space between the declaration keyword (let/const) and 'currentTickQueue'." +); + +const cleaned = __helpers.removeWhiteSpace(__helpers.removeJSComments(processGateFlow.toString())); +const solution = /(var|const|let)currentTickQueue=gate\.queue\[tickIndex\];?}$/; + +assert( + solution.test(cleaned), + "Inside your `processGateFlow` function, assign the element at `gate.queue[tickIndex]` to a variable named `currentTickQueue` as the only statement inside the function." +); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babf2bf78bade75fda0326.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babf2bf78bade75fda0326.md new file mode 100644 index 00000000000..4f771293c9a --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babf2bf78bade75fda0326.md @@ -0,0 +1,66 @@ +--- +id: 69babf2bf78bade75fda0326 +title: Step 6 +challengeType: 1 +dashedName: step-6 +--- + +# --description-- + +Next, you need a way to track how many attendees are processed by the gate during the tick. Create a variable named `processed` and initialize it to `0`. + +# --hints-- + +You should create a variable named `processed` and assign it a value of `0`. + +```js +const rawFuncStr = __helpers.removeJSComments(processGateFlow.toString()); +assert.match( + rawFuncStr, + /(var|const|let)\s+processed/, + "You must have at least one space between the declaration keyword (let/const) and 'processed'." +); + +const cleaned = __helpers.removeWhiteSpace(__helpers.removeJSComments(processGateFlow.toString())); +const solution = /(var|const|let)processed=0;?}$/; + +assert( + solution.test(cleaned), + "Inside your `processGateFlow` function, create a variable named `processed` and initialize it to 0." +); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babf3f446433af8d065bb5.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babf3f446433af8d065bb5.md new file mode 100644 index 00000000000..f7a7fb1b966 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babf3f446433af8d065bb5.md @@ -0,0 +1,69 @@ +--- +id: 69babf3f446433af8d065bb5 +title: Step 7 +challengeType: 1 +dashedName: step-7 +--- + +# --description-- + +Create an empty `while` loop that continues as long as there are attendees waiting (`currentTickQueue > 0`) and the gate has remaining capacity (`processed < gate.capacity`). + +# --hints-- + +Your `while` loop's condition should be `currentTickQueue > 0` and `processed < gate.capacity`. + +```js +const cleaned = __helpers.removeWhiteSpace(__helpers.removeJSComments(code)); +const viableSolutions = [ + "while(currentTickQueue>0&&processed0)&&(processed0){}}", + "while((processed0)){}}", + "while(currentTickQueue>0&&processed0)&&(processed0){};}", + "while((processed0)){};}" +]; +const endsCorrectly = viableSolutions.some(sol => cleaned.endsWith(sol)); +assert( + endsCorrectly, + "Your while loop must have a condition with both `currentTickQueue > 0` and `processed < gate.capacity`, in any order." +); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babfc35c3c58d0993d404d.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babfc35c3c58d0993d404d.md new file mode 100644 index 00000000000..7338ba19a63 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babfc35c3c58d0993d404d.md @@ -0,0 +1,135 @@ +--- +id: 69babfc35c3c58d0993d404d +title: Step 9 +challengeType: 1 +dashedName: step-9 +--- + +# --description-- + +To complete your `processGateFlow` function, return an object containing two properties: + +- `processed`: The number of attendees the gate was able to process during this tick. +- `overflow`: The number of attendees left in the queue after processing (which is `currentTickQueue` after the loop). + +You should directly return the object as such: + +```js +return { + firstKey: firstValue, + secondKey: secondValue +} +``` + +# --hints-- + +You should return an object with properties `processed` and `overflow`. Directly return the object without assignment to a variable. + +```js +const cleaned = __helpers.removeWhiteSpace(__helpers.removeJSComments(processGateFlow.toString())); +const viableSolutions = [ + "return{processed:processed,overflow:currentTickQueue}}", + "return{processed:processed,overflow:currentTickQueue,}}", + "return{overflow:currentTickQueue,processed:processed,}}", + "return{overflow:currentTickQueue,processed:processed}}", + "return{processed:processed,overflow:currentTickQueue};}", + "return{processed:processed,overflow:currentTickQueue,};}", + "return{overflow:currentTickQueue,processed:processed,};}", + "return{overflow:currentTickQueue,processed:processed};}", +]; +const endsCorrectly = viableSolutions.some(sol => cleaned.endsWith(sol)); +assert( + endsCorrectly, + "Your processGateFlow function must return the object with processed and overflow properties." +); + +const northGate = morningGates[0]; +const result = processGateFlow(northGate, 0); +assert( + typeof result === 'object' && result !== null && !Array.isArray(result), + 'Your function should return an object' +); +assert.hasAllKeys( + result, + ['processed', 'overflow'], + 'Your returned object should have properties `processed` and `overflow`' +); +``` + +Your returned object's `processed` property should have the value `processed`. + +```js +const northGate = morningGates[0]; +const result = processGateFlow(northGate, 0); +assert.strictEqual( + result.processed, + 3, + 'For the North gate at tick 0 with capacity 5 and queue 3, processed should be 3' +); +const westGate = morningGates[3]; +const resultWest = processGateFlow(westGate, 0); +assert.strictEqual( + resultWest.processed, + 2, + 'For the West gate at tick 0 with capacity 2 and queue 4, processed should be 2' +); +``` + +Your returned object's `overflow` property should have the value `currentTickQueue`. + +```js +const northGate = morningGates[0]; +const result = processGateFlow(northGate, 0); +assert.strictEqual( + result.overflow, + 0, + 'For the North gate at tick 0 with capacity 5 and queue 3, overflow should be 0' +); +const westGate = morningGates[3]; +const resultWest = processGateFlow(westGate, 0); +assert.strictEqual( + resultWest.overflow, + 2, + 'For the West gate at tick 0 with capacity 2 and queue 4, overflow should be 2' +); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babfd65c4c4632a0c76f63.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babfd65c4c4632a0c76f63.md new file mode 100644 index 00000000000..41a5f9edb3b --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69babfd65c4c4632a0c76f63.md @@ -0,0 +1,77 @@ +--- +id: 69babfd65c4c4632a0c76f63 +title: Step 10 +challengeType: 1 +dashedName: step-10 +--- + +# --description-- + +When a gate cannot process all attendees during a tick, some attendees remain in the queue. To handle this overflow, you will build a function that reroutes overflow attendees to another gate. + +Create an empty function named `rerouteOverflow` with the following parameters: + +- `gates`: The full array of gate objects. +- `currentGate`: The gate where the overflow occurred. +- `tickIndex`: The current tick position in the queue. +- `overflowAmount`: The number of attendees that could not be processed. + +# --hints-- + +You should create a function named `rerouteOverflow`. + +```js +assert.isFunction(rerouteOverflow); +``` + +Your `rerouteOverflow` function should have parameters `gates`, `currentGate`, `tickIndex`, and `overflowAmount`. + +```js +const regex = __helpers.functionRegex('rerouteOverflow', ['gates', 'currentGate', 'tickIndex', 'overflowAmount']); +assert.match(__helpers.removeJSComments(rerouteOverflow.toString()), regex); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac013466bd9bad0b809b1.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac013466bd9bad0b809b1.md new file mode 100644 index 00000000000..9feb18def07 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac013466bd9bad0b809b1.md @@ -0,0 +1,86 @@ +--- +id: 69bac013466bd9bad0b809b1 +title: Step 11 +challengeType: 1 +dashedName: step-11 +--- + +# --description-- + +First, you will find the position of a specific gate within the `gates` array. + +Use the `indexOf()` method on `gates` to locate the `currentGate` element and assign the result to a variable named `currentIndex`. + +Here is a refresher on how this can be done: + +```js +const index = array.indexOf(element); +``` + +# --hints-- + +You should use `indexOf()` on the `gates` array to access the index of `currentGate` and assign it to a variable named `currentIndex`. This should be done in one line. + +```js +const rawFuncStr = __helpers.removeJSComments(rerouteOverflow.toString()); +assert.match( + rawFuncStr, + /(var|const|let)\s+currentIndex/, + "You must have at least one space between the declaration keyword (let/const) and 'currentIndex'." +); + +const cleaned = __helpers.removeWhiteSpace(rawFuncStr); +const solution = /(var|const|let)currentIndex=gates\.indexOf\(currentGate\);?}$/; + +assert( + solution.test(cleaned), + "Inside your `rerouteOverflow` function, use `indexOf()` on the `gates` array to access the index of `currentGate` and assign it to a variable named `currentIndex`." +); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac027df64d4433c708d44.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac027df64d4433c708d44.md new file mode 100644 index 00000000000..2c82447e281 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac027df64d4433c708d44.md @@ -0,0 +1,89 @@ +--- +id: 69bac027df64d4433c708d44 +title: Step 12 +challengeType: 1 +dashedName: step-12 +--- + +# --description-- + +When rerouting overflow, you should send attendees to the next gate in the `gates` array. To do this, you need to find the index of the next gate. Normally, you could add 1 to the current index. However, if the current gate is the last one in the array, you need to wrap back to the first gate. + +You can use the modulo operator (`%`) to handle this: + +```js +const nextIndex = (currentIndex + 1) % array.length; +``` + +This works because when `currentIndex + 1` equals the `array.length`, the result becomes 0. + +Using this approach, create a variable named `nextGateIndex` that stores the index of the next gate in the `gates` array. + +# --hints-- + +You should use the modulo technique to access and assign the next index from `currentIndex` in the `gates` array to a variable named `nextGateIndex`. Follow the same one-line pattern in the example. + +```js +const rawFuncStr = __helpers.removeJSComments(rerouteOverflow.toString()); +assert.match( + rawFuncStr, + /(var|const|let)\s+nextGateIndex/, + "You must have at least one space between the declaration keyword (let/const) and 'nextGateIndex'." +); + +const cleaned = __helpers.removeWhiteSpace(rawFuncStr); +const solution = /(var|const|let)nextGateIndex=\((currentIndex\+1|1\+currentIndex)\)%gates\.length;?}$/; + +assert( + solution.test(cleaned), + "Inside your `rerouteOverflow` function, use the modulo technique to create a variable named `nextGateIndex` that stores the index of the next gate." +); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac03ae5faf2979a3339ff.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac03ae5faf2979a3339ff.md new file mode 100644 index 00000000000..7922aef6e68 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac03ae5faf2979a3339ff.md @@ -0,0 +1,117 @@ +--- +id: 69bac03ae5faf2979a3339ff +title: Step 13 +challengeType: 1 +dashedName: step-13 +--- + +# --description-- + +Now that you know which gate to reroute to, you can move the overflow attendees. + +Take the `overflowAmount` and add it to the next gate’s queue at the same `tickIndex`. + +Specifically, you must: + +- Access the next gate using `nextGateIndex`. +- Access its `queue`. +- Add the `overflowAmount` to the value at position `tickIndex`. + +# --hints-- + +Your `rerouteOverflow` function should add `overflowAmount` to the value at `tickIndex` in the `queue` of the next gate. + +```js +const testGates1 = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; +const northGate = testGates1[0]; +const originalEastQueue = testGates1[1].queue[0]; +rerouteOverflow(testGates1, northGate, 0, 5); +assert.strictEqual( + testGates1[1].queue[0], + originalEastQueue + 5, + "Rerouting from North gate should add overflow to East gate's queue at tickIndex 0" +); + +const testGates2 = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; +const westGate = testGates2[3]; +const originalNorthQueue = testGates2[0].queue[2]; +rerouteOverflow(testGates2, westGate, 2, 3); +assert.strictEqual( + testGates2[0].queue[2], + originalNorthQueue + 3, + "Rerouting from West gate should wrap around and add overflow to North gate's queue at tickIndex 2" +); + +const testGates3 = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; +const southGate = testGates3[2]; +const originalQueues = testGates3.map(gate => [...gate.queue]); +rerouteOverflow(testGates3, southGate, 1, 2); +assert.deepStrictEqual(testGates3[0].queue, originalQueues[0], "North gate queue should not be modified"); +assert.deepStrictEqual(testGates3[1].queue, originalQueues[1], "East gate queue should not be modified"); +assert.deepStrictEqual(testGates3[2].queue, originalQueues[2], "South gate queue should not be modified"); +assert.strictEqual(testGates3[3].queue[1], originalQueues[3][1] + 2, "Only West gate at tickIndex 1 should be modified"); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac047282614c7ef87d370.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac047282614c7ef87d370.md new file mode 100644 index 00000000000..3cf7cdfca78 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac047282614c7ef87d370.md @@ -0,0 +1,93 @@ +--- +id: 69bac047282614c7ef87d370 +title: Step 14 +challengeType: 1 +dashedName: step-14 +--- + +# --description-- + +To help track how attendees move through the system, log the following message to the console: `overflowAmount + " attendees rerouted to " + gates[nextGateIndex].id`. You can use either string concatenation or template literals. + +# --hints-- + +Your `rerouteOverflow` function should log the following message to the console `overflowAmount + " attendees rerouted to " + gates[nextGateIndex].id`. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; + +const spy = __helpers.spyOn(console, "log"); + +try { + const northGate = testGates[0]; + rerouteOverflow(testGates, northGate, 0, 5); + + const expectedCall = ["5 attendees rerouted to East"]; + + assert.deepEqual( + spy.calls[0], + expectedCall, + "rerouteOverflow should log the overflow amount and next gate's id" + ); + +} catch (err) { + assert.fail(err); +} finally { + spy.restore(); +} +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac067cd15c4685d07906d.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac067cd15c4685d07906d.md new file mode 100644 index 00000000000..9a7d2370352 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac067cd15c4685d07906d.md @@ -0,0 +1,87 @@ +--- +id: 69bac067cd15c4685d07906d +title: Step 15 +challengeType: 1 +dashedName: step-15 +--- + +# --description-- + +Now that you have functions to process individual ticks and handle overflow, it’s time to create a controller function for a single gate. This function will handle all the actions for one gate during a single tick of the simulation. + +Create an empty function named `handleGateAtTick` with parameters: + +- `gates`: The full array of gate objects. +- `gate`: The current gate being processed. +- `tickIndex`: The current tick index for the simulation. +- `throughputSummary`: An object tracking total processed attendees per gate. + +# --hints-- + +You should create a function named `handleGateAtTick`. + +```js +assert.isFunction(handleGateAtTick); +``` + +Your `handleGateAtTick` function should have parameters `gates`, `gate`, `tickIndex`, and `throughputSummary`. + +```js +const regex = __helpers.functionRegex('handleGateAtTick', ['gates', 'gate', 'tickIndex', 'throughputSummary']); +assert.match(__helpers.removeJSComments(handleGateAtTick.toString()), regex); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac073b1de8b484bbccc94.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac073b1de8b484bbccc94.md new file mode 100644 index 00000000000..012a8988787 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac073b1de8b484bbccc94.md @@ -0,0 +1,125 @@ +--- +id: 69bac073b1de8b484bbccc94 +title: Step 16 +challengeType: 1 +dashedName: step-16 +--- + +# --description-- + +First, log the following message to the console: `"\nProcessing " + gate.id + "..."`. + +# --hints-- + +Your `handleGateAtTick` function should first log the following message to the console, `"\nProcessing " + gate.id + "..."`. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; +const throughputSummary = { North: 0, East: 0, South: 0, West: 0 }; + +const spy = __helpers.spyOn(console, "log"); +try { + const northGate = testGates[0]; + handleGateAtTick(testGates, northGate, 0, throughputSummary); + const expectedCall = ["\nProcessing North..."]; + assert.deepEqual( + spy.calls[0], + expectedCall, + "handleGateAtTick should first log the processing message with the gate id" + ); +} catch (err) { + assert.fail(err); +} finally { + spy.restore(); +} +``` + +Your `handleGateAtTick` function should log the correct gate ID in the message. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; +const throughputSummary = { North: 0, East: 0, South: 0, West: 0 }; + +const spy = __helpers.spyOn(console, "log"); +try { + const southGate = testGates[2]; + handleGateAtTick(testGates, southGate, 1, throughputSummary); + const expectedCall = ["\nProcessing South..."]; + assert.deepEqual( + spy.calls[0], + expectedCall, + "handleGateAtTick should log the processing message with the correct gate id" + ); +} catch (err) { + assert.fail(err); +} finally { + spy.restore(); +} +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + " " + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac08105bec16db267dcbf.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac08105bec16db267dcbf.md new file mode 100644 index 00000000000..4673dab5b5f --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac08105bec16db267dcbf.md @@ -0,0 +1,139 @@ +--- +id: 69bac08105bec16db267dcbf +title: Step 17 +challengeType: 1 +dashedName: step-17 +--- + +# --description-- + +Next, log the following message to the console: `gate.queue[tickIndex] + " attendees arriving."`. + +# --hints-- + +Your `handleGateAtTick` function should log the following message to the console: `gate.queue[tickIndex] + " attendees arriving."`. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; +const throughputSummary = { North: 0, East: 0, South: 0, West: 0 }; + +const spy = __helpers.spyOn(console, "log"); +try { + const northGate = testGates[0]; + handleGateAtTick(testGates, northGate, 0, throughputSummary); + const expectedFirstCall = ["\nProcessing North..."]; + const expectedSecondCall = ["3 attendees arriving."]; + assert.deepEqual( + spy.calls[0], + expectedFirstCall, + "First log should be the processing message" + ); + assert.deepEqual( + spy.calls[1], + expectedSecondCall, + "Second log should show the number of attendees arriving" + ); + +} catch (err) { + assert.fail(err); +} finally { + spy.restore(); +} +``` + +Your `handleGateAtTick` function should log the correct number of attendees based on the gate's queue at the given tickIndex. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; +const throughputSummary = { North: 0, East: 0, South: 0, West: 0 }; + +const spy = __helpers.spyOn(console, "log"); +try { + const eastGate = testGates[1]; + handleGateAtTick(testGates, eastGate, 3, throughputSummary); + const expectedFirstCall = ["\nProcessing East..."]; + const expectedSecondCall = ["5 attendees arriving."]; + assert.deepEqual( + spy.calls[0], + expectedFirstCall, + "First log should be the processing message with the correct gate" + ); + assert.deepEqual( + spy.calls[1], + expectedSecondCall, + "Second log should show the correct number of attendees from queue at tickIndex 3" + ); +} catch (err) { + assert.fail(err); +} finally { + spy.restore(); +} +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac0a011a9c1f735d306c2.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac0a011a9c1f735d306c2.md new file mode 100644 index 00000000000..40b5ae79c7a --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac0a011a9c1f735d306c2.md @@ -0,0 +1,123 @@ +--- +id: 69bac0a011a9c1f735d306c2 +title: Step 18 +challengeType: 1 +dashedName: step-18 +--- + +# --description-- + +Process the input gate for the current tick by calling `processGateFlow()` with `gate` and `tickIndex` and assigning the result to a variable named `result`. + +# --hints-- + +Your `handleGateAtTick` function should call the `processGateFlow()` method. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; +const throughputSummary = { North: 0, East: 0, South: 0, West: 0 }; +const originalProcessGateFlow = processGateFlow; + +let called = false; +try { + processGateFlow = function (...args) { + called = true; + return originalProcessGateFlow(...args); + }; + const northGate = testGates[0]; + handleGateAtTick(testGates, northGate, 0, throughputSummary); + assert.isTrue( + called, + "handleGateAtTick should call processGateFlow()" + ); +} catch (err) { + assert.fail(err); +} finally { + processGateFlow = originalProcessGateFlow; +} +``` + +You should call `processGateFlow()` with `gate` and `tickIndex` and assign the result to a variable named `result`. + +```js +const rawFuncStr = __helpers.removeJSComments(handleGateAtTick.toString()); +assert.match( + rawFuncStr, + /(var|const|let)\s+result/, + "You must have at least one space between the declaration keyword (let/const) and 'result'." +); + +const cleaned = __helpers.removeWhiteSpace(rawFuncStr); +const solution = /(var|const|let)result=processGateFlow\(gate,tickIndex\);?}$/; + +assert( + solution.test(cleaned), + "Inside your `handleGateAtTick` function, call `processGateFlow()` with `gate` and `tickIndex` and assign the result to a variable named `result`." +); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac0d5a0b16122a2a938a4.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac0d5a0b16122a2a938a4.md new file mode 100644 index 00000000000..c8eeafbb5e0 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac0d5a0b16122a2a938a4.md @@ -0,0 +1,171 @@ +--- +id: 69bac0d5a0b16122a2a938a4 +title: Step 19 +challengeType: 1 +dashedName: step-19 +--- + +# --description-- + +Next, update the input `throughputSummary` object to reflect how many attendees were successfully processed by this gate at the current tick. Specifically, take the value of `processed` in `result` and add it to the element in `throughputSummary` at index `gate.id`. + +This step ensures that the throughput summary accumulates attendees over multiple ticks. + +# --hints-- + +You should add `result.processed` to `throughputSummary[gate.id]`. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; +const throughputSummary = { North: 0, East: 0, South: 0, West: 0 }; +const console_log = console.log; +console.log = () => {}; + +try { + const northGate = testGates[0]; + handleGateAtTick(testGates, northGate, 0, throughputSummary); + assert.strictEqual( + throughputSummary["North"], + 3, + "North gate should have 3 attendees processed at tick 0 (capacity 5, queue 3)" + ); +} finally { + console.log = console_log; +} +``` + +Your `handleGateAtTick` function should accumulate processed attendees over multiple ticks. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; + +const throughputSummary = { North: 0, East: 0, South: 0, West: 0 }; +const console_log = console.log; +console.log = () => {}; + +try { + const northGate = testGates[0]; + handleGateAtTick(testGates, northGate, 0, throughputSummary); + handleGateAtTick(testGates, northGate, 1, throughputSummary); + assert.strictEqual( + throughputSummary["North"], + 8, + "North gate should accumulate processed attendees: 3 from tick 0 + 5 from tick 1" + ); +} finally { + console.log = console_log; +} +``` + +Your `handleGateAtTick` function should only update the throughput for the specific gate being processed. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; + +const throughputSummary = { North: 0, East: 0, South: 0, West: 0 }; +const console_log = console.log; +console.log = () => {}; +try { + const westGate = testGates[3]; + handleGateAtTick(testGates, westGate, 0, throughputSummary); + assert.strictEqual( + throughputSummary["West"], + 2, + "West gate should have 2 attendees processed (capacity 2, queue 4)" + ); + assert.strictEqual( + throughputSummary["North"], + 0, + "North gate should remain unmodified" + ); + assert.strictEqual( + throughputSummary["East"], + 0, + "East gate should remain unmodified" + ); + assert.strictEqual( + throughputSummary["South"], + 0, + "South gate should remain unmodified" + ); +} finally { + console.log = console_log; +} +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac0e7112d02531998dbf0.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac0e7112d02531998dbf0.md new file mode 100644 index 00000000000..8112f93c4a6 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac0e7112d02531998dbf0.md @@ -0,0 +1,160 @@ +--- +id: 69bac0e7112d02531998dbf0 +title: Step 20 +challengeType: 1 +dashedName: step-20 +--- + +# --description-- + +The final task in your `handleGateAtTick` function is to handle any overflow for the current tick. + +If any overflow exists (`result.overflow > 0`), you should: + +- Log the following message `"Overflow of " + result.overflow + " attendees. Rerouting..."`. +- Reroute the overflow by calling your `rerouteOverflow()` function with `gates`, `gate`, `tickIndex`, and `result.overflow`. + +# --hints-- + +If any overflow exists (`result.overflow > 0`), you should first log the message `"Overflow of " + result.overflow + " attendees. Rerouting..."`. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; + +const throughputSummary = { North: 0, East: 0, South: 0, West: 0 }; +const spy = __helpers.spyOn(console, "log"); +try { + const westGate = testGates[3]; + handleGateAtTick(testGates, westGate, 0, throughputSummary); + const overflowMessage = spy.calls.find(call => call[0].includes("Overflow of")); + assert.isTrue( + typeof overflowMessage !== "undefined", + "handleGateAtTick should log an overflow message when overflow exists" + ); + assert.match( + overflowMessage[0], + /Overflow of \d+ attendees\. Rerouting\.\.\.$/, + "Overflow message should match the format 'Overflow of X attendees. Rerouting...'" + ); +} catch (err) { + assert.fail(err); +} finally { + spy.restore(); +} +``` + +If any overflow exists (`result.overflow > 0`), you should reroute the overflow by calling `rerouteOverflow()` with `gates`, `gate`, `tickIndex`, and `result.overflow`. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; +const throughputSummary = { North: 0, East: 0, South: 0, West: 0 }; +const originalRerouteOverflow = rerouteOverflow; +let called = false; +let callArgs = null; +try { + rerouteOverflow = function (...args) { + called = true; + callArgs = args; + return originalRerouteOverflow(...args); + }; + const consoleSpy = __helpers.spyOn(console, "log"); + try { + const westGate = testGates[3]; + const originalNorthQueue = testGates[0].queue[0]; + handleGateAtTick(testGates, westGate, 0, throughputSummary); + assert.isTrue( + called, + "handleGateAtTick should call rerouteOverflow() when overflow exists" + ); + assert.deepEqual( + callArgs, + [testGates, westGate, 0, 2], + "rerouteOverflow should be called with gates, gate, tickIndex, and overflow amount" + ); + assert.strictEqual( + testGates[0].queue[0], + originalNorthQueue + 2, + "Overflow from West should be rerouted to North gate's queue (next gate with wrap-around)" + ); + } finally { + consoleSpy.restore(); + } +} catch (err) { + assert.fail(err); +} finally { + rerouteOverflow = originalRerouteOverflow; +} +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); + throughputSummary[gate.id] += result.processed; +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac0f9659af01091c5967d.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac0f9659af01091c5967d.md new file mode 100644 index 00000000000..9b1f0862e32 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac0f9659af01091c5967d.md @@ -0,0 +1,96 @@ +--- +id: 69bac0f9659af01091c5967d +title: Step 21 +challengeType: 1 +dashedName: step-21 +--- + +# --description-- + +It will be helpful to have a function for displaying a summary of throughput during a simulation. Create an empty function named `printSummary` with a parameter `summary`. + +# --hints-- + +You should create a function named `printSummary`. + +```js +assert.isFunction(printSummary); +``` + +Your `printSummary` function should have a parameter `summary`. + +```js +const regex = __helpers.functionRegex('printSummary', ['summary']); +assert.match(__helpers.removeJSComments(printSummary.toString()), regex); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); + throughputSummary[gate.id] += result.processed; + if (result.overflow > 0) { + console.log( + "Overflow of " + result.overflow + + " attendees. Rerouting..." + ); + rerouteOverflow(gates, gate, tickIndex, result.overflow); + } +} + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac11611f4c349e8e666ec.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac11611f4c349e8e666ec.md new file mode 100644 index 00000000000..ebd5f8596ee --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac11611f4c349e8e666ec.md @@ -0,0 +1,106 @@ +--- +id: 69bac11611f4c349e8e666ec +title: Step 22 +challengeType: 1 +dashedName: step-22 +--- + +# --description-- + +First, log the following message to the console `"\nThroughput Summary"`. + +# --hints-- + +Your `printSummary` function should first log the following message to the console, `"\nThroughput Summary"`. + +```js +const testSummary = { North: 5, East: 3, South: 4, West: 2 }; + +const spy = __helpers.spyOn(console, "log"); +try { + printSummary(testSummary); + const expectedCall = ["\nThroughput Summary"]; + assert.deepEqual( + spy.calls[0], + expectedCall, + "printSummary should log the throughput summary header" + ); +} catch (err) { + assert.fail(err); +} finally { + spy.restore(); +} +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); + throughputSummary[gate.id] += result.processed; + if (result.overflow > 0) { + console.log( + "Overflow of " + result.overflow + + " attendees. Rerouting..." + ); + rerouteOverflow(gates, gate, tickIndex, result.overflow); + } +} + +function printSummary(summary) { +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac12483f656898154f969.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac12483f656898154f969.md new file mode 100644 index 00000000000..3445087102d --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac12483f656898154f969.md @@ -0,0 +1,147 @@ +--- +id: 69bac12483f656898154f969 +title: Step 23 +challengeType: 1 +dashedName: step-23 +--- + +# --description-- + +Use a `for...in` loop to iterate through every gate in the input `summary` object and log the total attendees processed. + +For each gate, log a message in this format: `gate + ": " + summary[gate] + " attendees processed"`. + +As an example, for an input `summary` object defined as `{ North: 5, East: 3, South: 4, West: 2 }`, you should log: + +```js +North: 5 attendees processed +East: 3 attendees processed +South: 4 attendees processed +West: 2 attendees processed +``` + +# --hints-- + +You should loop through every element in the `summary` object and log the following message once per element: `element + ": " + summary[element] + " attendees processed`. + +```js +const testSummary = { North: 12, East: 8, South: 5, West: 4 }; + +const spy = __helpers.spyOn(console, "log"); + +try { + printSummary(testSummary); + + assert.lengthOf( + spy.calls, + 5, + "Should log 1 header message + 4 gate messages" + ); + + assert.deepEqual( + spy.calls[0], + ["\nThroughput Summary"], + "First log should be the header" + ); + + assert.match( + spy.calls[1][0], + /North: 12 attendees processed/, + "Should log throughput for North gate" + ); + assert.match( + spy.calls[2][0], + /East: 8 attendees processed/, + "Should log throughput for East gate" + ); + assert.match( + spy.calls[3][0], + /South: 5 attendees processed/, + "Should log throughput for South gate" + ); + assert.match( + spy.calls[4][0], + /West: 4 attendees processed/, + "Should log throughput for West gate" + ); + +} catch (err) { + assert.fail(err); +} finally { + spy.restore(); +} +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); + throughputSummary[gate.id] += result.processed; + if (result.overflow > 0) { + console.log( + "Overflow of " + result.overflow + + " attendees. Rerouting..." + ); + rerouteOverflow(gates, gate, tickIndex, result.overflow); + } +} + +function printSummary(summary) { + console.log("\nThroughput Summary"); +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac146b5f30209b4179af1.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac146b5f30209b4179af1.md new file mode 100644 index 00000000000..4bae31779cd --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac146b5f30209b4179af1.md @@ -0,0 +1,106 @@ +--- +id: 69bac146b5f30209b4179af1 +title: Step 24 +challengeType: 1 +dashedName: step-24 +--- + +# --description-- + +Now you can build a function for simulating the festival. Create an empty function named `simulateFestival` with parameters `gates` and `timeBlock`. + +# --hints-- + +You should create a function named `simulateFestival`. + +```js +assert.isFunction(simulateFestival); +``` + +Your `simulateFestival` function should have parameters `gates` and `timeBlock`. + +```js +const regex = __helpers.functionRegex('simulateFestival', ['gates', 'timeBlock']); +assert.match(__helpers.removeJSComments(simulateFestival.toString()), regex); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); + throughputSummary[gate.id] += result.processed; + if (result.overflow > 0) { + console.log( + "Overflow of " + result.overflow + + " attendees. Rerouting..." + ); + rerouteOverflow(gates, gate, tickIndex, result.overflow); + } +} + +function printSummary(summary) { + console.log("\nThroughput Summary"); + for (const gateId in summary) { + console.log( + gateId + ": " + summary[gateId] + + " attendees processed" + ); + } +} + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac15062ac8a348b84fe46.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac15062ac8a348b84fe46.md new file mode 100644 index 00000000000..9799af95235 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac15062ac8a348b84fe46.md @@ -0,0 +1,155 @@ +--- +id: 69bac15062ac8a348b84fe46 +title: Step 25 +challengeType: 1 +dashedName: step-25 +--- + +# --description-- + +First, log the following message to the console: `"\n" + timeBlock + " Simulation"`. + +# --hints-- + +Your `simulateFestival` function should first log the message `"\n" + timeBlock + " Simulation"`. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; + +const spy = __helpers.spyOn(console, "log"); + +try { + simulateFestival(testGates, "Morning"); + + const expectedCall = ["\nMorning Simulation"]; + + assert.deepEqual( + spy.calls[0], + expectedCall, + "simulateFestival should log the simulation header with the timeBlock" + ); + +} catch (err) { + assert.fail(err); +} finally { + spy.restore(); +} +``` + +Your `simulateFestival` function should log the correct time block in the message. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; + +const spy = __helpers.spyOn(console, "log"); + +try { + simulateFestival(testGates, "Night"); + + const expectedCall = ["\nNight Simulation"]; + + assert.deepEqual( + spy.calls[0], + expectedCall, + "simulateFestival should correctly embed the timeBlock parameter in the message" + ); + +} catch (err) { + assert.fail(err); +} finally { + spy.restore(); +} +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); + throughputSummary[gate.id] += result.processed; + if (result.overflow > 0) { + console.log( + "Overflow of " + result.overflow + + " attendees. Rerouting..." + ); + rerouteOverflow(gates, gate, tickIndex, result.overflow); + } +} + +function printSummary(summary) { + console.log("\nThroughput Summary"); + for (const gateId in summary) { + console.log( + gateId + ": " + summary[gateId] + + " attendees processed" + ); + } +} + +function simulateFestival(gates, timeBlock) { +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac17a4c53a3812b595eb9.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac17a4c53a3812b595eb9.md new file mode 100644 index 00000000000..10c05990e64 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac17a4c53a3812b595eb9.md @@ -0,0 +1,162 @@ +--- +id: 69bac17a4c53a3812b595eb9 +title: Step 26 +challengeType: 1 +dashedName: step-26 +--- + +# --description-- + +Before starting the simulation, you need an object to track how many attendees each gate processes over all ticks. Call `initializeThroughput()` with `gates` and assign the result to a variable named `throughputSummary`. + +# --hints-- + +You should call `initializeThroughput()` with `gates` and assign the result to a variable named `throughputSummary`. + +```js +const rawFuncStr = __helpers.removeJSComments(simulateFestival.toString()); +assert.match( + rawFuncStr, + /(var|const|let)\s+throughputSummary/, + "You must have at least one space between the declaration keyword (let/const) and 'throughputSummary'." +); + +const cleaned = __helpers.removeWhiteSpace(rawFuncStr); +const solution = /(var|const|let)throughputSummary=initializeThroughput\(gates\);?}$/; + +assert( + solution.test(cleaned), + "Inside your `simulateFestival` function, call `initializeThroughput()` with `gates` and assign the result to a variable named `throughputSummary`." +); +``` + +Your `simulateFestival` function should call the `initializeThroughput()` function with `gates`. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; + +const originalInitializeThroughput = initializeThroughput; +let called = false; +let callArgs = null; + +try { + initializeThroughput = function (...args) { + called = true; + callArgs = args; + return originalInitializeThroughput(...args); + }; + + const consoleSpy = __helpers.spyOn(console, "log"); + + try { + simulateFestival(testGates, "Morning"); + + assert.isTrue( + called, + "simulateFestival should call initializeThroughput()" + ); + assert.deepEqual( + callArgs, + [testGates], + "initializeThroughput should be called with gates parameter" + ); + + } finally { + consoleSpy.restore(); + } + +} catch (err) { + assert.fail(err); +} finally { + initializeThroughput = originalInitializeThroughput; +} +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); + throughputSummary[gate.id] += result.processed; + if (result.overflow > 0) { + console.log( + "Overflow of " + result.overflow + + " attendees. Rerouting..." + ); + rerouteOverflow(gates, gate, tickIndex, result.overflow); + } +} + +function printSummary(summary) { + console.log("\nThroughput Summary"); + for (const gateId in summary) { + console.log( + gateId + ": " + summary[gateId] + + " attendees processed" + ); + } +} + +function simulateFestival(gates, timeBlock) { + console.log("\n" + timeBlock + " Simulation"); +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac18fe3729f6761344365.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac18fe3729f6761344365.md new file mode 100644 index 00000000000..4947cfddc1b --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac18fe3729f6761344365.md @@ -0,0 +1,123 @@ +--- +id: 69bac18fe3729f6761344365 +title: Step 27 +challengeType: 1 +dashedName: step-27 +--- + +# --description-- + +You need to know how many ticks the simulation should run. Recall that: + +- Each gate has a `queue` array that shows how many attendees arrive at each tick. +- Since all gates have the same number of ticks, you can simply use the length of the first gate’s queue. + +Create a variable named `maxTicks` and assign it the value of `gates[0].queue.length`. + +This value will be used to control your simulation loop, ensuring each tick is processed for all gates. + +# --hints-- + +You should create a variable named `maxTicks` and assign it the value of `gates[0].queue.length`. + +```js +const rawFuncStr = __helpers.removeJSComments(simulateFestival.toString()); +assert.match( + rawFuncStr, + /(var|const|let)\s+maxTicks/, + "You must have at least one space between the declaration keyword (let/const) and 'maxTicks'." +); + +const cleaned = __helpers.removeWhiteSpace(rawFuncStr); +const solution = /(var|const|let)maxTicks=gates\[0\]\.queue\.length;?}$/; + +assert( + solution.test(cleaned), + "Inside your `simulateFestival` function, create a variable named `maxTicks` and assign it the value of `gates[0].queue.length`." +); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); + throughputSummary[gate.id] += result.processed; + if (result.overflow > 0) { + console.log( + "Overflow of " + result.overflow + + " attendees. Rerouting..." + ); + rerouteOverflow(gates, gate, tickIndex, result.overflow); + } +} + +function printSummary(summary) { + console.log("\nThroughput Summary"); + for (const gateId in summary) { + console.log( + gateId + ": " + summary[gateId] + + " attendees processed" + ); + } +} + +function simulateFestival(gates, timeBlock) { + console.log("\n" + timeBlock + " Simulation"); + const throughputSummary = initializeThroughput(gates); +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac1a094d01d84ae4c9e75.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac1a094d01d84ae4c9e75.md new file mode 100644 index 00000000000..7b60dce43d7 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac1a094d01d84ae4c9e75.md @@ -0,0 +1,119 @@ +--- +id: 69bac1a094d01d84ae4c9e75 +title: Step 28 +challengeType: 1 +dashedName: step-28 +--- + +# --description-- + +To keep track of which tick is currently being processed during the simulation, create a variable named `tickIndex` and assign it an initial value of `0`. + +This variable will be used to loop through all ticks in your simulation and ensure each gate is processed in the correct order. + +# --hints-- + +You should create a variable named `tickIndex` and assign it a value of `0`. + +```js +const rawFuncStr = __helpers.removeJSComments(simulateFestival.toString()); +assert.match( + rawFuncStr, + /(var|const|let)\s+tickIndex/, + "You must have at least one space between the declaration keyword (let/const) and 'tickIndex'." +); + +const cleaned = __helpers.removeWhiteSpace(rawFuncStr); +const solution = /(var|const|let)tickIndex=0;?}$/; + +assert( + solution.test(cleaned), + "Inside your `simulateFestival` function, create a variable named `tickIndex` and assign it a value of `0`." +); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); + throughputSummary[gate.id] += result.processed; + if (result.overflow > 0) { + console.log( + "Overflow of " + result.overflow + + " attendees. Rerouting..." + ); + rerouteOverflow(gates, gate, tickIndex, result.overflow); + } +} + +function printSummary(summary) { + console.log("\nThroughput Summary"); + for (const gateId in summary) { + console.log( + gateId + ": " + summary[gateId] + + " attendees processed" + ); + } +} + +function simulateFestival(gates, timeBlock) { + console.log("\n" + timeBlock + " Simulation"); + const throughputSummary = initializeThroughput(gates); + const maxTicks = gates[0].queue.length; +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac1c29c13f446b39f524f.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac1c29c13f446b39f524f.md new file mode 100644 index 00000000000..47f160f3e1f --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac1c29c13f446b39f524f.md @@ -0,0 +1,116 @@ +--- +id: 69bac1c29c13f446b39f524f +title: Step 29 +challengeType: 1 +dashedName: step-29 +--- + +# --description-- + +You should now implement the main simulation loop. This loop will ensure that every gate is processed for each tick in the simulation until all ticks have been completed. + +Create an empty `while` loop that continues as long as `tickIndex` is less than `maxTicks`. + +# --hints-- + +You should create an empty `while` loop with the condition `(tickIndex < maxTicks)`. + +```js +const cleaned = __helpers.removeWhiteSpace(__helpers.removeJSComments(code)); +const viableSolutions = [ + "while(tickIndex cleaned.endsWith(sol)); +assert( + endsCorrectly, + "In your `simulateFestival` function, you should create an empty `while` loop with the condition `(tickIndex < maxTicks)`." +); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); + throughputSummary[gate.id] += result.processed; + if (result.overflow > 0) { + console.log( + "Overflow of " + result.overflow + + " attendees. Rerouting..." + ); + rerouteOverflow(gates, gate, tickIndex, result.overflow); + } +} + +function printSummary(summary) { + console.log("\nThroughput Summary"); + for (const gateId in summary) { + console.log( + gateId + ": " + summary[gateId] + + " attendees processed" + ); + } +} + +function simulateFestival(gates, timeBlock) { + console.log("\n" + timeBlock + " Simulation"); + const throughputSummary = initializeThroughput(gates); + const maxTicks = gates[0].queue.length; + let tickIndex = 0; +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac1d7a104cfeccc356118.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac1d7a104cfeccc356118.md new file mode 100644 index 00000000000..8cc65e9e694 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac1d7a104cfeccc356118.md @@ -0,0 +1,182 @@ +--- +id: 69bac1d7a104cfeccc356118 +title: Step 31 +challengeType: 1 +dashedName: step-31 +--- + +# --description-- + +To complete your `simulateFestival` function, display a summary of the simulation results. Call `printSummary()` with your local `throughputSummary` object. + +# --hints-- + +You should call `printSummary()` with `throughputSummary`. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; + +const originalPrintSummary = printSummary; +let printSummaryCallCount = 0; +let printSummaryArg = null; + +try { + printSummary = function (arg) { + printSummaryCallCount++; + printSummaryArg = arg; + return originalPrintSummary(arg); + }; + + const consoleSpy = __helpers.spyOn(console, "log"); + + try { + simulateFestival(testGates, "Morning"); + + assert.strictEqual( + printSummaryCallCount, + 1, + "printSummary should be called exactly once" + ); + + assert.isObject( + printSummaryArg, + "printSummary should be called with an object" + ); + + assert.strictEqual( + printSummaryArg.North, + 14, + "After simulation, North should have processed 14 attendees total" + ); + assert.strictEqual( + printSummaryArg.East, + 11, + "After simulation, East should have processed 11 attendees total" + ); + assert.strictEqual( + printSummaryArg.South, + 11, + "After simulation, South should have processed 11 attendees total" + ); + assert.strictEqual( + printSummaryArg.West, + 7, + "After simulation, West should have processed 7 attendees total" + ); + + const allLogs = consoleSpy.calls.map(call => call[0]).join(""); + assert.include( + allLogs, + "Throughput Summary", + "Summary should be printed with 'Throughput Summary' header" + ); + + } finally { + consoleSpy.restore(); + } + +} catch (err) { + assert.fail(err); +} finally { + printSummary = originalPrintSummary; +} +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); + throughputSummary[gate.id] += result.processed; + if (result.overflow > 0) { + console.log( + "Overflow of " + result.overflow + + " attendees. Rerouting..." + ); + rerouteOverflow(gates, gate, tickIndex, result.overflow); + } +} + +function printSummary(summary) { + console.log("\nThroughput Summary"); + for (const gateId in summary) { + console.log( + gateId + ": " + summary[gateId] + + " attendees processed" + ); + } +} + +function simulateFestival(gates, timeBlock) { + console.log("\n" + timeBlock + " Simulation"); + const throughputSummary = initializeThroughput(gates); + const maxTicks = gates[0].queue.length; + let tickIndex = 0; + while (tickIndex < maxTicks) { + console.log("\nTick " + (tickIndex + 1)); + for (const gate of gates) { + handleGateAtTick(gates, gate, tickIndex, throughputSummary); + } + tickIndex++; + } +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac1e1ae414b16a12e4041.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac1e1ae414b16a12e4041.md new file mode 100644 index 00000000000..b0c7c9ab267 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69bac1e1ae414b16a12e4041.md @@ -0,0 +1,125 @@ +--- +id: 69bac1e1ae414b16a12e4041 +title: Step 32 +challengeType: 1 +dashedName: step-32 +--- + +# --description-- + +Finally, you can run your simulations. + +Begin with the morning shift. Call `simulateFestival()` with `morningGates` and `"Morning"`. + +# --hints-- + +You should call `simulateFestival()` with `morningGates` and `"Morning"`. + +```js +const cleaned = __helpers.removeWhiteSpace(__helpers.removeJSComments(code)); +const viableSolutions = [ + `simulateFestival(morningGates,"Morning")`, + `simulateFestival(morningGates,"Morning");` +]; +const endsCorrectly = viableSolutions.some(sol => cleaned.endsWith(sol)); +assert( + endsCorrectly, + "You should call `simulateFestival()` with `morningGates` and Morning." +); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); + throughputSummary[gate.id] += result.processed; + if (result.overflow > 0) { + console.log( + "Overflow of " + result.overflow + + " attendees. Rerouting..." + ); + rerouteOverflow(gates, gate, tickIndex, result.overflow); + } +} + +function printSummary(summary) { + console.log("\nThroughput Summary"); + for (const gateId in summary) { + console.log( + gateId + ": " + summary[gateId] + + " attendees processed" + ); + } +} + +function simulateFestival(gates, timeBlock) { + console.log("\n" + timeBlock + " Simulation"); + const throughputSummary = initializeThroughput(gates); + const maxTicks = gates[0].queue.length; + let tickIndex = 0; + while (tickIndex < maxTicks) { + console.log("\nTick " + (tickIndex + 1)); + for (const gate of gates) { + handleGateAtTick(gates, gate, tickIndex, throughputSummary); + } + tickIndex++; + } + printSummary(throughputSummary); +} + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69cfdc80443b715a8aa452a7.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69cfdc80443b715a8aa452a7.md new file mode 100644 index 00000000000..6ff783e2652 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69cfdc80443b715a8aa452a7.md @@ -0,0 +1,152 @@ +--- +id: 69cfdc80443b715a8aa452a7 +title: Step 3 +challengeType: 1 +dashedName: step-3 +--- + +# --description-- + +To complete your `initializeThroughput` function: + +- Loop through each gate in the input `gates` array. For each gate, add a property to `summary` where: + - The key is the gate's `id`. + - The value is `0`. +- After the loop, return the `summary` object. + +Your `initializeThroughput` function should not mutate the source input array `gates`. + +# --hints-- + +Calling `initializeThroughput()` with `[]` should return `{}` without mutating the source input. + +```js +const input = []; +const original = JSON.parse(JSON.stringify(input)); +const result = initializeThroughput(input); + +assert.deepEqual( + result, + {}, + "Calling `initializeThroughput([])` should return an empty object." +); + +assert.deepEqual( + input, + original, + "`initializeThroughput` should not mutate the source array when given an empty array." +); +``` + +Calling `initializeThroughput()` with `[{ id: "North", capacity: 5, queue: [1, 2, 3] }]` should return `{ North: 0 }` without mutating the source input. + +```js +const input = [{ id: "North", capacity: 5, queue: [1, 2, 3] }]; +const original = JSON.parse(JSON.stringify(input)); +const result = initializeThroughput(input); + +assert.deepEqual( + result, + { North: 0 }, + "Calling `initializeThroughput([{ id: 'North', ... }])` should return `{ North: 0 }`." +); + +assert.deepEqual( + input, + original, + "`initializeThroughput` should not mutate the source array when given one gate." +); +``` + +Calling `initializeThroughput()` with `[{ id: "North", capacity: 5, queue: [] }, { id: "East", capacity: 3, queue: [] }, { id: "South", capacity: 4, queue: [] }]` should return `{ North: 0, East: 0, South: 0}` without mutating the source input. + +```js +const input = [ + { id: "North", capacity: 5, queue: [] }, + { id: "East", capacity: 3, queue: [] }, + { id: "South", capacity: 4, queue: [] } +]; + +const original = JSON.parse(JSON.stringify(input)); +const result = initializeThroughput(input); + +assert.deepEqual( + result, + { North: 0, East: 0, South: 0 }, + "Calling `initializeThroughput` with multiple gates should return an object with an entry for each gate id set to 0." +); + +assert.deepEqual( + input, + original, + "`initializeThroughput` should not mutate the source array when given multiple gates." +); +``` + +You should use a `for` loop to iterate through each gate object inside of the input `gates` array. + +```js +const cleaned = __helpers.removeWhiteSpace(__helpers.removeJSComments(initializeThroughput.toString())); +assert.match(cleaned, /for\s*\(/); +``` + +After the loop, you should return `summary`. This should be the final line in the function. + +```js +const funcStr = __helpers.removeWhiteSpace(__helpers.removeJSComments(initializeThroughput.toString())); + +assert.match( + funcStr, + /returnsummary;?}$/, + "Your `initializeThroughput` function should end with `return summary`." +); +``` + +Your `initializeThroughput` function should not mutate the source input array `gates`. + +```js +const morningCopy = JSON.parse(JSON.stringify(morningGates)); +const nightCopy = JSON.parse(JSON.stringify(nightGates)); + +initializeThroughput(morningGates); +initializeThroughput(nightGates); + +assert.deepEqual( + morningGates, + morningCopy, + "`initializeThroughput` should not mutate the `morningGates` seed array." +); + +assert.deepEqual( + nightGates, + nightCopy, + "`initializeThroughput` should not mutate the `nightGates` seed array." +); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; +--fcc-editable-region-- + +--fcc-editable-region-- +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69d048937c7bb4b4c22ebb18.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69d048937c7bb4b4c22ebb18.md new file mode 100644 index 00000000000..4bbf28a4285 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69d048937c7bb4b4c22ebb18.md @@ -0,0 +1,98 @@ +--- +id: 69d048937c7bb4b4c22ebb18 +title: Step 8 +challengeType: 1 +dashedName: step-8 +--- + +# --description-- + +Now it is time to process attendees through the gate. Inside the `while` loop: + +- Decrement `currentTickQueue` by 1 to show one attendee has passed. Use the decrement operator (`--`). +- Increment `processed` by 1 to track how many attendees were processed. Use the increment operator (`++`). + +# --hints-- + +Inside your `while` loop, you should decrement `currentTickQueue` using the decrement operator `--` and increment `processed` using the increment operator `++`. + +```js +const rawFuncStr = __helpers.removeJSComments(processGateFlow.toString()); +const opsRegex = /\s*processed\+\+\s*;?\s*currentTickQueue--\s*|\s*currentTickQueue--\s*;?\s*processed\+\+/; +assert( + opsRegex.test(rawFuncStr), + "Inside your while loop, `processed++` and `currentTickQueue--` must appear next to each other, separated by a semicolon or newline." +); + +const cleaned1 = __helpers.removeWhiteSpace(rawFuncStr); +const viableSolutions1 = [ + "processed++;currentTickQueue--;}}", + "processed++currentTickQueue--;}}", + "processed++;currentTickQueue--}}", + "processed++currentTickQueue--}}", + "currentTickQueue--;processed++;}}", + "currentTickQueue--processed++;}}", + "currentTickQueue--;processed++}}", + "currentTickQueue--processed++}}" +]; +const endsCorrectly1 = viableSolutions1.some(sol1 => cleaned1.endsWith(sol1)); +assert( + endsCorrectly1, + "Your function must end with the while loop and both increment and decrement operations inside of it." +); + +const cleaned2 = __helpers.removeWhiteSpace(__helpers.removeJSComments(code)); +const viableSolutions2 = [ +"while(currentTickQueue>0&&processed0&&processed0&&processed0&&processed0&&processed0&&processed0&&processed0&&processed cleaned2.endsWith(sol2)); +assert( + endsCorrectly2, + "Your while loop must have both, and only both, of the increment and decrement operations inside it." +); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { +--fcc-editable-region-- + +--fcc-editable-region-- + } +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69d08c139139f65a17cc615c.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69d08c139139f65a17cc615c.md new file mode 100644 index 00000000000..aeb9e3f52bb --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69d08c139139f65a17cc615c.md @@ -0,0 +1,236 @@ +--- +id: 69d08c139139f65a17cc615c +title: Step 30 +challengeType: 1 +dashedName: step-30 +--- + +# --description-- + +Inside your `while` loop: + +- Log the message, `"\nTick " + (tickIndex + 1)`. +- Loop through each gate in the `gates` array and call `handleGateAtTick()` with these arguments: + - `gates`: the full gates input array. + - `gate`: the current gate object. + - `tickIndex`: the current tick. + - `throughputSummary`: the local object tracking total attendees processed per gate. +- Increment `tickIndex` by 1 to move to the next tick. + +# --hints-- + +You should log the following message to the console, `"\nTick " + (tickIndex + 1)`. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; + +const spy = __helpers.spyOn(console, "log"); + +try { + simulateFestival(testGates, "Morning"); + const tickMessages = spy.calls.filter(call => call[0].includes("Tick")); + assert.lengthOf( + tickMessages, + 4, + "Should log a tick message for each tick (4 ticks total)" + ); + assert.match( + tickMessages[0][0], + /\nTick 1/, + "First tick should log '\\nTick 1'" + ); + assert.match( + tickMessages[1][0], + /\nTick 2/, + "Second tick should log '\\nTick 2'" + ); + assert.match( + tickMessages[2][0], + /\nTick 3/, + "Third tick should log '\\nTick 3'" + ); + assert.match( + tickMessages[3][0], + /\nTick 4/, + "Fourth tick should log '\\nTick 4'" + ); + +} catch (err) { + assert.fail(err); +} finally { + spy.restore(); +} +``` + +After logging to the console, you should loop through each gate in the `gates` array and call `handleGateAtTick()` with `gates`, `gate`, `tickIndex`, and `throughputSummary`. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; + +const originalHandleGateAtTick = handleGateAtTick; +let handleGateCallCount = 0; + +try { + handleGateAtTick = function (...args) { + handleGateCallCount++; + return originalHandleGateAtTick(...args); + }; + + const consoleSpy = __helpers.spyOn(console, "log"); + + try { + simulateFestival(testGates, "Morning"); + + const expectedCallCount = testGates.length * testGates[0].queue.length; + + assert.strictEqual( + handleGateCallCount, + expectedCallCount, + `handleGateAtTick should be called ${expectedCallCount} times (4 gates × 4 ticks)` + ); + + } finally { + consoleSpy.restore(); + } + +} catch (err) { + assert.fail(err); +} finally { + handleGateAtTick = originalHandleGateAtTick; +} +``` + +After your loop through the `gates` array, you should increment `tickIndex` by 1. + +```js +const testGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] } +]; + +const consoleSpy = __helpers.spyOn(console, "log"); + +try { + simulateFestival(testGates, "Morning"); + + const allLogs = consoleSpy.calls.length; + + assert.isTrue( + allLogs > 0, + "Function should complete and log messages (without infinite loop)" + ); + + const tickMessages = consoleSpy.calls.filter(call => call[0].includes("Tick")); + assert.lengthOf( + tickMessages, + 4, + "tickIndex should be incremented properly to complete all 4 ticks without infinite loop" + ); + +} catch (err) { + assert.fail(err); +} finally { + consoleSpy.restore(); +} +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); + throughputSummary[gate.id] += result.processed; + if (result.overflow > 0) { + console.log( + "Overflow of " + result.overflow + + " attendees. Rerouting..." + ); + rerouteOverflow(gates, gate, tickIndex, result.overflow); + } +} + +function printSummary(summary) { + console.log("\nThroughput Summary"); + for (const gateId in summary) { + console.log( + gateId + ": " + summary[gateId] + + " attendees processed" + ); + } +} + +function simulateFestival(gates, timeBlock) { + console.log("\n" + timeBlock + " Simulation"); + const throughputSummary = initializeThroughput(gates); + const maxTicks = gates[0].queue.length; + let tickIndex = 0; + while (tickIndex < maxTicks) { +--fcc-editable-region-- + +--fcc-editable-region-- + } +} +``` diff --git a/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69d1b6d762a5a7edeb997258.md b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69d1b6d762a5a7edeb997258.md new file mode 100644 index 00000000000..bbb9007b6eb --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-festival-crowd-flow-simulator/69d1b6d762a5a7edeb997258.md @@ -0,0 +1,219 @@ +--- +id: 69d1b6d762a5a7edeb997258 +title: Step 33 +challengeType: 1 +dashedName: step-33 +--- + +# --description-- + +Simulate the night shift by calling `simulateFestival()` with `nightGates` and `"Night"`. + +With that, your simulations are complete! + +# --hints-- + +You should call `simulateFestival()` with `nightGates` and `"Night"`. + +```js +const cleaned = __helpers.removeWhiteSpace(__helpers.removeJSComments(code)); +const viableSolutions = [ + `simulateFestival(nightGates,"Night")`, + `simulateFestival(nightGates,"Night");` +]; +const endsCorrectly = viableSolutions.some(sol => cleaned.endsWith(sol)); +assert( + endsCorrectly, + "You should call `simulateFestival()` with `nightGates` and Night." +); +``` + +# --seed-- + +## --seed-contents-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); + throughputSummary[gate.id] += result.processed; + if (result.overflow > 0) { + console.log( + "Overflow of " + result.overflow + + " attendees. Rerouting..." + ); + rerouteOverflow(gates, gate, tickIndex, result.overflow); + } +} + +function printSummary(summary) { + console.log("\nThroughput Summary"); + for (const gateId in summary) { + console.log( + gateId + ": " + summary[gateId] + + " attendees processed" + ); + } +} + +function simulateFestival(gates, timeBlock) { + console.log("\n" + timeBlock + " Simulation"); + const throughputSummary = initializeThroughput(gates); + const maxTicks = gates[0].queue.length; + let tickIndex = 0; + while (tickIndex < maxTicks) { + console.log("\nTick " + (tickIndex + 1)); + for (const gate of gates) { + handleGateAtTick(gates, gate, tickIndex, throughputSummary); + } + tickIndex++; + } + printSummary(throughputSummary); +} + +simulateFestival(morningGates, "Morning"); +--fcc-editable-region-- + +--fcc-editable-region-- +``` + +# --solutions-- + +```js +const morningGates = [ + { id: "North", capacity: 5, queue: [3, 6, 2, 4] }, + { id: "East", capacity: 3, queue: [2, 4, 3, 5] }, + { id: "South", capacity: 4, queue: [1, 2, 3, 1] }, + { id: "West", capacity: 2, queue: [4, 1, 2, 3] }, +]; + +const nightGates = [ + { id: "North", capacity: 4, queue: [6, 2, 5, 1] }, + { id: "East", capacity: 2, queue: [3, 3, 4, 2] }, + { id: "South", capacity: 5, queue: [2, 1, 2, 3] }, + { id: "West", capacity: 3, queue: [5, 2, 1, 4] }, +]; + +function initializeThroughput(gates) { + const summary = {}; + for (const gate of gates) { + summary[gate.id] = 0; + }; + return summary; +} + +function processGateFlow(gate, tickIndex) { + let currentTickQueue = gate.queue[tickIndex]; + let processed = 0; + while (currentTickQueue > 0 && processed < gate.capacity) { + currentTickQueue--; + processed++; + } + return { + processed: processed, + overflow: currentTickQueue + }; +} + +function rerouteOverflow(gates, currentGate, tickIndex, overflowAmount) { + const currentIndex = gates.indexOf(currentGate); + const nextGateIndex = (currentIndex + 1) % gates.length; + gates[nextGateIndex].queue[tickIndex] += overflowAmount; + console.log( + overflowAmount + " attendees rerouted to " + + gates[nextGateIndex].id + ); +} + +function handleGateAtTick(gates, gate, tickIndex, throughputSummary) { + console.log("\nProcessing " + gate.id + "..."); + console.log( + gate.queue[tickIndex] + " attendees arriving." + ); + const result = processGateFlow(gate, tickIndex); + throughputSummary[gate.id] += result.processed; + if (result.overflow > 0) { + console.log( + "Overflow of " + result.overflow + + " attendees. Rerouting..." + ); + rerouteOverflow(gates, gate, tickIndex, result.overflow); + } +} + +function printSummary(summary) { + console.log("\nThroughput Summary"); + for (const gateId in summary) { + console.log( + gateId + ": " + summary[gateId] + + " attendees processed" + ); + } +} + +function simulateFestival(gates, timeBlock) { + console.log("\n" + timeBlock + " Simulation"); + const throughputSummary = initializeThroughput(gates); + const maxTicks = gates[0].queue.length; + let tickIndex = 0; + while (tickIndex < maxTicks) { + console.log("\nTick " + (tickIndex + 1)); + for (const gate of gates) { + handleGateAtTick(gates, gate, tickIndex, throughputSummary); + } + tickIndex++; + } + printSummary(throughputSummary); +} + +simulateFestival(morningGates, "Morning"); +simulateFestival(nightGates, "Night"); +``` diff --git a/curriculum/structure/blocks/workshop-festival-crowd-flow-simulator.json b/curriculum/structure/blocks/workshop-festival-crowd-flow-simulator.json new file mode 100644 index 00000000000..79ebf2c0951 --- /dev/null +++ b/curriculum/structure/blocks/workshop-festival-crowd-flow-simulator.json @@ -0,0 +1,44 @@ +{ + "isUpcomingChange": false, + "dashedName": "workshop-festival-crowd-flow-simulator", + "helpCategory": "JavaScript", + "blockLayout": "challenge-grid", + "challengeOrder": [ + { "id": "69baafd6d683dd9921b75db3", "title": "Step 1" }, + { "id": "69bab8292750d84cac5b52a0", "title": "Step 2" }, + { "id": "69cfdc80443b715a8aa452a7", "title": "Step 3" }, + { "id": "69babdf3f896711c11516373", "title": "Step 4" }, + { "id": "69babea99725b43a01adb6d1", "title": "Step 5" }, + { "id": "69babf2bf78bade75fda0326", "title": "Step 6" }, + { "id": "69babf3f446433af8d065bb5", "title": "Step 7" }, + { "id": "69d048937c7bb4b4c22ebb18", "title": "Step 8" }, + { "id": "69babfc35c3c58d0993d404d", "title": "Step 9" }, + { "id": "69babfd65c4c4632a0c76f63", "title": "Step 10" }, + { "id": "69bac013466bd9bad0b809b1", "title": "Step 11" }, + { "id": "69bac027df64d4433c708d44", "title": "Step 12" }, + { "id": "69bac03ae5faf2979a3339ff", "title": "Step 13" }, + { "id": "69bac047282614c7ef87d370", "title": "Step 14" }, + { "id": "69bac067cd15c4685d07906d", "title": "Step 15" }, + { "id": "69bac073b1de8b484bbccc94", "title": "Step 16" }, + { "id": "69bac08105bec16db267dcbf", "title": "Step 17" }, + { "id": "69bac0a011a9c1f735d306c2", "title": "Step 18" }, + { "id": "69bac0d5a0b16122a2a938a4", "title": "Step 19" }, + { "id": "69bac0e7112d02531998dbf0", "title": "Step 20" }, + { "id": "69bac0f9659af01091c5967d", "title": "Step 21" }, + { "id": "69bac11611f4c349e8e666ec", "title": "Step 22" }, + { "id": "69bac12483f656898154f969", "title": "Step 23" }, + { "id": "69bac146b5f30209b4179af1", "title": "Step 24" }, + { "id": "69bac15062ac8a348b84fe46", "title": "Step 25" }, + { "id": "69bac17a4c53a3812b595eb9", "title": "Step 26" }, + { "id": "69bac18fe3729f6761344365", "title": "Step 27" }, + { "id": "69bac1a094d01d84ae4c9e75", "title": "Step 28" }, + { "id": "69bac1c29c13f446b39f524f", "title": "Step 29" }, + { "id": "69d08c139139f65a17cc615c", "title": "Step 30" }, + { "id": "69bac1d7a104cfeccc356118", "title": "Step 31" }, + { "id": "69bac1e1ae414b16a12e4041", "title": "Step 32" }, + { "id": "69d1b6d762a5a7edeb997258", "title": "Step 33" } + ], + "blockLabel": "workshop", + "usesMultifileEditor": true, + "hasEditableBoundaries": true +} diff --git a/curriculum/structure/superblocks/javascript-v9.json b/curriculum/structure/superblocks/javascript-v9.json index 2d12dc3ee4b..972646ff3d7 100644 --- a/curriculum/structure/superblocks/javascript-v9.json +++ b/curriculum/structure/superblocks/javascript-v9.json @@ -107,6 +107,7 @@ "lab-chunky-monkey", "lab-profile-lookup", "lab-repeat-a-string", + "workshop-festival-crowd-flow-simulator", "lab-missing-letter-detector", "review-javascript-loops", "quiz-javascript-loops"