@plasius/learning 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +28 -0
  2. package/dist/chunk-2UIOBMJZ.js +44 -0
  3. package/dist/chunk-2UIOBMJZ.js.map +1 -0
  4. package/dist/courses/beacon-bot.cjs +362 -0
  5. package/dist/courses/beacon-bot.cjs.map +1 -0
  6. package/dist/courses/beacon-bot.d.cts +6 -0
  7. package/dist/courses/beacon-bot.d.ts +6 -0
  8. package/dist/courses/beacon-bot.js +168 -0
  9. package/dist/courses/beacon-bot.js.map +1 -0
  10. package/dist/courses/dance-rover.cjs +374 -0
  11. package/dist/courses/dance-rover.cjs.map +1 -0
  12. package/dist/courses/dance-rover.d.cts +6 -0
  13. package/dist/courses/dance-rover.d.ts +6 -0
  14. package/dist/courses/dance-rover.js +180 -0
  15. package/dist/courses/dance-rover.js.map +1 -0
  16. package/dist/courses/meteor-shield.cjs +369 -0
  17. package/dist/courses/meteor-shield.cjs.map +1 -0
  18. package/dist/courses/meteor-shield.d.cts +6 -0
  19. package/dist/courses/meteor-shield.d.ts +6 -0
  20. package/dist/courses/meteor-shield.js +175 -0
  21. package/dist/courses/meteor-shield.js.map +1 -0
  22. package/dist/courses/obstacle-explorer.cjs +368 -0
  23. package/dist/courses/obstacle-explorer.cjs.map +1 -0
  24. package/dist/courses/obstacle-explorer.d.cts +6 -0
  25. package/dist/courses/obstacle-explorer.d.ts +6 -0
  26. package/dist/courses/obstacle-explorer.js +174 -0
  27. package/dist/courses/obstacle-explorer.js.map +1 -0
  28. package/dist/courses/pixel-trail-challenge.cjs +366 -0
  29. package/dist/courses/pixel-trail-challenge.cjs.map +1 -0
  30. package/dist/courses/pixel-trail-challenge.d.cts +6 -0
  31. package/dist/courses/pixel-trail-challenge.d.ts +6 -0
  32. package/dist/courses/pixel-trail-challenge.js +172 -0
  33. package/dist/courses/pixel-trail-challenge.js.map +1 -0
  34. package/dist/courses/rainbow-rescue-rover.cjs +377 -0
  35. package/dist/courses/rainbow-rescue-rover.cjs.map +1 -0
  36. package/dist/courses/rainbow-rescue-rover.d.cts +6 -0
  37. package/dist/courses/rainbow-rescue-rover.d.ts +6 -0
  38. package/dist/courses/rainbow-rescue-rover.js +183 -0
  39. package/dist/courses/rainbow-rescue-rover.js.map +1 -0
  40. package/dist/courses/rescue-crew-commander.cjs +369 -0
  41. package/dist/courses/rescue-crew-commander.cjs.map +1 -0
  42. package/dist/courses/rescue-crew-commander.d.cts +6 -0
  43. package/dist/courses/rescue-crew-commander.d.ts +6 -0
  44. package/dist/courses/rescue-crew-commander.js +175 -0
  45. package/dist/courses/rescue-crew-commander.js.map +1 -0
  46. package/dist/courses/robot-maze-dash.js +3 -38
  47. package/dist/courses/robot-maze-dash.js.map +1 -1
  48. package/dist/courses/servo-creature.cjs +369 -0
  49. package/dist/courses/servo-creature.cjs.map +1 -0
  50. package/dist/courses/servo-creature.d.cts +6 -0
  51. package/dist/courses/servo-creature.d.ts +6 -0
  52. package/dist/courses/servo-creature.js +175 -0
  53. package/dist/courses/servo-creature.js.map +1 -0
  54. package/dist/courses/skywing-sprint.cjs +367 -0
  55. package/dist/courses/skywing-sprint.cjs.map +1 -0
  56. package/dist/courses/skywing-sprint.d.cts +6 -0
  57. package/dist/courses/skywing-sprint.d.ts +6 -0
  58. package/dist/courses/skywing-sprint.js +173 -0
  59. package/dist/courses/skywing-sprint.js.map +1 -0
  60. package/dist/courses/star-defender-squadron.cjs +369 -0
  61. package/dist/courses/star-defender-squadron.cjs.map +1 -0
  62. package/dist/courses/star-defender-squadron.d.cts +6 -0
  63. package/dist/courses/star-defender-squadron.d.ts +6 -0
  64. package/dist/courses/star-defender-squadron.js +175 -0
  65. package/dist/courses/star-defender-squadron.js.map +1 -0
  66. package/package.json +51 -1
@@ -0,0 +1,6 @@
1
+ import { L as LearningCourseV1, C as CoursePracticeQuestion } from '../course-authoring-DImvrK5r.cjs';
2
+
3
+ declare const course: LearningCourseV1;
4
+ declare const practice: CoursePracticeQuestion[];
5
+
6
+ export { course, practice };
@@ -0,0 +1,6 @@
1
+ import { L as LearningCourseV1, C as CoursePracticeQuestion } from '../course-authoring-DImvrK5r.js';
2
+
3
+ declare const course: LearningCourseV1;
4
+ declare const practice: CoursePracticeQuestion[];
5
+
6
+ export { course, practice };
@@ -0,0 +1,168 @@
1
+ import {
2
+ authorCourse
3
+ } from "../chunk-2UIOBMJZ.js";
4
+ import "../chunk-OXWMIHYI.js";
5
+
6
+ // src/courses/beacon-bot.ts
7
+ var { course, practice } = authorCourse({
8
+ slug: "beacon-bot",
9
+ title: "Beacon Bot",
10
+ category: "robot",
11
+ summary: "Program a simulated rescue beacon that communicates with light, buttons and bounded infrared messages. Learn non-blocking timing and persistent state, distinguish a press from a held button, and prevent duplicate or stale messages from creating a response loop. No physical equipment is required.",
12
+ projectFiles: [{ path: "robot.cpp", language: "cpp", maximumCharacters: 24e3 }],
13
+ starterProject: { files: [{ path: "robot.cpp", source: `void setup() {
14
+ setVisibleSignal("off");
15
+ }
16
+
17
+ void loop() {
18
+ setVisibleSignal("off");
19
+ }
20
+ ` }] },
21
+ reference: [
22
+ { name: "setup and loop", signature: "void setup(); void loop();", description: "Setup runs once per simulator session; loop runs once for each supplied observation. Global primitive variables persist between calls. This documented C++ subset does not compile firmware or access devices.", example: 'bool wasPressed = false;\nvoid setup() { setVisibleSignal("off"); }\nvoid loop() { }' },
23
+ { name: "setVisibleSignal", signature: "setVisibleSignal(string colour)", description: "Set the simulated beacon to off, red, amber or green. The host pairs each state with text and a shape. Colours do not grant any real-world emergency-signalling capability.", example: 'setVisibleSignal("amber");' },
24
+ { name: "millis", signature: "int millis()", description: "Read supplied simulated milliseconds. Compare elapsed values without blocking. Pattern changes in this course are at least 500 ms apart; use pause or manual stepping and respect reduced-motion controls.", example: "if (millis() - lastChange >= 500) { lastChange = millis(); }" },
25
+ { name: "button and stop", signature: 'boolSensor("button"); boolSensor("stop");', description: "Read fresh button and stop observations. A press edge is pressed and not wasPressed. Stop takes priority over every pattern or message: switch off, emit no packet and retain an understandable stopped state.", example: 'bool pressed = boolSensor("button"); bool rising = pressed && !wasPressed; wasPressed = pressed;' },
26
+ { name: "receiver", signature: 'textSensor("receiver")', description: "The receiver supplies none, help, ack or an unknown string for a rejected-message scenario. Read numberSensor for messageId, messageAgeMs, senderId and nodeId. IDs are integers; negative or over-1000-ms ages are invalid or stale.", example: 'string packet = textSensor("receiver"); int id = numberSensor("messageId");' },
27
+ { name: "sendIrMessage", signature: "sendIrMessage(string packet)", description: "Emit a simulated help or ack packet. Permit at most one packet per 1000 ms per node. Acknowledge each fresh help id once, ignore the node's own packets and never acknowledge an ack. No physical infrared hardware is opened.", example: 'if (millis() - lastSent >= 1000) { sendIrMessage("help"); lastSent = millis(); }' },
28
+ { name: "typed state", signature: "int, double, bool, string", description: "Use typed globals and helper functions, if/else and bounded loops. Conditions require bool. millis uses int arithmetic; numberSensor returns double and can initialise an int for a supplied integer id. Includes, pointers and blocking waits are outside the simulator subset.", example: "int lastSent = -1000;\nint lastAcknowledged = -1;\nbool wasPressed = false;" }
29
+ ]
30
+ }, [
31
+ {
32
+ title: "A signal with a meaning",
33
+ concepts: ["Output", "Named states", "Safe defaults"],
34
+ goals: ["Produce documented signal states with matching text and shape feedback.", "Keep setup and stop behaviour explicit before adding automatic patterns."],
35
+ extension: "Design a four-state signal legend using both words and shapes. Explain how someone can understand it without distinguishing the colours.",
36
+ activities: {
37
+ learn: ["A signal is useful when its meaning is clear. The simulator accepts off, red, amber and green, and presents each with text and a shape. setup runs once; loop handles repeated observations. Start with off and give the stop input priority before any other output.", "A colour alone is not a complete interface. Read the state label and shape as part of the signal."],
38
+ predict: ["Predict the output when setup sets off and loop sets amber. Then make stop true and decide which output should remain, even if the ordinary loop would select green.", "Stop is a decision made from the current observation. Returning after switching off prevents later code from overriding it."],
39
+ build: ['Edit robot.cpp so setup sets off, loop reads boolSensor("stop") and returns after switching off when stopped. Otherwise select amber as the current working signal. Keep the documented function names and supported colour strings.', "The simulator parses a closed C++ subset. Misspelled commands or unsupported colour values produce feedback rather than opening a device."],
40
+ run: ["Run with stop false, then toggle the stop control using the keyboard and onscreen input. Compare the beacon, text label and shape. Restart the simulator to see setup happen once again.", "Restarting simulation recreates runtime state; it does not reset or erase your saved source."],
41
+ assess: ["Check startup, ordinary output, stop priority and supported signal values. The evaluator verifies emitted commands from the saved source, not a screenshot or a learner-supplied success flag.", "A final green command after an off command would override stop. Inspect the whole output sequence for that observation."],
42
+ inspect: ["If stop seems ineffective, inspect whether execution continues after setting off. If no signal appears, inspect spelling and quoting. If colours and text disagree, report the host display issue separately from the program's emitted state.", "The command trace lets you distinguish a program rule from how the preview presents it."],
43
+ fix: ["Repair output selection and repeat both stop values. Keep the safe setup state and verify a normal observation still selects the intended signal after stop is released.", "Use an early return for the priority stop case so later pattern code cannot accidentally override it."],
44
+ explain: ["Choose why the beacon needs text and shape as well as colour, then explain why an explicit stopped state belongs before normal signalling logic.", "The same state should remain understandable when colours are hard to distinguish or animation is reduced."],
45
+ reward: ["Save Clear signal. You have a defined output and a priority stop path. The next mission changes signals over simulated time while keeping the program available to read new inputs.", "Keep the stopped-state example as a required check for every later mission."]
46
+ },
47
+ questions: {
48
+ learn: { question: "Why pair a beacon colour with a state label and shape?", choices: ["So meaning does not depend on colour alone", "So stop can be ignored", "So every colour means the same thing"], correctChoice: 0, feedback: "Text and shape provide another way to understand the signal, including when colours are difficult to distinguish." },
49
+ predict: { question: "Stop is true while normal logic would select green; which output should remain?", choices: ["Green because it came later", "Off with later signalling skipped", "A rapid mixture of every colour"], correctChoice: 1, feedback: "Stop takes priority. Switching off and returning prevents later logic from overriding that decision." },
50
+ explain: { question: "What does restarting the simulator reset?", choices: ["All account save slots", "The learner's course badge", "The live program state so setup runs again"], correctChoice: 2, feedback: "A new simulation session resets live globals and setup state while preserving the separately saved project and progress." }
51
+ }
52
+ },
53
+ {
54
+ title: "Time without waiting",
55
+ concepts: ["Elapsed time", "Persistent variables", "Non-blocking logic"],
56
+ goals: ["Alternate a signal using elapsed simulated time with no blocking delay.", "Continue observing stop during the interval between pattern changes."],
57
+ extension: "Compare 500-ms and 1000-ms intervals in separate saves. Use manual stepping to predict the next change without requiring animated flashing.",
58
+ activities: {
59
+ learn: ["Blocking while waiting would stop the program reading new inputs. Instead store lastChange and a bool lit globally. Each loop reads millis; when elapsed time reaches 500 ms, toggle lit and update lastChange. Between changes, return promptly and retain the current signal.", "A global survives the next loop call. A local lastChange initialised to zero on every observation cannot remember the previous transition."],
60
+ predict: ["lastChange is zero and lit is false. Predict whether observations at 499 ms and 500 ms change the state, then predict the next transition after lastChange becomes 500. Decide whether a stop at 250 ms should wait.", "The comparison is elapsed >= 500. Stop is checked on every observation, including while the pattern interval has not finished."],
61
+ build: ["Add int lastChange=0 and bool lit=false as globals. Keep stop first. When millis()-lastChange >= 500, toggle lit, select green or off and record the current millis value. Use no wait, sleep or busy loop.", "Update the timestamp only when the transition happens. Resetting it on every observation prevents elapsed time from reaching the interval."],
62
+ run: ["Step to just before and exactly at the 500-ms boundary, then continue through another interval. Toggle stop midway through a wait. Inspect lit and lastChange in the variable snapshot alongside the emitted commands.", "The host supplies game time. Pausing the simulator should not make the next observation jump ahead because real time passed."],
63
+ assess: ["Check the boundary, persistence across loop calls, prompt stop handling and absence of unbounded waiting. A pattern must remain responsive without emitting a new transition on every observation.", "The runtime's operation budget stops runaway code; reaching that limit is a program error to repair, not a timing technique."],
64
+ inspect: ["If the signal never changes, inspect timestamp updates. If it changes on every call, inspect elapsed comparison and global state. If stop is delayed, look for a loop waiting for millis to change inside one observation.", "millis is fixed during a supplied observation. Re-reading it in a busy loop cannot advance simulation time."],
65
+ fix: ["Repair elapsed-time logic and rerun the 499/500-ms and midway-stop cases. Keep intervals at least 500 ms and verify pause/resume uses supplied time without creating extra transitions.", "Use one bounded decision per loop call; the host will supply the next observation when simulation advances."],
66
+ explain: ["Choose why comparing timestamps lets the program stay responsive while waiting for a pattern interval. Explain why repeatedly reading a fixed observation's clock cannot make time move.", "Waiting is represented by state between calls, not by occupying execution until a later time arrives."],
67
+ reward: ["Save Responsive timing. Your beacon now changes over time and can still stop promptly. Next you will give a reusable multi-stage signal pattern a named function.", "Keep the time-boundary trace for testing later patterns and message cooldowns."]
68
+ },
69
+ questions: {
70
+ learn: { question: "Where should lastChange live if it must persist between loop calls?", choices: ["In a local reset on every call", "In a global belonging to the simulator session", "Only in the display colour"], correctChoice: 1, feedback: "A session global retains the previous transition time so the next observation can calculate elapsed time correctly." },
71
+ predict: { question: "With lastChange=0 and a 500-ms interval, when does the first transition occur?", choices: ["At 499 ms", "On every observation", "At 500 ms or the first later observation"], correctChoice: 2, feedback: "The interval is reached when elapsed time is at least 500 ms; earlier observations leave the state unchanged." },
72
+ explain: { question: "Why use a timestamp comparison instead of a busy waiting loop?", choices: ["It allows the program to return and receive fresh inputs", "It removes the need for state", "It accesses the real device clock automatically"], correctChoice: 0, feedback: "A non-blocking decision finishes quickly, allowing later observations to carry new stop, button and timing information." }
73
+ }
74
+ },
75
+ {
76
+ title: "A pattern worth naming",
77
+ concepts: ["Functions", "Parameters", "Cyclic state"],
78
+ goals: ["Create a named function that maps a bounded phase to its documented signal.", "Advance a three-stage pattern without copying its full logic into several event paths."],
79
+ extension: "Write another three-stage pattern in a separate helper and compare the two legends. Keep the same timing code so only the pattern mapping changes.",
80
+ activities: {
81
+ learn: ["A helper function gives a name to a useful operation. Write showPhase(int phase) to select red for zero, amber for one and green for two. Advance phase with (phase+1)%3 at each timed transition, and call the helper instead of copying those choices everywhere.", "The remainder operator wraps two plus one back to zero. The function definition alone does not emit a signal until called."],
82
+ predict: ["Starting at phase zero, predict the next four phases after repeated increments modulo three. Then define showPhase without calling it and decide whether any pattern output should appear.", "Track the numeric phase separately from its displayed colour, and remember that a definition describes work rather than performing it."],
83
+ build: ["Add the showPhase helper and a global phase. Call it for the initial active phase and after each 500-ms transition. Keep the stop branch outside the helper and before the pattern update so stopping cannot be hidden by reuse.", "Use if/else comparisons for the three supported phase values; provide a safe off result for an unexpected value."],
84
+ run: ["Step through two full cycles and match each phase to the legend. Pause, stop and restart at different phases. Confirm each call produces the same output for the same phase regardless of where it was invoked.", "A named helper should simplify explanation, not hide a timestamp change or stop override as an unexpected side effect."],
85
+ assess: ["Check the phase mapping, wraparound, reusable function call and retained stop priority. Cases include an unexpected phase value to verify the helper's safe fallback.", "The function should map its parameter, not ignore it and always select the current global phase."],
86
+ inspect: ["If every call shows the same colour, inspect use of the parameter. If phase grows without wrapping, inspect modulo. If stop is overridden, inspect the order of helper calls after the priority branch.", "Test showPhase with each small input before debugging the whole timed pattern."],
87
+ fix: ["Repair the helper or phase transition and replay two cycles. Keep timing at the same interval while checking mapping, then rerun the stop and restart cases from earlier missions.", "Changing both timing and colour mapping together makes it harder to identify which repair changed the behaviour."],
88
+ explain: ["Choose why a parameterised helper is more useful than several copied signal branches. Explain how its input and output contract lets you test one piece independently.", "A clear function name and a small parameter describe a decision that can be reused and checked on its own."],
89
+ reward: ["Save Named pattern. You have combined functions with persistent timed state. The next mission responds to a real input transition so a held button cannot trigger the same action repeatedly.", "Keep the helper's mapping examples as a reference when adding receiver-driven signals."]
90
+ },
91
+ questions: {
92
+ learn: { question: "What makes showPhase reusable?", choices: ["Reading no input and always selecting red", "Copying it under several different names", "Mapping its phase parameter through one defined rule"], correctChoice: 2, feedback: "A parameterised mapping can be called from several places while keeping the signal rule in one understandable function." },
93
+ predict: { question: "What are the next four phases after zero using (phase+1)%3?", choices: ["1, 2, 0, 1", "1, 2, 3, 4", "0, 0, 0, 0"], correctChoice: 0, feedback: "Remainder modulo three keeps the cycle within zero, one and two before returning to zero." },
94
+ explain: { question: "Why include a safe fallback in the phase helper?", choices: ["To ignore stop", "To give unexpected input an explicit harmless result", "To replace every valid phase with an error"], correctChoice: 1, feedback: "A defined off result avoids an accidental signal when the phase is outside the supported mapping." }
95
+ }
96
+ },
97
+ {
98
+ title: "A button is an event",
99
+ concepts: ["Rising edge", "Fresh sensors", "Stateful input"],
100
+ goals: ["React once to a new button press and re-arm only after release.", "Keep stop and receiver observations distinct from a stale remembered input."],
101
+ extension: "Compare press, hold, release and press again as four observations. Explain which transitions count as events and which are repeated state.",
102
+ activities: {
103
+ learn: ["A held button can be true in many observations, but a new press happens only when current true follows previous false. Store wasPressed globally, compute rising=pressed&&!wasPressed, then update wasPressed. This separates one event from repeated observations of the same state.", 'Read boolSensor("button") each time. A value captured only during setup never describes later interaction.'],
104
+ predict: ["Predict the rising-edge results for false, true, true, false, true. Then stop while the button is held and decide whether releasing stop alone should manufacture another press event.", "Update the remembered button state even while stopped, so an old hold does not become a new activation on resume."],
105
+ build: ["Add edge detection before the stop return while keeping stop's output priority. Use a new press to select the next named signal phase once. Read receiver text separately and map a fresh ack to green without treating it as a button press.", "The input records describe different channels. Keep previous button state separate from message identity and age."],
106
+ run: ["Press, hold, release and press again using keyboard and onscreen controls. Step through each observation and inspect wasPressed. Stop during a hold, then release stop while still holding and compare the event count.", "Holding a control should preserve state without behaving like an unlimited stream of fresh presses."],
107
+ assess: ["Check the five-observation edge sequence, long holds, release re-arming and stop/resume during a hold. Receiver tests distinguish none, ack and unknown strings without copying stale values from setup.", "A test may supply the same true value repeatedly; only the first transition from false is an event."],
108
+ inspect: ["If holding advances every phase, inspect the previous-state comparison. If only the first ever press works, inspect updates after release. If resume triggers unexpectedly, inspect the remembered state around the stop return.", "Trace both current and previous button values before deciding whether the rising expression is correct."],
109
+ fix: ["Repair edge detection and repeat the hold-through-stop scenario. Preserve responsive timing and receiver handling, and confirm an actual release followed by a new press still works after the repair.", "Rejecting every press would remove repeats but also remove useful interaction. Keep the valid edge path."],
110
+ explain: ["Choose why observing true repeatedly is different from receiving several new presses. Explain how one small piece of persistent state helps convert observations into meaningful events.", "Events describe change over time; a single observation tells only the current state."],
111
+ reward: ["Save Deliberate input. Your beacon now responds once per intentional press and reads fresh observations. Next it will exchange bounded messages without repeating stale acknowledgements.", "Keep the edge sequence as a regression case for the final network programme."]
112
+ },
113
+ questions: {
114
+ learn: { question: "Which expression identifies a new press from current and previous button states?", choices: ["pressed && !wasPressed", "pressed || wasPressed", "wasPressed alone"], correctChoice: 0, feedback: "A rising edge requires the button to be true now and false in the previous observation." },
115
+ predict: { question: "How many new presses occur in false, true, true, false, true?", choices: ["Three", "Two", "Five"], correctChoice: 1, feedback: "Only the two false-to-true transitions count. The repeated true observation is part of one held press." },
116
+ explain: { question: "Why remember button state even while stopped?", choices: ["To send packets during stop", "To ignore future releases", "To avoid treating a continuing hold as a fresh press after resume"], correctChoice: 2, feedback: "Maintaining observation history prevents stop/resume from inventing a transition that the button did not make." }
117
+ }
118
+ },
119
+ {
120
+ title: "A message with a limit",
121
+ concepts: ["Message identity", "Freshness", "Rate limits"],
122
+ goals: ["Acknowledge each fresh help message once while rejecting stale, duplicate and unknown packets.", "Bound simulated transmission to one packet per second and give stop priority over sending."],
123
+ extension: "Create a timeline with duplicate help packets and a delayed acknowledgement. Explain which packet ids should be handled and why an ack must not create another ack.",
124
+ activities: {
125
+ learn: ["A receiver may show the same packet in several observations. Use messageId to remember which help was acknowledged and messageAgeMs to reject negative or over-1000-ms ages. Ignore your own senderId, unknown packet text and acknowledgements as reply triggers. Limit every transmission with lastSent.", "A response to every observation creates duplicates; replying to acknowledgements can create an endless feedback loop."],
126
+ predict: ["A fresh help packet id seven is observed twice, followed by an ack and a stale help id eight. Predict the acknowledgements. Then a valid packet arrives 400 ms after the last send: decide whether the one-second transmission rule allows an immediate reply.", "Update lastAcknowledged only when the reply is actually sent. A rate-limited attempt must not falsely record successful acknowledgement."],
127
+ build: ["Track lastAcknowledged=-1 and lastSent=-1000 globally. Reply ack only to a fresh external help id not already acknowledged and when millis()-lastSent>=1000. Record both values after sending. Keep stop first and never reply to ack, none or unknown text.", "All transmissions share the same lastSent budget. Separate unlimited counters for help and ack could exceed the node's total limit."],
128
+ run: ["Use the packet controls to supply new, repeated, stale and unknown messages. Step through a cooldown boundary and inspect sent packet ids/times. Confirm the trace explicitly identifies ignored input without opening any physical infrared device.", "The simulator uses supplied message records. No camera, serial port or transmitter permission is part of this exercise."],
129
+ assess: ["Check duplicate ids, freshness boundaries, self-origin packets, ack-loop prevention, missing cooldown and stop while receiving. A passing trace emits at most one packet per 1000 ms and never claims an unsent reply as handled.", "Different ids can still arrive too quickly; identity checking does not replace the time budget."],
130
+ inspect: ["If the same help is acknowledged twice, inspect saved identity. If packets flood, inspect the shared cooldown and ack branch. If a delayed message is lost permanently, inspect whether it was marked handled before sending.", "Track three separate facts: what arrived, what was permitted and what was actually transmitted."],
131
+ fix: ["Repair the first invalid transmission and replay its timeline plus a valid new help message. Keep stale and self-origin packets rejected, and recheck that stop emits neither help nor ack.", "A repair must preserve useful communication; simply disabling every transmission would not satisfy a valid-message scenario."],
132
+ explain: ["Choose why identity, freshness and rate limits solve different problems. Explain why an acknowledgement is the end of this small exchange rather than a request for another acknowledgement.", "Each rule needs its own evidence: a new id can be stale, and a fresh new id can still arrive during cooldown."],
133
+ reward: ["Save Bounded messages. Your beacon now communicates through a small explicit protocol. The capstone joins button events, signal patterns and receiver handling into a recoverable simulated network.", "Keep the duplicate and cooldown timelines as regression tests for the final integration."]
134
+ },
135
+ questions: {
136
+ learn: { question: "Which value prevents acknowledging the same help observation repeatedly?", choices: ["Only the colour", "The last successfully acknowledged message id", "The array index of the signal legend"], correctChoice: 1, feedback: "Remembering the handled id distinguishes one packet observed repeatedly from a genuinely new help message." },
137
+ predict: { question: "A fresh new help arrives 400 ms after the last send; may this node transmit immediately?", choices: ["Yes, because the id is new", "Yes, because it is fresh", "No, the shared 1000-ms transmission interval still applies"], correctChoice: 2, feedback: "Identity and freshness do not remove the node's rate limit. All outgoing packets share the same transmission budget." },
138
+ explain: { question: "Why should ack packets not trigger another ack?", choices: ["To terminate the exchange instead of creating a response loop", "To make all help messages stale", "To erase sender identity"], correctChoice: 0, feedback: "An acknowledgement closes this protocol exchange. Acknowledging acknowledgements would create repeated traffic without new information." }
139
+ }
140
+ },
141
+ {
142
+ title: "The rescue beacon network",
143
+ concepts: ["Integration", "Protocol priority", "Accessible status"],
144
+ goals: ["Combine deliberate help requests, one-time acknowledgements and clear signal states.", "Keep stop, duplicate handling and the shared transmission budget correct across several simulated nodes."],
145
+ extension: "Design another message type on paper with a clear sender, receiver action, freshness rule and termination condition. Keep it separate from the tested help/ack protocol until those rules are implemented and checked.",
146
+ activities: {
147
+ learn: ["The network runs the same programme in separate node sessions with their own globals and nodeId. A new local button press can send help within the shared budget; a fresh external help can receive one ack. Give eligible acknowledgements priority over a simultaneous new local request, and never transmit while stopped.", "A button press during cooldown is ignored in this simple protocol. State that limit clearly instead of pretending the request was queued or sent."],
148
+ predict: ["At one node, a new button press and a fresh external help arrive together while transmission is available. Predict which packet wins, then follow an ack through a second node. Decide whether it creates any further response.", "One shared budget permits one outgoing packet. The documented priority selects ack and avoids a hidden two-packet burst."],
149
+ build: ["Integrate input edges, receiver validation, acknowledgement priority and local help in one loop. Update button history even while stopped, keep cooldown shared and expose signal states consistently. Preserve separate per-session message and timing history.", "Use helpers for mapping signals or testing freshness, while keeping the order of stop and transmission decisions obvious."],
150
+ run: ["Operate the simulated network with keyboard controls, pause on a simultaneous event and inspect each node's packet timeline and signal label. Try duplicate delivery, stop/resume and a restart, then compare independent node histories.", "No physical equipment is accessed. The text timeline and static shapes provide the same information when motion or colour is not useful."],
151
+ assess: ["Run the final saved-source cases across several node ids and message timelines. Checks combine signal timing, press edges, freshness, duplicates, self-origin packets, rate limits, ack priority and stop behaviour.", "A single successful exchange is not enough to establish that the protocol avoids loops and remains bounded under repeated inputs."],
152
+ inspect: ["Locate the first invalid packet or signal in a failing timeline and identify its input, node id and prior state. Distinguish a missing edge, stale sample, duplicate id and exhausted cooldown before editing the programme.", "Look for the first rule violation rather than counting only the final number of packets."],
153
+ fix: ["Repair that decision and replay both the failure timeline and an ordinary help/ack exchange. Save and assess the new source, checking that a local change has not removed stop priority or mixed state between nodes.", "A node's global variables are its own history. Avoid encoding answers for specific node ids or known timelines."],
154
+ explain: ["Choose what the network tests establish and describe one deliberate limitation of the simple protocol. Explain how clear priority, bounded work and a terminating exchange made the behaviour understandable.", "The project is a simulator teaching programming and protocol reasoning, not a certified emergency communication system."],
155
+ reward: ["Save Rescue network and complete the final assessment. You have created a responsive beacon with reusable logic, deliberate input and bounded messaging. Replay any mission or explore another pattern in a named save while retaining earned completion.", "Keep the finished simulator project before experimenting. Physical builds remain a separate activity with their existing guardian and hardware protections."]
156
+ },
157
+ questions: {
158
+ learn: { question: "How should each simulated node store its timing and message history?", choices: ["In one shared mutable history for every node", "Only in the scene's background colour", "In its own isolated session globals"], correctChoice: 2, feedback: "Each node has independent observations and transmissions, so its persistent state must belong to its own session." },
159
+ predict: { question: "An eligible ack and a new local help request coincide with one transmission available; which is sent?", choices: ["Ack under the documented priority", "Both immediately", "Neither because all communication is disabled"], correctChoice: 0, feedback: "The protocol gives acknowledgement priority and applies one shared transmission budget, avoiding an unexpected burst." },
160
+ explain: { question: "What does passing the simulator's network tests demonstrate?", choices: ["Approval to operate emergency hardware", "Evidence for the documented bounded protocol scenarios", "A guarantee that future changes need no checks"], correctChoice: 1, feedback: "The tests support the stated simulator behaviours; they do not certify physical equipment or eliminate the need to verify changes." }
161
+ }
162
+ }
163
+ ]);
164
+ export {
165
+ course,
166
+ practice
167
+ };
168
+ //# sourceMappingURL=beacon-bot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/courses/beacon-bot.ts"],"sourcesContent":["import { authorCourse } from \"./course-authoring.js\";\n\nexport const { course, practice } = authorCourse({\n slug: \"beacon-bot\", title: \"Beacon Bot\", category: \"robot\",\n summary: \"Program a simulated rescue beacon that communicates with light, buttons and bounded infrared messages. Learn non-blocking timing and persistent state, distinguish a press from a held button, and prevent duplicate or stale messages from creating a response loop. No physical equipment is required.\",\n projectFiles: [{ path: \"robot.cpp\", language: \"cpp\", maximumCharacters: 24000 }],\n starterProject: { files: [{ path: \"robot.cpp\", source: `void setup() {\n setVisibleSignal(\"off\");\n}\n\nvoid loop() {\n setVisibleSignal(\"off\");\n}\n` }] },\n reference: [\n { name: \"setup and loop\", signature: \"void setup(); void loop();\", description: \"Setup runs once per simulator session; loop runs once for each supplied observation. Global primitive variables persist between calls. This documented C++ subset does not compile firmware or access devices.\", example: 'bool wasPressed = false;\\nvoid setup() { setVisibleSignal(\"off\"); }\\nvoid loop() { }' },\n { name: \"setVisibleSignal\", signature: \"setVisibleSignal(string colour)\", description: \"Set the simulated beacon to off, red, amber or green. The host pairs each state with text and a shape. Colours do not grant any real-world emergency-signalling capability.\", example: 'setVisibleSignal(\"amber\");' },\n { name: \"millis\", signature: \"int millis()\", description: \"Read supplied simulated milliseconds. Compare elapsed values without blocking. Pattern changes in this course are at least 500 ms apart; use pause or manual stepping and respect reduced-motion controls.\", example: \"if (millis() - lastChange >= 500) { lastChange = millis(); }\" },\n { name: \"button and stop\", signature: 'boolSensor(\"button\"); boolSensor(\"stop\");', description: \"Read fresh button and stop observations. A press edge is pressed and not wasPressed. Stop takes priority over every pattern or message: switch off, emit no packet and retain an understandable stopped state.\", example: 'bool pressed = boolSensor(\"button\"); bool rising = pressed && !wasPressed; wasPressed = pressed;' },\n { name: \"receiver\", signature: 'textSensor(\"receiver\")', description: \"The receiver supplies none, help, ack or an unknown string for a rejected-message scenario. Read numberSensor for messageId, messageAgeMs, senderId and nodeId. IDs are integers; negative or over-1000-ms ages are invalid or stale.\", example: 'string packet = textSensor(\"receiver\"); int id = numberSensor(\"messageId\");' },\n { name: \"sendIrMessage\", signature: \"sendIrMessage(string packet)\", description: \"Emit a simulated help or ack packet. Permit at most one packet per 1000 ms per node. Acknowledge each fresh help id once, ignore the node's own packets and never acknowledge an ack. No physical infrared hardware is opened.\", example: 'if (millis() - lastSent >= 1000) { sendIrMessage(\"help\"); lastSent = millis(); }' },\n { name: \"typed state\", signature: \"int, double, bool, string\", description: \"Use typed globals and helper functions, if/else and bounded loops. Conditions require bool. millis uses int arithmetic; numberSensor returns double and can initialise an int for a supplied integer id. Includes, pointers and blocking waits are outside the simulator subset.\", example: 'int lastSent = -1000;\\nint lastAcknowledged = -1;\\nbool wasPressed = false;' },\n ],\n}, [\n {\n title: \"A signal with a meaning\", concepts: [\"Output\", \"Named states\", \"Safe defaults\"],\n goals: [\"Produce documented signal states with matching text and shape feedback.\", \"Keep setup and stop behaviour explicit before adding automatic patterns.\"],\n extension: \"Design a four-state signal legend using both words and shapes. Explain how someone can understand it without distinguishing the colours.\",\n activities: {\n learn: [\"A signal is useful when its meaning is clear. The simulator accepts off, red, amber and green, and presents each with text and a shape. setup runs once; loop handles repeated observations. Start with off and give the stop input priority before any other output.\", \"A colour alone is not a complete interface. Read the state label and shape as part of the signal.\"],\n predict: [\"Predict the output when setup sets off and loop sets amber. Then make stop true and decide which output should remain, even if the ordinary loop would select green.\", \"Stop is a decision made from the current observation. Returning after switching off prevents later code from overriding it.\"],\n build: [\"Edit robot.cpp so setup sets off, loop reads boolSensor(\\\"stop\\\") and returns after switching off when stopped. Otherwise select amber as the current working signal. Keep the documented function names and supported colour strings.\", \"The simulator parses a closed C++ subset. Misspelled commands or unsupported colour values produce feedback rather than opening a device.\"],\n run: [\"Run with stop false, then toggle the stop control using the keyboard and onscreen input. Compare the beacon, text label and shape. Restart the simulator to see setup happen once again.\", \"Restarting simulation recreates runtime state; it does not reset or erase your saved source.\"],\n assess: [\"Check startup, ordinary output, stop priority and supported signal values. The evaluator verifies emitted commands from the saved source, not a screenshot or a learner-supplied success flag.\", \"A final green command after an off command would override stop. Inspect the whole output sequence for that observation.\"],\n inspect: [\"If stop seems ineffective, inspect whether execution continues after setting off. If no signal appears, inspect spelling and quoting. If colours and text disagree, report the host display issue separately from the program's emitted state.\", \"The command trace lets you distinguish a program rule from how the preview presents it.\"],\n fix: [\"Repair output selection and repeat both stop values. Keep the safe setup state and verify a normal observation still selects the intended signal after stop is released.\", \"Use an early return for the priority stop case so later pattern code cannot accidentally override it.\"],\n explain: [\"Choose why the beacon needs text and shape as well as colour, then explain why an explicit stopped state belongs before normal signalling logic.\", \"The same state should remain understandable when colours are hard to distinguish or animation is reduced.\"],\n reward: [\"Save Clear signal. You have a defined output and a priority stop path. The next mission changes signals over simulated time while keeping the program available to read new inputs.\", \"Keep the stopped-state example as a required check for every later mission.\"],\n },\n questions: {\n learn: { question: \"Why pair a beacon colour with a state label and shape?\", choices: [\"So meaning does not depend on colour alone\", \"So stop can be ignored\", \"So every colour means the same thing\"], correctChoice: 0, feedback: \"Text and shape provide another way to understand the signal, including when colours are difficult to distinguish.\" },\n predict: { question: \"Stop is true while normal logic would select green; which output should remain?\", choices: [\"Green because it came later\", \"Off with later signalling skipped\", \"A rapid mixture of every colour\"], correctChoice: 1, feedback: \"Stop takes priority. Switching off and returning prevents later logic from overriding that decision.\" },\n explain: { question: \"What does restarting the simulator reset?\", choices: [\"All account save slots\", \"The learner's course badge\", \"The live program state so setup runs again\"], correctChoice: 2, feedback: \"A new simulation session resets live globals and setup state while preserving the separately saved project and progress.\" },\n },\n },\n {\n title: \"Time without waiting\", concepts: [\"Elapsed time\", \"Persistent variables\", \"Non-blocking logic\"],\n goals: [\"Alternate a signal using elapsed simulated time with no blocking delay.\", \"Continue observing stop during the interval between pattern changes.\"],\n extension: \"Compare 500-ms and 1000-ms intervals in separate saves. Use manual stepping to predict the next change without requiring animated flashing.\",\n activities: {\n learn: [\"Blocking while waiting would stop the program reading new inputs. Instead store lastChange and a bool lit globally. Each loop reads millis; when elapsed time reaches 500 ms, toggle lit and update lastChange. Between changes, return promptly and retain the current signal.\", \"A global survives the next loop call. A local lastChange initialised to zero on every observation cannot remember the previous transition.\"],\n predict: [\"lastChange is zero and lit is false. Predict whether observations at 499 ms and 500 ms change the state, then predict the next transition after lastChange becomes 500. Decide whether a stop at 250 ms should wait.\", \"The comparison is elapsed >= 500. Stop is checked on every observation, including while the pattern interval has not finished.\"],\n build: [\"Add int lastChange=0 and bool lit=false as globals. Keep stop first. When millis()-lastChange >= 500, toggle lit, select green or off and record the current millis value. Use no wait, sleep or busy loop.\", \"Update the timestamp only when the transition happens. Resetting it on every observation prevents elapsed time from reaching the interval.\"],\n run: [\"Step to just before and exactly at the 500-ms boundary, then continue through another interval. Toggle stop midway through a wait. Inspect lit and lastChange in the variable snapshot alongside the emitted commands.\", \"The host supplies game time. Pausing the simulator should not make the next observation jump ahead because real time passed.\"],\n assess: [\"Check the boundary, persistence across loop calls, prompt stop handling and absence of unbounded waiting. A pattern must remain responsive without emitting a new transition on every observation.\", \"The runtime's operation budget stops runaway code; reaching that limit is a program error to repair, not a timing technique.\"],\n inspect: [\"If the signal never changes, inspect timestamp updates. If it changes on every call, inspect elapsed comparison and global state. If stop is delayed, look for a loop waiting for millis to change inside one observation.\", \"millis is fixed during a supplied observation. Re-reading it in a busy loop cannot advance simulation time.\"],\n fix: [\"Repair elapsed-time logic and rerun the 499/500-ms and midway-stop cases. Keep intervals at least 500 ms and verify pause/resume uses supplied time without creating extra transitions.\", \"Use one bounded decision per loop call; the host will supply the next observation when simulation advances.\"],\n explain: [\"Choose why comparing timestamps lets the program stay responsive while waiting for a pattern interval. Explain why repeatedly reading a fixed observation's clock cannot make time move.\", \"Waiting is represented by state between calls, not by occupying execution until a later time arrives.\"],\n reward: [\"Save Responsive timing. Your beacon now changes over time and can still stop promptly. Next you will give a reusable multi-stage signal pattern a named function.\", \"Keep the time-boundary trace for testing later patterns and message cooldowns.\"],\n },\n questions: {\n learn: { question: \"Where should lastChange live if it must persist between loop calls?\", choices: [\"In a local reset on every call\", \"In a global belonging to the simulator session\", \"Only in the display colour\"], correctChoice: 1, feedback: \"A session global retains the previous transition time so the next observation can calculate elapsed time correctly.\" },\n predict: { question: \"With lastChange=0 and a 500-ms interval, when does the first transition occur?\", choices: [\"At 499 ms\", \"On every observation\", \"At 500 ms or the first later observation\"], correctChoice: 2, feedback: \"The interval is reached when elapsed time is at least 500 ms; earlier observations leave the state unchanged.\" },\n explain: { question: \"Why use a timestamp comparison instead of a busy waiting loop?\", choices: [\"It allows the program to return and receive fresh inputs\", \"It removes the need for state\", \"It accesses the real device clock automatically\"], correctChoice: 0, feedback: \"A non-blocking decision finishes quickly, allowing later observations to carry new stop, button and timing information.\" },\n },\n },\n {\n title: \"A pattern worth naming\", concepts: [\"Functions\", \"Parameters\", \"Cyclic state\"],\n goals: [\"Create a named function that maps a bounded phase to its documented signal.\", \"Advance a three-stage pattern without copying its full logic into several event paths.\"],\n extension: \"Write another three-stage pattern in a separate helper and compare the two legends. Keep the same timing code so only the pattern mapping changes.\",\n activities: {\n learn: [\"A helper function gives a name to a useful operation. Write showPhase(int phase) to select red for zero, amber for one and green for two. Advance phase with (phase+1)%3 at each timed transition, and call the helper instead of copying those choices everywhere.\", \"The remainder operator wraps two plus one back to zero. The function definition alone does not emit a signal until called.\"],\n predict: [\"Starting at phase zero, predict the next four phases after repeated increments modulo three. Then define showPhase without calling it and decide whether any pattern output should appear.\", \"Track the numeric phase separately from its displayed colour, and remember that a definition describes work rather than performing it.\"],\n build: [\"Add the showPhase helper and a global phase. Call it for the initial active phase and after each 500-ms transition. Keep the stop branch outside the helper and before the pattern update so stopping cannot be hidden by reuse.\", \"Use if/else comparisons for the three supported phase values; provide a safe off result for an unexpected value.\"],\n run: [\"Step through two full cycles and match each phase to the legend. Pause, stop and restart at different phases. Confirm each call produces the same output for the same phase regardless of where it was invoked.\", \"A named helper should simplify explanation, not hide a timestamp change or stop override as an unexpected side effect.\"],\n assess: [\"Check the phase mapping, wraparound, reusable function call and retained stop priority. Cases include an unexpected phase value to verify the helper's safe fallback.\", \"The function should map its parameter, not ignore it and always select the current global phase.\"],\n inspect: [\"If every call shows the same colour, inspect use of the parameter. If phase grows without wrapping, inspect modulo. If stop is overridden, inspect the order of helper calls after the priority branch.\", \"Test showPhase with each small input before debugging the whole timed pattern.\"],\n fix: [\"Repair the helper or phase transition and replay two cycles. Keep timing at the same interval while checking mapping, then rerun the stop and restart cases from earlier missions.\", \"Changing both timing and colour mapping together makes it harder to identify which repair changed the behaviour.\"],\n explain: [\"Choose why a parameterised helper is more useful than several copied signal branches. Explain how its input and output contract lets you test one piece independently.\", \"A clear function name and a small parameter describe a decision that can be reused and checked on its own.\"],\n reward: [\"Save Named pattern. You have combined functions with persistent timed state. The next mission responds to a real input transition so a held button cannot trigger the same action repeatedly.\", \"Keep the helper's mapping examples as a reference when adding receiver-driven signals.\"],\n },\n questions: {\n learn: { question: \"What makes showPhase reusable?\", choices: [\"Reading no input and always selecting red\", \"Copying it under several different names\", \"Mapping its phase parameter through one defined rule\"], correctChoice: 2, feedback: \"A parameterised mapping can be called from several places while keeping the signal rule in one understandable function.\" },\n predict: { question: \"What are the next four phases after zero using (phase+1)%3?\", choices: [\"1, 2, 0, 1\", \"1, 2, 3, 4\", \"0, 0, 0, 0\"], correctChoice: 0, feedback: \"Remainder modulo three keeps the cycle within zero, one and two before returning to zero.\" },\n explain: { question: \"Why include a safe fallback in the phase helper?\", choices: [\"To ignore stop\", \"To give unexpected input an explicit harmless result\", \"To replace every valid phase with an error\"], correctChoice: 1, feedback: \"A defined off result avoids an accidental signal when the phase is outside the supported mapping.\" },\n },\n },\n {\n title: \"A button is an event\", concepts: [\"Rising edge\", \"Fresh sensors\", \"Stateful input\"],\n goals: [\"React once to a new button press and re-arm only after release.\", \"Keep stop and receiver observations distinct from a stale remembered input.\"],\n extension: \"Compare press, hold, release and press again as four observations. Explain which transitions count as events and which are repeated state.\",\n activities: {\n learn: [\"A held button can be true in many observations, but a new press happens only when current true follows previous false. Store wasPressed globally, compute rising=pressed&&!wasPressed, then update wasPressed. This separates one event from repeated observations of the same state.\", \"Read boolSensor(\\\"button\\\") each time. A value captured only during setup never describes later interaction.\"],\n predict: [\"Predict the rising-edge results for false, true, true, false, true. Then stop while the button is held and decide whether releasing stop alone should manufacture another press event.\", \"Update the remembered button state even while stopped, so an old hold does not become a new activation on resume.\"],\n build: [\"Add edge detection before the stop return while keeping stop's output priority. Use a new press to select the next named signal phase once. Read receiver text separately and map a fresh ack to green without treating it as a button press.\", \"The input records describe different channels. Keep previous button state separate from message identity and age.\"],\n run: [\"Press, hold, release and press again using keyboard and onscreen controls. Step through each observation and inspect wasPressed. Stop during a hold, then release stop while still holding and compare the event count.\", \"Holding a control should preserve state without behaving like an unlimited stream of fresh presses.\"],\n assess: [\"Check the five-observation edge sequence, long holds, release re-arming and stop/resume during a hold. Receiver tests distinguish none, ack and unknown strings without copying stale values from setup.\", \"A test may supply the same true value repeatedly; only the first transition from false is an event.\"],\n inspect: [\"If holding advances every phase, inspect the previous-state comparison. If only the first ever press works, inspect updates after release. If resume triggers unexpectedly, inspect the remembered state around the stop return.\", \"Trace both current and previous button values before deciding whether the rising expression is correct.\"],\n fix: [\"Repair edge detection and repeat the hold-through-stop scenario. Preserve responsive timing and receiver handling, and confirm an actual release followed by a new press still works after the repair.\", \"Rejecting every press would remove repeats but also remove useful interaction. Keep the valid edge path.\"],\n explain: [\"Choose why observing true repeatedly is different from receiving several new presses. Explain how one small piece of persistent state helps convert observations into meaningful events.\", \"Events describe change over time; a single observation tells only the current state.\"],\n reward: [\"Save Deliberate input. Your beacon now responds once per intentional press and reads fresh observations. Next it will exchange bounded messages without repeating stale acknowledgements.\", \"Keep the edge sequence as a regression case for the final network programme.\"],\n },\n questions: {\n learn: { question: \"Which expression identifies a new press from current and previous button states?\", choices: [\"pressed && !wasPressed\", \"pressed || wasPressed\", \"wasPressed alone\"], correctChoice: 0, feedback: \"A rising edge requires the button to be true now and false in the previous observation.\" },\n predict: { question: \"How many new presses occur in false, true, true, false, true?\", choices: [\"Three\", \"Two\", \"Five\"], correctChoice: 1, feedback: \"Only the two false-to-true transitions count. The repeated true observation is part of one held press.\" },\n explain: { question: \"Why remember button state even while stopped?\", choices: [\"To send packets during stop\", \"To ignore future releases\", \"To avoid treating a continuing hold as a fresh press after resume\"], correctChoice: 2, feedback: \"Maintaining observation history prevents stop/resume from inventing a transition that the button did not make.\" },\n },\n },\n {\n title: \"A message with a limit\", concepts: [\"Message identity\", \"Freshness\", \"Rate limits\"],\n goals: [\"Acknowledge each fresh help message once while rejecting stale, duplicate and unknown packets.\", \"Bound simulated transmission to one packet per second and give stop priority over sending.\"],\n extension: \"Create a timeline with duplicate help packets and a delayed acknowledgement. Explain which packet ids should be handled and why an ack must not create another ack.\",\n activities: {\n learn: [\"A receiver may show the same packet in several observations. Use messageId to remember which help was acknowledged and messageAgeMs to reject negative or over-1000-ms ages. Ignore your own senderId, unknown packet text and acknowledgements as reply triggers. Limit every transmission with lastSent.\", \"A response to every observation creates duplicates; replying to acknowledgements can create an endless feedback loop.\"],\n predict: [\"A fresh help packet id seven is observed twice, followed by an ack and a stale help id eight. Predict the acknowledgements. Then a valid packet arrives 400 ms after the last send: decide whether the one-second transmission rule allows an immediate reply.\", \"Update lastAcknowledged only when the reply is actually sent. A rate-limited attempt must not falsely record successful acknowledgement.\"],\n build: [\"Track lastAcknowledged=-1 and lastSent=-1000 globally. Reply ack only to a fresh external help id not already acknowledged and when millis()-lastSent>=1000. Record both values after sending. Keep stop first and never reply to ack, none or unknown text.\", \"All transmissions share the same lastSent budget. Separate unlimited counters for help and ack could exceed the node's total limit.\"],\n run: [\"Use the packet controls to supply new, repeated, stale and unknown messages. Step through a cooldown boundary and inspect sent packet ids/times. Confirm the trace explicitly identifies ignored input without opening any physical infrared device.\", \"The simulator uses supplied message records. No camera, serial port or transmitter permission is part of this exercise.\"],\n assess: [\"Check duplicate ids, freshness boundaries, self-origin packets, ack-loop prevention, missing cooldown and stop while receiving. A passing trace emits at most one packet per 1000 ms and never claims an unsent reply as handled.\", \"Different ids can still arrive too quickly; identity checking does not replace the time budget.\"],\n inspect: [\"If the same help is acknowledged twice, inspect saved identity. If packets flood, inspect the shared cooldown and ack branch. If a delayed message is lost permanently, inspect whether it was marked handled before sending.\", \"Track three separate facts: what arrived, what was permitted and what was actually transmitted.\"],\n fix: [\"Repair the first invalid transmission and replay its timeline plus a valid new help message. Keep stale and self-origin packets rejected, and recheck that stop emits neither help nor ack.\", \"A repair must preserve useful communication; simply disabling every transmission would not satisfy a valid-message scenario.\"],\n explain: [\"Choose why identity, freshness and rate limits solve different problems. Explain why an acknowledgement is the end of this small exchange rather than a request for another acknowledgement.\", \"Each rule needs its own evidence: a new id can be stale, and a fresh new id can still arrive during cooldown.\"],\n reward: [\"Save Bounded messages. Your beacon now communicates through a small explicit protocol. The capstone joins button events, signal patterns and receiver handling into a recoverable simulated network.\", \"Keep the duplicate and cooldown timelines as regression tests for the final integration.\"],\n },\n questions: {\n learn: { question: \"Which value prevents acknowledging the same help observation repeatedly?\", choices: [\"Only the colour\", \"The last successfully acknowledged message id\", \"The array index of the signal legend\"], correctChoice: 1, feedback: \"Remembering the handled id distinguishes one packet observed repeatedly from a genuinely new help message.\" },\n predict: { question: \"A fresh new help arrives 400 ms after the last send; may this node transmit immediately?\", choices: [\"Yes, because the id is new\", \"Yes, because it is fresh\", \"No, the shared 1000-ms transmission interval still applies\"], correctChoice: 2, feedback: \"Identity and freshness do not remove the node's rate limit. All outgoing packets share the same transmission budget.\" },\n explain: { question: \"Why should ack packets not trigger another ack?\", choices: [\"To terminate the exchange instead of creating a response loop\", \"To make all help messages stale\", \"To erase sender identity\"], correctChoice: 0, feedback: \"An acknowledgement closes this protocol exchange. Acknowledging acknowledgements would create repeated traffic without new information.\" },\n },\n },\n {\n title: \"The rescue beacon network\", concepts: [\"Integration\", \"Protocol priority\", \"Accessible status\"],\n goals: [\"Combine deliberate help requests, one-time acknowledgements and clear signal states.\", \"Keep stop, duplicate handling and the shared transmission budget correct across several simulated nodes.\"],\n extension: \"Design another message type on paper with a clear sender, receiver action, freshness rule and termination condition. Keep it separate from the tested help/ack protocol until those rules are implemented and checked.\",\n activities: {\n learn: [\"The network runs the same programme in separate node sessions with their own globals and nodeId. A new local button press can send help within the shared budget; a fresh external help can receive one ack. Give eligible acknowledgements priority over a simultaneous new local request, and never transmit while stopped.\", \"A button press during cooldown is ignored in this simple protocol. State that limit clearly instead of pretending the request was queued or sent.\"],\n predict: [\"At one node, a new button press and a fresh external help arrive together while transmission is available. Predict which packet wins, then follow an ack through a second node. Decide whether it creates any further response.\", \"One shared budget permits one outgoing packet. The documented priority selects ack and avoids a hidden two-packet burst.\"],\n build: [\"Integrate input edges, receiver validation, acknowledgement priority and local help in one loop. Update button history even while stopped, keep cooldown shared and expose signal states consistently. Preserve separate per-session message and timing history.\", \"Use helpers for mapping signals or testing freshness, while keeping the order of stop and transmission decisions obvious.\"],\n run: [\"Operate the simulated network with keyboard controls, pause on a simultaneous event and inspect each node's packet timeline and signal label. Try duplicate delivery, stop/resume and a restart, then compare independent node histories.\", \"No physical equipment is accessed. The text timeline and static shapes provide the same information when motion or colour is not useful.\"],\n assess: [\"Run the final saved-source cases across several node ids and message timelines. Checks combine signal timing, press edges, freshness, duplicates, self-origin packets, rate limits, ack priority and stop behaviour.\", \"A single successful exchange is not enough to establish that the protocol avoids loops and remains bounded under repeated inputs.\"],\n inspect: [\"Locate the first invalid packet or signal in a failing timeline and identify its input, node id and prior state. Distinguish a missing edge, stale sample, duplicate id and exhausted cooldown before editing the programme.\", \"Look for the first rule violation rather than counting only the final number of packets.\"],\n fix: [\"Repair that decision and replay both the failure timeline and an ordinary help/ack exchange. Save and assess the new source, checking that a local change has not removed stop priority or mixed state between nodes.\", \"A node's global variables are its own history. Avoid encoding answers for specific node ids or known timelines.\"],\n explain: [\"Choose what the network tests establish and describe one deliberate limitation of the simple protocol. Explain how clear priority, bounded work and a terminating exchange made the behaviour understandable.\", \"The project is a simulator teaching programming and protocol reasoning, not a certified emergency communication system.\"],\n reward: [\"Save Rescue network and complete the final assessment. You have created a responsive beacon with reusable logic, deliberate input and bounded messaging. Replay any mission or explore another pattern in a named save while retaining earned completion.\", \"Keep the finished simulator project before experimenting. Physical builds remain a separate activity with their existing guardian and hardware protections.\"],\n },\n questions: {\n learn: { question: \"How should each simulated node store its timing and message history?\", choices: [\"In one shared mutable history for every node\", \"Only in the scene's background colour\", \"In its own isolated session globals\"], correctChoice: 2, feedback: \"Each node has independent observations and transmissions, so its persistent state must belong to its own session.\" },\n predict: { question: \"An eligible ack and a new local help request coincide with one transmission available; which is sent?\", choices: [\"Ack under the documented priority\", \"Both immediately\", \"Neither because all communication is disabled\"], correctChoice: 0, feedback: \"The protocol gives acknowledgement priority and applies one shared transmission budget, avoiding an unexpected burst.\" },\n explain: { question: \"What does passing the simulator's network tests demonstrate?\", choices: [\"Approval to operate emergency hardware\", \"Evidence for the documented bounded protocol scenarios\", \"A guarantee that future changes need no checks\"], correctChoice: 1, feedback: \"The tests support the stated simulator behaviours; they do not certify physical equipment or eliminate the need to verify changes.\" },\n },\n },\n]);\n"],"mappings":"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","names":[]}