@plasius/learning 0.4.0 → 0.5.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 (36) hide show
  1. package/README.md +16 -0
  2. package/dist/chunk-2UIOBMJZ.js +44 -0
  3. package/dist/chunk-2UIOBMJZ.js.map +1 -0
  4. package/dist/courses/meteor-shield.cjs +369 -0
  5. package/dist/courses/meteor-shield.cjs.map +1 -0
  6. package/dist/courses/meteor-shield.d.cts +6 -0
  7. package/dist/courses/meteor-shield.d.ts +6 -0
  8. package/dist/courses/meteor-shield.js +175 -0
  9. package/dist/courses/meteor-shield.js.map +1 -0
  10. package/dist/courses/pixel-trail-challenge.cjs +366 -0
  11. package/dist/courses/pixel-trail-challenge.cjs.map +1 -0
  12. package/dist/courses/pixel-trail-challenge.d.cts +6 -0
  13. package/dist/courses/pixel-trail-challenge.d.ts +6 -0
  14. package/dist/courses/pixel-trail-challenge.js +172 -0
  15. package/dist/courses/pixel-trail-challenge.js.map +1 -0
  16. package/dist/courses/rescue-crew-commander.cjs +369 -0
  17. package/dist/courses/rescue-crew-commander.cjs.map +1 -0
  18. package/dist/courses/rescue-crew-commander.d.cts +6 -0
  19. package/dist/courses/rescue-crew-commander.d.ts +6 -0
  20. package/dist/courses/rescue-crew-commander.js +175 -0
  21. package/dist/courses/rescue-crew-commander.js.map +1 -0
  22. package/dist/courses/robot-maze-dash.js +3 -38
  23. package/dist/courses/robot-maze-dash.js.map +1 -1
  24. package/dist/courses/skywing-sprint.cjs +367 -0
  25. package/dist/courses/skywing-sprint.cjs.map +1 -0
  26. package/dist/courses/skywing-sprint.d.cts +6 -0
  27. package/dist/courses/skywing-sprint.d.ts +6 -0
  28. package/dist/courses/skywing-sprint.js +173 -0
  29. package/dist/courses/skywing-sprint.js.map +1 -0
  30. package/dist/courses/star-defender-squadron.cjs +369 -0
  31. package/dist/courses/star-defender-squadron.cjs.map +1 -0
  32. package/dist/courses/star-defender-squadron.d.cts +6 -0
  33. package/dist/courses/star-defender-squadron.d.ts +6 -0
  34. package/dist/courses/star-defender-squadron.js +175 -0
  35. package/dist/courses/star-defender-squadron.js.map +1 -0
  36. package/package.json +26 -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,175 @@
1
+ import {
2
+ authorCourse
3
+ } from "../chunk-2UIOBMJZ.js";
4
+ import "../chunk-OXWMIHYI.js";
5
+
6
+ // src/courses/meteor-shield.ts
7
+ var { course, practice } = authorCourse({
8
+ slug: "meteor-shield",
9
+ title: "Meteor Shield",
10
+ category: "game",
11
+ summary: "Build a defence game that protects three research stations. Learn coordinates and vectors, launch interceptors, grow short-lived shields, resolve waves and manage limited energy. Your final project must survive a repeatable meteor shower and reset cleanly for another attempt.",
12
+ projectFiles: [{ path: "game.js", language: "javascript", maximumCharacters: 32e3 }],
13
+ starterProject: { files: [{ path: "game.js", source: `function initialState() {
14
+ return {
15
+ bases: [{ id: "west", x: 60, y: 330, health: 3 },
16
+ { id: "centre", x: 240, y: 330, health: 3 },
17
+ { id: "east", x: 420, y: 330, health: 3 }],
18
+ meteors: [], seenMeteorIds: [], interceptors: [], shields: [],
19
+ energy: 10, score: 0, elapsed: 0, wave: 1, nextId: 1, status: "ready"
20
+ };
21
+ }
22
+
23
+ function update(state, input) {
24
+ return state;
25
+ }
26
+ ` }] },
27
+ reference: [
28
+ { name: "initialState", signature: "initialState() -> state", description: "Create fresh arrays and three research stations in a 480 by 360 world. The launcher is fixed at (240,340). Ready, playing, over and won are the permitted status values.", example: 'if (input.action === "restart") return initialState();' },
29
+ { name: "update", signature: "update(state, input) -> state", description: "Handle start, launch, repair, tick or restart. Launch includes x and y; repair includes baseId. A playing tick supplies dt=0.02 seconds, spawns, wave and waveComplete.", example: 'if (input.action === "start" && state.status === "ready") state.status = "playing";' },
30
+ { name: "distance", signature: "Math.hypot(dx, dy)", description: "Find the remaining straight-line distance. Move at most speed \xD7 dt toward a target; clamp to the target when closer than one step and avoid division by zero.", example: "const d = Math.hypot(targetX - x, targetY - y); const step = Math.min(d, speed * dt);" },
31
+ { name: "input.spawns", signature: "Array<{ id, x, y, targetId, speed }>", description: "The host supplies at most eight arrivals per tick and 32 distinct meteors per wave. Keep seenMeteorIds until the next wave so a repeated arrival cannot revive a destroyed meteor. Copy new ids once and move toward their named stations.", example: "const target = state.bases.find(base => base.id === meteor.targetId);" },
32
+ { name: "interceptors", signature: "Array<{ id, x, y, targetX, targetY }>", description: "A valid launch costs one energy and starts an interceptor at (240,340). It moves at 180 pixels per second. On arrival replace it with a shield at the selected point; nextId supplies unique local ids.", example: 'const id = "shot-" + state.nextId; state.nextId += 1;' },
33
+ { name: "shields", signature: "Array<{ id, x, y, age, radius }>", description: "A shield grows at 60 pixels per second to radius 36, stays until age 1.2 seconds, then expires. During a tick resolve shield hits before station impacts. Each destroyed meteor awards one point once.", example: "const radius = Math.min(36, age * 60); const hit = Math.hypot(meteor.x - shield.x, meteor.y - shield.y) <= radius;" },
34
+ { name: "energy", signature: "energy: number in [0,10]", description: "Regenerate 0.25 energy per playing second, capped at 10. Launch requires at least one energy. Repair costs three and adds one health to a living damaged station, up to three; a destroyed station cannot be revived.", example: "state.energy = Math.min(10, state.energy + 0.25 * input.dt);" },
35
+ { name: "waveComplete", signature: "input.waveComplete: boolean", description: "True means no more meteors will be supplied for this wave. Win after wave three is complete with no meteors and at least one living station. Lose when every station reaches zero health; terminal states ignore launch and tick.", example: "const alive = state.bases.some(base => base.health > 0);" }
36
+ ]
37
+ }, [
38
+ {
39
+ title: "Stations on the map",
40
+ concepts: ["Coordinates", "Records", "Invariants"],
41
+ goals: ["Represent three distinct stations with bounded health and stable identities.", "Start and restart a defence session without retaining state from the previous attempt."],
42
+ extension: "Sketch another station layout, save it separately and explain how more widely spaced stations change the defence problem.",
43
+ activities: {
44
+ learn: ["A station is a record with an id, position and health. The world origin is the top-left: x increases rightwards and y downwards. Keep health between zero and three, and identify a station by id even if its array position changes.", "Draw the three starter stations at y=330 before changing code. Their different x values explain which side each protects."],
45
+ predict: ["Compare west at (60,330), centre at (240,330) and east at (420,330). Predict which station is closest to the launcher at (240,340), and which coordinate changes when selecting a station further right.", "Distance depends on both coordinates. A small vertical difference does not erase a large horizontal difference."],
46
+ build: ["Handle start by changing ready to playing, and handle restart by returning initialState. Keep all three stations and their initial health, empty projectile arrays, full energy and a zero score. Advance elapsed only on a playing tick.", "Create new arrays on restart. Reusing a mutated array can make a second attempt begin with an already damaged station."],
47
+ run: ["Start, pause and restart the preview. Use the station list as well as the map to compare coordinates and health. Confirm that time remains still while ready and that restart restores every station.", "The station list provides the same state without requiring you to interpret colour or position alone."],
48
+ assess: ["Check fresh state, start, ready ticks and repeated restarts. The checks require unique station ids, bounded health and a clean new attempt. A correct picture cannot compensate for a missing station record.", "Inspect which field differs from its initial value after restart rather than resetting fields at random."],
49
+ inspect: ["Find any state that survives a restart unexpectedly. Trace where the initial arrays are created and whether initialState returns the same previously changed object. Compare the three station ids for accidental duplicates.", "A factory function should create a new attempt each time it is called, not return a global mutable template."],
50
+ fix: ["Repair state creation or start handling and repeat the ready-to-playing-to-restart sequence twice. Keep elapsed, score, energy and all object arrays consistent with a fresh session.", "Two consecutive restarts catch stale references that a single first attempt may hide."],
51
+ explain: ["Choose why stable ids matter when selecting a station to defend or repair. Explain why a station's position in an array is not a durable identity.", "Filtering or sorting can change indices while the station itself remains the same object in the world."],
52
+ reward: ["Save Station map. Your defence game now has a consistent starting state and a reliable new-attempt action. Next you will send an interceptor from the launcher to a chosen point.", "Keep the initial-state save as a reference for later health, score and energy bugs."]
53
+ },
54
+ questions: {
55
+ learn: { question: "What identifies a station when its array position changes?", choices: ["Its id", "Its current index", "Its current health"], correctChoice: 0, feedback: "An id describes the same station across sorting and filtering; health and indices can change." },
56
+ predict: { question: "Which starter station is closest to the launcher at (240,340)?", choices: ["West", "Centre", "East"], correctChoice: 1, feedback: "Centre has the same x coordinate and is only ten pixels above the launcher." },
57
+ explain: { question: "Why should initialState create fresh arrays on every restart?", choices: ["To keep damage from the last attempt", "To change the world size randomly", "To prevent previous mutations leaking into the new attempt"], correctChoice: 2, feedback: "A new attempt needs independent objects so earlier damage and projectiles cannot remain through shared references." }
58
+ }
59
+ },
60
+ {
61
+ title: "Aim beyond the launcher",
62
+ concepts: ["Vectors", "Normalisation", "Arrival"],
63
+ goals: ["Launch an interceptor toward a selected valid world position.", "Move at a constant speed and arrive exactly without overshooting or dividing by zero."],
64
+ extension: "Compare horizontal, vertical and diagonal launches with equal distances. Measure their simulated travel times to check that direction does not change speed.",
65
+ activities: {
66
+ learn: ["A target gives a direction vector: dx=targetX-x and dy=targetY-y. Divide by the distance to get a unit direction, then multiply by the allowed step. Clamp the step to the remaining distance so the interceptor can arrive exactly.", "Handle distance zero first. Dividing zero by zero creates an invalid number that cannot describe a position."],
67
+ predict: ["An interceptor travels at 180 pixels per second. Predict its movement during a 0.02-second tick, and what should happen if the target is only two pixels away. Consider a launch aimed directly at the launcher's position.", "The usual step is 3.6 pixels, but a closer target needs a shorter step and immediate arrival."],
68
+ build: ["Handle a launch at finite x and y inside the world. Create an interceptor at (240,340) with a unique id and its target coordinates. During ticks move it toward that target, clamping arrival; replace an arrived interceptor with a new age-zero shield.", "Reject invalid coordinates without spending energy or creating a projectile. Keep energy charging for the resource mission, but keep the validity check now."],
69
+ run: ["Aim above, left and right of the launcher using pointer controls and the keyboard target control. Step through a near target and an exact launcher target. Observe the transition from travelling interceptor to stationary shield.", "The target coordinate readout helps compare directions without relying on a small pointer location."],
70
+ assess: ["Check straight and diagonal movement, a near target, zero distance and invalid target data. The evaluator checks finite coordinates and one shield per arrival, not merely a projectile disappearing from view.", "Removing the interceptor before creating its shield loses the arrival event. Creating the shield without removal repeats it next tick."],
71
+ inspect: ["If diagonal travel is too fast, inspect whether the direction was normalised. If the interceptor circles its target, inspect step clamping. If coordinates become invalid, inspect the zero-distance branch.", "Separate direction, distance and step length into named values while debugging the movement calculation."],
72
+ fix: ["Repair movement or arrival and repeat the three directional tests. Ensure one arriving id is removed once and produces exactly one shield. Then rerun start and restart to confirm temporary objects are cleared.", "Do not patch a specific target coordinate. The vector calculation should work for every permitted target."],
73
+ explain: ["Choose why clamping the movement step is necessary even when the speed calculation is correct. Explain how zero-distance handling keeps all state values finite.", "A fixed step can be larger than the final gap to the target. Arrival is a condition, not an endless oscillation."],
74
+ reward: ["Save Accurate interceptor. You have implemented directional movement and a one-time arrival transition. Next the shield will grow, intercept meteors and expire after a controlled lifetime.", "Keep the zero-distance example as a regression case whenever you change targeting."]
75
+ },
76
+ questions: {
77
+ learn: { question: "Why divide dx and dy by the remaining distance before applying speed?", choices: ["To increase diagonal speed", "To create a unit direction", "To remove the target"], correctChoice: 1, feedback: "Normalising separates direction from length, so the movement speed stays consistent in every direction." },
78
+ predict: { question: "With a 3.6-pixel step and a target two pixels away, how far should the interceptor move?", choices: ["3.6 pixels past the target", "Zero pixels forever", "Two pixels and arrive"], correctChoice: 2, feedback: "The step is clamped to the remaining distance, allowing exact arrival without overshooting." },
79
+ explain: { question: "What must happen before normalising a zero-distance vector?", choices: ["Handle arrival without division", "Divide by zero anyway", "Add a random direction"], correctChoice: 0, feedback: "The projectile is already at its target, so arrival can be handled directly without invalid arithmetic." }
80
+ }
81
+ },
82
+ {
83
+ title: "A shield with a lifetime",
84
+ concepts: ["Time", "Collision radius", "Unique events"],
85
+ goals: ["Grow and expire shields using simulated age rather than frame count.", "Destroy a meteor within a live shield once and award one corresponding point."],
86
+ extension: "Compare a small fast-growing shield with a larger slow-growing one in separate saves. Explain how the timing changes which meteor paths it can protect.",
87
+ activities: {
88
+ learn: ["A shield stores its age in seconds. Each tick increases age by dt and sets radius to min(36, age\xD760). Remove it at age 1.2. A meteor is intercepted when the distance from its centre to a live shield is no greater than the radius.", "A growing ring has both a position and a time-dependent reach. Being near where a shield used to exist is not a hit."],
89
+ predict: ["Predict a shield's radius at ages 0.2 and 0.8 seconds, then decide whether it still exists at age 1.2. Consider a meteor covered by two live shields on the same tick: how many points should it give?", "The radius stops growing at 36, and one meteor is still one destruction even if two shields overlap it."],
90
+ build: ["Update shield ages and radii on playing ticks, then discard expired shields. For each meteor, decide whether any live shield contains it. Remove intercepted meteors and add one point for each distinct removed id.", "Use one decision per meteor, such as some over the shield array. Adding points inside a nested hit loop can double-count overlapping shields."],
91
+ run: ["Create a shield on a meteor's path and step through growth, contact and expiry. Try two overlapping shields, then observe a meteor reaching the same point after they expire. Compare the score with the destroyed ids.", "Use the object's age and radius readouts to distinguish a late launch from a broken distance calculation."],
92
+ assess: ["Check radius before and after the growth cap, expiry at the lifetime boundary, a just-inside and just-outside meteor and overlapping shields. Each destroyed meteor must disappear and score exactly once.", "Test equality at the radius boundary as well as an obvious centre hit. Small edge cases define the collision rule."],
93
+ inspect: ["If score doubles, inspect whether each shield awards points independently for one meteor. If old shields keep defending, inspect the age filter. If growth depends on animation speed, inspect use of dt instead of tick count.", "Keep a list of intercepted ids for the current tick while reasoning; it should contain no duplicates."],
94
+ fix: ["Repair the lifetime or hit decision, then rerun overlap and expiry cases. Ensure expired shields are removed before collision checks and that stationary shields do not accidentally inherit an interceptor's movement.", "Separate updating the shield collection from filtering the meteor collection so each has a clear responsibility."],
95
+ explain: ["Choose why a meteor should be assessed against any shield and counted once, rather than awarding a point for every shield that contains it. Connect your answer to stable object identity.", "The score represents destroyed meteors, not the number of geometric overlaps observed by the program."],
96
+ reward: ["Save Timed shields. Your project now combines moving objects, time-limited areas and one-time collision events. Next the incoming meteors will threaten station health in organised waves.", "Keep your overlap scenario for regression testing when several objects are active together."]
97
+ },
98
+ questions: {
99
+ learn: { question: "Which clock should determine a shield's lifetime?", choices: ["Display redraw count", "The computer's current date", "Accumulated simulated dt"], correctChoice: 2, feedback: "Adding supplied simulated seconds makes growth and expiry reproducible while pausing remains predictable." },
100
+ predict: { question: "What radius does the shield have at age 0.8 seconds?", choices: ["36", "48", "0.8"], correctChoice: 0, feedback: "Age \xD7 60 would be 48, but the documented maximum caps the radius at 36 pixels." },
101
+ explain: { question: "One meteor is covered by two shields in the same tick; what score change is correct?", choices: ["Two points", "One point", "One point per later frame"], correctChoice: 1, feedback: "The meteor is destroyed once. Multiple shields confirming the same hit must not duplicate the reward." }
102
+ }
103
+ },
104
+ {
105
+ title: "The incoming waves",
106
+ concepts: ["Event order", "Collections", "Win and loss conditions"],
107
+ goals: ["Move unique seeded meteors toward their named stations and apply each impact once.", "Resolve shield hits before impacts and finish the campaign only when its documented conditions hold."],
108
+ extension: "Design a wave aimed at different stations rather than increasing only speed. Explain how target distribution changes the player's decisions.",
109
+ activities: {
110
+ learn: ["The host supplies a reproducible wave through input.spawns. Meteors follow their named station using the vector movement you learned. In each tick update movement and live shields, remove intercepted meteors, then apply surviving impacts. Clamp station health at zero.", "Event order resolves a meteor that enters both a shield and a station on the same tick: the shield gets the chance to protect it first."],
111
+ predict: ["A meteor reaches a station on a tick when a live shield also covers it. Predict the health and score changes under the documented event order. Then consider an empty meteor array before the wave has finished spawning.", "Empty now does not mean the wave is complete. The host's waveComplete signal tells you whether more arrivals remain."],
112
+ build: ["Accept only ids absent from seenMeteorIds, record them and move meteors toward valid target stations. Remove arrivals after one health loss. Reset seen ids only when the supplied wave advances. Lose when all health is zero; win after completed wave three has no meteors and at least one station remains.", "Do not end a wave solely from array length. Check wave number and waveComplete together with the remaining threats."],
113
+ run: ["Play a wave while deliberately leaving one meteor undefended. Pause at impact and confirm a single health decrement. Restart the same seed to defend it. Compare simultaneous shield contact and impact using the text event trace.", "A meteor left in the array after impact can damage the station every subsequent tick. Watch that id disappear."],
114
+ assess: ["Check duplicate spawns, station targeting, a single impact, simultaneous interception, all-stations-lost and final-wave completion. Intermediate empty intervals must leave a playing campaign active.", "Terminal state checks use several fields together. A score threshold is not a substitute for surviving the actual waves."],
115
+ inspect: ["If stations lose health repeatedly, inspect impact removal. If wins arrive early, inspect the waveComplete condition. If a covered station is still hit, inspect the order in which interception and impact are resolved.", "Trace one meteor id from spawn through movement to exactly one final event: intercepted or impacted."],
116
+ fix: ["Repair the first wrong event and replay the same seeded wave. Keep health within zero to three, ignore invalid target ids safely and preserve a terminal result when later ticks arrive. Recheck shield double-counting.", "A destroyed station remains a valid named target, but further impacts cannot make its health negative."],
117
+ explain: ["Choose why simulation order is part of the game's rules. Explain why an empty array and an explicit no-more-spawns signal answer different questions.", "One describes current state; the other describes whether future events are still scheduled."],
118
+ reward: ["Save Wave defence. You have a complete threat lifecycle and meaningful win and loss conditions. The next mission adds limited resources so that choosing when and where to act matters.", "Retain a wave with a simultaneous interception and impact as an edge-case save."]
119
+ },
120
+ questions: {
121
+ learn: { question: "Which event is resolved first when shield contact and station impact coincide?", choices: ["Shield interception", "Station damage twice", "A random choice"], correctChoice: 0, feedback: "The documented order gives live shields their interception check before surviving meteors can damage stations." },
122
+ predict: { question: "What does an empty meteor array before waveComplete mean?", choices: ["The whole campaign is won", "No threats are present now, but more may arrive", "Every station must be destroyed"], correctChoice: 1, feedback: "An empty current collection does not establish that the wave has finished supplying future meteors." },
123
+ explain: { question: "Why remove a meteor immediately after its station impact is applied?", choices: ["To erase all other meteors", "To regenerate full energy", "To prevent the same impact damaging the station again"], correctChoice: 2, feedback: "Each meteor has one terminal event. Removing it records that the impact has already been processed." }
124
+ }
125
+ },
126
+ {
127
+ title: "Energy and emergency repairs",
128
+ concepts: ["Resources", "Preconditions", "Atomic changes"],
129
+ goals: ["Spend and regenerate bounded energy with no negative balances.", "Apply repairs only when the selected station and available energy satisfy every requirement."],
130
+ extension: "Compare saving energy for repairs with spending it on early shields. Use the same wave seed and record both surviving health and unused energy.",
131
+ activities: {
132
+ learn: ["A resource action has preconditions and effects. A launch needs a valid target and at least one energy; then it creates one interceptor and spends one. A repair needs three energy and a living damaged station; then it adds one health and spends three.", "Check all preconditions before changing either side. An invalid repair should not spend energy without healing anything."],
133
+ predict: ["You have 2.9 energy and a station at health two. Predict a repair attempt, then a launch. Next consider a fully healthy station with energy ten. Decide whether a repair should be allowed to spend resources there.", "Fractional regeneration is real state: 2.9 is still less than the three required for repair."],
134
+ build: ["Charge valid launches one energy. Add repair handling for a selected baseId, requiring health greater than zero and less than three plus energy at least three. Regenerate 0.25\xD7dt while playing, capped at ten; rejected actions must preserve both resources and objects.", "Do not revive a destroyed station in this game's rules. Validate the selected id before reading its health."],
135
+ run: ["Launch until energy is low, attempt one more launch and inspect whether both energy and projectile count stay unchanged. Wait in simulated time, repair a damaged station, then try repairing it again at full health.", "Pause should also pause regeneration because no playing ticks are being supplied."],
136
+ assess: ["Check energy exactly at and just below costs, regeneration at the cap, repair of damaged, full, destroyed and missing stations, and actions after a terminal result. Failed preconditions must leave the entire action unapplied.", "A test may compare both health and energy. Getting one right while the other changes incorrectly is still a broken transaction."],
137
+ inspect: ["If energy disappears on rejected actions, find a subtraction before validation. If energy becomes negative, inspect the comparison at the cost boundary. If repairs exceed three health, inspect the station's precondition and cap.", "Write a before-and-after table for one action: energy, health and new object count. Every effect should agree with one valid decision."],
138
+ fix: ["Group each action's validation before its state changes, then repeat the boundary cases. Recheck an ordinary successful launch and repair as well as the rejected actions, and verify regeneration does not run while ready or over.", "A safety fix that rejects every action would stop invalid spending but also make the game unusable. Preserve the valid path."],
139
+ explain: ["Choose why the resource check and its effects belong to one coherent action. Explain the difference between a rejected action and a partially applied one using your repair trace.", "The player should either receive the documented effect at its cost or keep the previous state."],
140
+ reward: ["Save Resourceful defence. Your game now has meaningful limited choices and consistent resource rules. Next you will combine the whole defence campaign and test it through to a final result.", "Keep a low-energy save to revisit fractional boundaries when experimenting with recharge rates."]
141
+ },
142
+ questions: {
143
+ learn: { question: "When should a repair spend its three energy?", choices: ["Before checking the station", "After every precondition succeeds", "Even if the station id is unknown"], correctChoice: 1, feedback: "Validate the station and balance first, then apply the health increase and resource cost together." },
144
+ predict: { question: "With 2.9 energy, what should an attempted three-energy repair do?", choices: ["Spend 2.9 and repair anyway", "Make energy negative", "Leave health and energy unchanged"], correctChoice: 2, feedback: "The balance is below the full cost, so the action is rejected without partial effects." },
145
+ explain: { question: "What is wrong with charging for an invalid repair and then returning?", choices: ["It applies only the cost without the promised effect", "It creates a new station", "It makes regeneration too precise"], correctChoice: 0, feedback: "A partially applied action breaks the resource contract: the player loses energy while receiving no valid repair." }
146
+ }
147
+ },
148
+ {
149
+ title: "The last station",
150
+ concepts: ["Integration", "Strategy", "Regression evidence"],
151
+ goals: ["Run a complete three-wave defence with consistent movement, shields, impacts and resources.", "Keep controls usable and restart cleanly after either victory or defeat."],
152
+ extension: "Create an alternative difficulty profile in a separate save. Change one parameter, state its intended effect and rerun movement, impact and resource boundaries before comparing strategies.",
153
+ activities: {
154
+ learn: ["The final defence depends on many small contracts working together: targets are valid, movement is bounded, shields expire, impacts happen once and resources are conserved. Combine these into a campaign without replacing real outcomes with a manually assigned score or win state.", "A good strategy and a correct simulation are different things. The evaluator checks the rules even when a player loses a difficult attempt."],
155
+ predict: ["Predict what should happen when the final meteor of wave three is intercepted with one living station, and when it impacts the last health point instead. Include status, score, remaining meteors and whether another launch is permitted.", "Resolve the meteor's terminal event first, then decide victory or defeat from the resulting world state."],
156
+ build: ["Organise the completed update into clear event and tick paths. Preserve the documented resolution order and use small helpers for movement or collision when useful. Ensure both won and over states remain stable until restart.", "Keep helpers focused: moving a projectile should not unexpectedly charge energy or alter station health."],
157
+ run: ["Play all three waves with keyboard targeting and then pointer or touch aiming. Pause to inspect a crowded scene, test repair selection from the station list and restart after both final outcomes. Use the text state if animation is difficult to follow.", "The same launch and repair actions should reach your project regardless of which accessible control produced them."],
158
+ assess: ["Run the final saved-project assessment across seeded waves and boundary scenarios. It checks direction, arrival, lifetime, duplicate events, simultaneous contacts, terminal states and valid or rejected resource actions together.", "Read a failed rule before changing your strategy. A rule failure must be fixed in the project rather than hidden by selecting an easier wave."],
159
+ inspect: ["Trace the earliest incorrect event in a failing campaign and link it to its teaching mission. Separate a missed defensive shot from a double impact or invalid energy change. Find which invariant first stopped being true.", "A late defeat may be correct gameplay; a station losing two health to one meteor is an implementation defect."],
160
+ fix: ["Repair the failing rule, replay its focused case and rerun previously passing wave and resource checks. Save the new source before assessing it, then confirm the final result refers to that exact project version.", "Do not hard-code the final status for a known seed. The same rules must work with different supplied meteor paths."],
161
+ explain: ["Choose what makes the completed defence trustworthy, then identify a strategy trade-off between immediate interception and reserving energy for repairs. Use a trace to support the difference between rules and player decisions.", "Explain which conditions your tests cover and keep future improvements separate from what the current project actually does."],
162
+ reward: ["Save Last station and complete the final assessment. You have created a defence game with moving threats, timed shields, explicit outcomes and fair resource accounting. Replay any mission or keep a separate experimental difficulty save.", "Your account keeps earned completion while you experiment; preserve a stable named version before a larger redesign."]
163
+ },
164
+ questions: {
165
+ learn: { question: "What should determine the final outcome of the defence campaign?", choices: ["A hard-coded success after enough redraws", "The name of the save slot", "The actual remaining threats, completed wave and station health"], correctChoice: 2, feedback: "Victory and defeat must follow the simulated world and documented rules, not a display counter or chosen label." },
166
+ predict: { question: "The final meteor is intercepted after wave three completes and one station survives; what follows?", choices: ["A won state with the meteor removed", "An automatic station impact as well", "Unlimited new launches after the result"], correctChoice: 0, feedback: "Interception removes the last threat. With the final wave complete and a station alive, the campaign ends in a stable won state." },
167
+ explain: { question: "Which observation is an implementation defect rather than simply a difficult strategy?", choices: ["A player aims too late", "One meteor damages the same station on several later ticks", "A player reserves energy instead of firing"], correctChoice: 1, feedback: "Repeated damage from one already processed impact violates the one-terminal-event rule regardless of the player's strategy." }
168
+ }
169
+ }
170
+ ]);
171
+ export {
172
+ course,
173
+ practice
174
+ };
175
+ //# sourceMappingURL=meteor-shield.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/courses/meteor-shield.ts"],"sourcesContent":["import { authorCourse } from \"./course-authoring.js\";\n\nexport const { course, practice } = authorCourse({\n slug: \"meteor-shield\", title: \"Meteor Shield\", category: \"game\",\n summary: \"Build a defence game that protects three research stations. Learn coordinates and vectors, launch interceptors, grow short-lived shields, resolve waves and manage limited energy. Your final project must survive a repeatable meteor shower and reset cleanly for another attempt.\",\n projectFiles: [{ path: \"game.js\", language: \"javascript\", maximumCharacters: 32000 }],\n starterProject: { files: [{ path: \"game.js\", source: `function initialState() {\n return {\n bases: [{ id: \"west\", x: 60, y: 330, health: 3 },\n { id: \"centre\", x: 240, y: 330, health: 3 },\n { id: \"east\", x: 420, y: 330, health: 3 }],\n meteors: [], seenMeteorIds: [], interceptors: [], shields: [],\n energy: 10, score: 0, elapsed: 0, wave: 1, nextId: 1, status: \"ready\"\n };\n}\n\nfunction update(state, input) {\n return state;\n}\n` }] },\n reference: [\n { name: \"initialState\", signature: \"initialState() -> state\", description: \"Create fresh arrays and three research stations in a 480 by 360 world. The launcher is fixed at (240,340). Ready, playing, over and won are the permitted status values.\", example: 'if (input.action === \"restart\") return initialState();' },\n { name: \"update\", signature: \"update(state, input) -> state\", description: \"Handle start, launch, repair, tick or restart. Launch includes x and y; repair includes baseId. A playing tick supplies dt=0.02 seconds, spawns, wave and waveComplete.\", example: 'if (input.action === \"start\" && state.status === \"ready\") state.status = \"playing\";' },\n { name: \"distance\", signature: \"Math.hypot(dx, dy)\", description: \"Find the remaining straight-line distance. Move at most speed × dt toward a target; clamp to the target when closer than one step and avoid division by zero.\", example: \"const d = Math.hypot(targetX - x, targetY - y); const step = Math.min(d, speed * dt);\" },\n { name: \"input.spawns\", signature: \"Array<{ id, x, y, targetId, speed }>\", description: \"The host supplies at most eight arrivals per tick and 32 distinct meteors per wave. Keep seenMeteorIds until the next wave so a repeated arrival cannot revive a destroyed meteor. Copy new ids once and move toward their named stations.\", example: \"const target = state.bases.find(base => base.id === meteor.targetId);\" },\n { name: \"interceptors\", signature: \"Array<{ id, x, y, targetX, targetY }>\", description: \"A valid launch costs one energy and starts an interceptor at (240,340). It moves at 180 pixels per second. On arrival replace it with a shield at the selected point; nextId supplies unique local ids.\", example: 'const id = \"shot-\" + state.nextId; state.nextId += 1;' },\n { name: \"shields\", signature: \"Array<{ id, x, y, age, radius }>\", description: \"A shield grows at 60 pixels per second to radius 36, stays until age 1.2 seconds, then expires. During a tick resolve shield hits before station impacts. Each destroyed meteor awards one point once.\", example: \"const radius = Math.min(36, age * 60); const hit = Math.hypot(meteor.x - shield.x, meteor.y - shield.y) <= radius;\" },\n { name: \"energy\", signature: \"energy: number in [0,10]\", description: \"Regenerate 0.25 energy per playing second, capped at 10. Launch requires at least one energy. Repair costs three and adds one health to a living damaged station, up to three; a destroyed station cannot be revived.\", example: \"state.energy = Math.min(10, state.energy + 0.25 * input.dt);\" },\n { name: \"waveComplete\", signature: \"input.waveComplete: boolean\", description: \"True means no more meteors will be supplied for this wave. Win after wave three is complete with no meteors and at least one living station. Lose when every station reaches zero health; terminal states ignore launch and tick.\", example: \"const alive = state.bases.some(base => base.health > 0);\" },\n ],\n}, [\n {\n title: \"Stations on the map\", concepts: [\"Coordinates\", \"Records\", \"Invariants\"],\n goals: [\"Represent three distinct stations with bounded health and stable identities.\", \"Start and restart a defence session without retaining state from the previous attempt.\"],\n extension: \"Sketch another station layout, save it separately and explain how more widely spaced stations change the defence problem.\",\n activities: {\n learn: [\"A station is a record with an id, position and health. The world origin is the top-left: x increases rightwards and y downwards. Keep health between zero and three, and identify a station by id even if its array position changes.\", \"Draw the three starter stations at y=330 before changing code. Their different x values explain which side each protects.\"],\n predict: [\"Compare west at (60,330), centre at (240,330) and east at (420,330). Predict which station is closest to the launcher at (240,340), and which coordinate changes when selecting a station further right.\", \"Distance depends on both coordinates. A small vertical difference does not erase a large horizontal difference.\"],\n build: [\"Handle start by changing ready to playing, and handle restart by returning initialState. Keep all three stations and their initial health, empty projectile arrays, full energy and a zero score. Advance elapsed only on a playing tick.\", \"Create new arrays on restart. Reusing a mutated array can make a second attempt begin with an already damaged station.\"],\n run: [\"Start, pause and restart the preview. Use the station list as well as the map to compare coordinates and health. Confirm that time remains still while ready and that restart restores every station.\", \"The station list provides the same state without requiring you to interpret colour or position alone.\"],\n assess: [\"Check fresh state, start, ready ticks and repeated restarts. The checks require unique station ids, bounded health and a clean new attempt. A correct picture cannot compensate for a missing station record.\", \"Inspect which field differs from its initial value after restart rather than resetting fields at random.\"],\n inspect: [\"Find any state that survives a restart unexpectedly. Trace where the initial arrays are created and whether initialState returns the same previously changed object. Compare the three station ids for accidental duplicates.\", \"A factory function should create a new attempt each time it is called, not return a global mutable template.\"],\n fix: [\"Repair state creation or start handling and repeat the ready-to-playing-to-restart sequence twice. Keep elapsed, score, energy and all object arrays consistent with a fresh session.\", \"Two consecutive restarts catch stale references that a single first attempt may hide.\"],\n explain: [\"Choose why stable ids matter when selecting a station to defend or repair. Explain why a station's position in an array is not a durable identity.\", \"Filtering or sorting can change indices while the station itself remains the same object in the world.\"],\n reward: [\"Save Station map. Your defence game now has a consistent starting state and a reliable new-attempt action. Next you will send an interceptor from the launcher to a chosen point.\", \"Keep the initial-state save as a reference for later health, score and energy bugs.\"],\n },\n questions: {\n learn: { question: \"What identifies a station when its array position changes?\", choices: [\"Its id\", \"Its current index\", \"Its current health\"], correctChoice: 0, feedback: \"An id describes the same station across sorting and filtering; health and indices can change.\" },\n predict: { question: \"Which starter station is closest to the launcher at (240,340)?\", choices: [\"West\", \"Centre\", \"East\"], correctChoice: 1, feedback: \"Centre has the same x coordinate and is only ten pixels above the launcher.\" },\n explain: { question: \"Why should initialState create fresh arrays on every restart?\", choices: [\"To keep damage from the last attempt\", \"To change the world size randomly\", \"To prevent previous mutations leaking into the new attempt\"], correctChoice: 2, feedback: \"A new attempt needs independent objects so earlier damage and projectiles cannot remain through shared references.\" },\n },\n },\n {\n title: \"Aim beyond the launcher\", concepts: [\"Vectors\", \"Normalisation\", \"Arrival\"],\n goals: [\"Launch an interceptor toward a selected valid world position.\", \"Move at a constant speed and arrive exactly without overshooting or dividing by zero.\"],\n extension: \"Compare horizontal, vertical and diagonal launches with equal distances. Measure their simulated travel times to check that direction does not change speed.\",\n activities: {\n learn: [\"A target gives a direction vector: dx=targetX-x and dy=targetY-y. Divide by the distance to get a unit direction, then multiply by the allowed step. Clamp the step to the remaining distance so the interceptor can arrive exactly.\", \"Handle distance zero first. Dividing zero by zero creates an invalid number that cannot describe a position.\"],\n predict: [\"An interceptor travels at 180 pixels per second. Predict its movement during a 0.02-second tick, and what should happen if the target is only two pixels away. Consider a launch aimed directly at the launcher's position.\", \"The usual step is 3.6 pixels, but a closer target needs a shorter step and immediate arrival.\"],\n build: [\"Handle a launch at finite x and y inside the world. Create an interceptor at (240,340) with a unique id and its target coordinates. During ticks move it toward that target, clamping arrival; replace an arrived interceptor with a new age-zero shield.\", \"Reject invalid coordinates without spending energy or creating a projectile. Keep energy charging for the resource mission, but keep the validity check now.\"],\n run: [\"Aim above, left and right of the launcher using pointer controls and the keyboard target control. Step through a near target and an exact launcher target. Observe the transition from travelling interceptor to stationary shield.\", \"The target coordinate readout helps compare directions without relying on a small pointer location.\"],\n assess: [\"Check straight and diagonal movement, a near target, zero distance and invalid target data. The evaluator checks finite coordinates and one shield per arrival, not merely a projectile disappearing from view.\", \"Removing the interceptor before creating its shield loses the arrival event. Creating the shield without removal repeats it next tick.\"],\n inspect: [\"If diagonal travel is too fast, inspect whether the direction was normalised. If the interceptor circles its target, inspect step clamping. If coordinates become invalid, inspect the zero-distance branch.\", \"Separate direction, distance and step length into named values while debugging the movement calculation.\"],\n fix: [\"Repair movement or arrival and repeat the three directional tests. Ensure one arriving id is removed once and produces exactly one shield. Then rerun start and restart to confirm temporary objects are cleared.\", \"Do not patch a specific target coordinate. The vector calculation should work for every permitted target.\"],\n explain: [\"Choose why clamping the movement step is necessary even when the speed calculation is correct. Explain how zero-distance handling keeps all state values finite.\", \"A fixed step can be larger than the final gap to the target. Arrival is a condition, not an endless oscillation.\"],\n reward: [\"Save Accurate interceptor. You have implemented directional movement and a one-time arrival transition. Next the shield will grow, intercept meteors and expire after a controlled lifetime.\", \"Keep the zero-distance example as a regression case whenever you change targeting.\"],\n },\n questions: {\n learn: { question: \"Why divide dx and dy by the remaining distance before applying speed?\", choices: [\"To increase diagonal speed\", \"To create a unit direction\", \"To remove the target\"], correctChoice: 1, feedback: \"Normalising separates direction from length, so the movement speed stays consistent in every direction.\" },\n predict: { question: \"With a 3.6-pixel step and a target two pixels away, how far should the interceptor move?\", choices: [\"3.6 pixels past the target\", \"Zero pixels forever\", \"Two pixels and arrive\"], correctChoice: 2, feedback: \"The step is clamped to the remaining distance, allowing exact arrival without overshooting.\" },\n explain: { question: \"What must happen before normalising a zero-distance vector?\", choices: [\"Handle arrival without division\", \"Divide by zero anyway\", \"Add a random direction\"], correctChoice: 0, feedback: \"The projectile is already at its target, so arrival can be handled directly without invalid arithmetic.\" },\n },\n },\n {\n title: \"A shield with a lifetime\", concepts: [\"Time\", \"Collision radius\", \"Unique events\"],\n goals: [\"Grow and expire shields using simulated age rather than frame count.\", \"Destroy a meteor within a live shield once and award one corresponding point.\"],\n extension: \"Compare a small fast-growing shield with a larger slow-growing one in separate saves. Explain how the timing changes which meteor paths it can protect.\",\n activities: {\n learn: [\"A shield stores its age in seconds. Each tick increases age by dt and sets radius to min(36, age×60). Remove it at age 1.2. A meteor is intercepted when the distance from its centre to a live shield is no greater than the radius.\", \"A growing ring has both a position and a time-dependent reach. Being near where a shield used to exist is not a hit.\"],\n predict: [\"Predict a shield's radius at ages 0.2 and 0.8 seconds, then decide whether it still exists at age 1.2. Consider a meteor covered by two live shields on the same tick: how many points should it give?\", \"The radius stops growing at 36, and one meteor is still one destruction even if two shields overlap it.\"],\n build: [\"Update shield ages and radii on playing ticks, then discard expired shields. For each meteor, decide whether any live shield contains it. Remove intercepted meteors and add one point for each distinct removed id.\", \"Use one decision per meteor, such as some over the shield array. Adding points inside a nested hit loop can double-count overlapping shields.\"],\n run: [\"Create a shield on a meteor's path and step through growth, contact and expiry. Try two overlapping shields, then observe a meteor reaching the same point after they expire. Compare the score with the destroyed ids.\", \"Use the object's age and radius readouts to distinguish a late launch from a broken distance calculation.\"],\n assess: [\"Check radius before and after the growth cap, expiry at the lifetime boundary, a just-inside and just-outside meteor and overlapping shields. Each destroyed meteor must disappear and score exactly once.\", \"Test equality at the radius boundary as well as an obvious centre hit. Small edge cases define the collision rule.\"],\n inspect: [\"If score doubles, inspect whether each shield awards points independently for one meteor. If old shields keep defending, inspect the age filter. If growth depends on animation speed, inspect use of dt instead of tick count.\", \"Keep a list of intercepted ids for the current tick while reasoning; it should contain no duplicates.\"],\n fix: [\"Repair the lifetime or hit decision, then rerun overlap and expiry cases. Ensure expired shields are removed before collision checks and that stationary shields do not accidentally inherit an interceptor's movement.\", \"Separate updating the shield collection from filtering the meteor collection so each has a clear responsibility.\"],\n explain: [\"Choose why a meteor should be assessed against any shield and counted once, rather than awarding a point for every shield that contains it. Connect your answer to stable object identity.\", \"The score represents destroyed meteors, not the number of geometric overlaps observed by the program.\"],\n reward: [\"Save Timed shields. Your project now combines moving objects, time-limited areas and one-time collision events. Next the incoming meteors will threaten station health in organised waves.\", \"Keep your overlap scenario for regression testing when several objects are active together.\"],\n },\n questions: {\n learn: { question: \"Which clock should determine a shield's lifetime?\", choices: [\"Display redraw count\", \"The computer's current date\", \"Accumulated simulated dt\"], correctChoice: 2, feedback: \"Adding supplied simulated seconds makes growth and expiry reproducible while pausing remains predictable.\" },\n predict: { question: \"What radius does the shield have at age 0.8 seconds?\", choices: [\"36\", \"48\", \"0.8\"], correctChoice: 0, feedback: \"Age × 60 would be 48, but the documented maximum caps the radius at 36 pixels.\" },\n explain: { question: \"One meteor is covered by two shields in the same tick; what score change is correct?\", choices: [\"Two points\", \"One point\", \"One point per later frame\"], correctChoice: 1, feedback: \"The meteor is destroyed once. Multiple shields confirming the same hit must not duplicate the reward.\" },\n },\n },\n {\n title: \"The incoming waves\", concepts: [\"Event order\", \"Collections\", \"Win and loss conditions\"],\n goals: [\"Move unique seeded meteors toward their named stations and apply each impact once.\", \"Resolve shield hits before impacts and finish the campaign only when its documented conditions hold.\"],\n extension: \"Design a wave aimed at different stations rather than increasing only speed. Explain how target distribution changes the player's decisions.\",\n activities: {\n learn: [\"The host supplies a reproducible wave through input.spawns. Meteors follow their named station using the vector movement you learned. In each tick update movement and live shields, remove intercepted meteors, then apply surviving impacts. Clamp station health at zero.\", \"Event order resolves a meteor that enters both a shield and a station on the same tick: the shield gets the chance to protect it first.\"],\n predict: [\"A meteor reaches a station on a tick when a live shield also covers it. Predict the health and score changes under the documented event order. Then consider an empty meteor array before the wave has finished spawning.\", \"Empty now does not mean the wave is complete. The host's waveComplete signal tells you whether more arrivals remain.\"],\n build: [\"Accept only ids absent from seenMeteorIds, record them and move meteors toward valid target stations. Remove arrivals after one health loss. Reset seen ids only when the supplied wave advances. Lose when all health is zero; win after completed wave three has no meteors and at least one station remains.\", \"Do not end a wave solely from array length. Check wave number and waveComplete together with the remaining threats.\"],\n run: [\"Play a wave while deliberately leaving one meteor undefended. Pause at impact and confirm a single health decrement. Restart the same seed to defend it. Compare simultaneous shield contact and impact using the text event trace.\", \"A meteor left in the array after impact can damage the station every subsequent tick. Watch that id disappear.\"],\n assess: [\"Check duplicate spawns, station targeting, a single impact, simultaneous interception, all-stations-lost and final-wave completion. Intermediate empty intervals must leave a playing campaign active.\", \"Terminal state checks use several fields together. A score threshold is not a substitute for surviving the actual waves.\"],\n inspect: [\"If stations lose health repeatedly, inspect impact removal. If wins arrive early, inspect the waveComplete condition. If a covered station is still hit, inspect the order in which interception and impact are resolved.\", \"Trace one meteor id from spawn through movement to exactly one final event: intercepted or impacted.\"],\n fix: [\"Repair the first wrong event and replay the same seeded wave. Keep health within zero to three, ignore invalid target ids safely and preserve a terminal result when later ticks arrive. Recheck shield double-counting.\", \"A destroyed station remains a valid named target, but further impacts cannot make its health negative.\"],\n explain: [\"Choose why simulation order is part of the game's rules. Explain why an empty array and an explicit no-more-spawns signal answer different questions.\", \"One describes current state; the other describes whether future events are still scheduled.\"],\n reward: [\"Save Wave defence. You have a complete threat lifecycle and meaningful win and loss conditions. The next mission adds limited resources so that choosing when and where to act matters.\", \"Retain a wave with a simultaneous interception and impact as an edge-case save.\"],\n },\n questions: {\n learn: { question: \"Which event is resolved first when shield contact and station impact coincide?\", choices: [\"Shield interception\", \"Station damage twice\", \"A random choice\"], correctChoice: 0, feedback: \"The documented order gives live shields their interception check before surviving meteors can damage stations.\" },\n predict: { question: \"What does an empty meteor array before waveComplete mean?\", choices: [\"The whole campaign is won\", \"No threats are present now, but more may arrive\", \"Every station must be destroyed\"], correctChoice: 1, feedback: \"An empty current collection does not establish that the wave has finished supplying future meteors.\" },\n explain: { question: \"Why remove a meteor immediately after its station impact is applied?\", choices: [\"To erase all other meteors\", \"To regenerate full energy\", \"To prevent the same impact damaging the station again\"], correctChoice: 2, feedback: \"Each meteor has one terminal event. Removing it records that the impact has already been processed.\" },\n },\n },\n {\n title: \"Energy and emergency repairs\", concepts: [\"Resources\", \"Preconditions\", \"Atomic changes\"],\n goals: [\"Spend and regenerate bounded energy with no negative balances.\", \"Apply repairs only when the selected station and available energy satisfy every requirement.\"],\n extension: \"Compare saving energy for repairs with spending it on early shields. Use the same wave seed and record both surviving health and unused energy.\",\n activities: {\n learn: [\"A resource action has preconditions and effects. A launch needs a valid target and at least one energy; then it creates one interceptor and spends one. A repair needs three energy and a living damaged station; then it adds one health and spends three.\", \"Check all preconditions before changing either side. An invalid repair should not spend energy without healing anything.\"],\n predict: [\"You have 2.9 energy and a station at health two. Predict a repair attempt, then a launch. Next consider a fully healthy station with energy ten. Decide whether a repair should be allowed to spend resources there.\", \"Fractional regeneration is real state: 2.9 is still less than the three required for repair.\"],\n build: [\"Charge valid launches one energy. Add repair handling for a selected baseId, requiring health greater than zero and less than three plus energy at least three. Regenerate 0.25×dt while playing, capped at ten; rejected actions must preserve both resources and objects.\", \"Do not revive a destroyed station in this game's rules. Validate the selected id before reading its health.\"],\n run: [\"Launch until energy is low, attempt one more launch and inspect whether both energy and projectile count stay unchanged. Wait in simulated time, repair a damaged station, then try repairing it again at full health.\", \"Pause should also pause regeneration because no playing ticks are being supplied.\"],\n assess: [\"Check energy exactly at and just below costs, regeneration at the cap, repair of damaged, full, destroyed and missing stations, and actions after a terminal result. Failed preconditions must leave the entire action unapplied.\", \"A test may compare both health and energy. Getting one right while the other changes incorrectly is still a broken transaction.\"],\n inspect: [\"If energy disappears on rejected actions, find a subtraction before validation. If energy becomes negative, inspect the comparison at the cost boundary. If repairs exceed three health, inspect the station's precondition and cap.\", \"Write a before-and-after table for one action: energy, health and new object count. Every effect should agree with one valid decision.\"],\n fix: [\"Group each action's validation before its state changes, then repeat the boundary cases. Recheck an ordinary successful launch and repair as well as the rejected actions, and verify regeneration does not run while ready or over.\", \"A safety fix that rejects every action would stop invalid spending but also make the game unusable. Preserve the valid path.\"],\n explain: [\"Choose why the resource check and its effects belong to one coherent action. Explain the difference between a rejected action and a partially applied one using your repair trace.\", \"The player should either receive the documented effect at its cost or keep the previous state.\"],\n reward: [\"Save Resourceful defence. Your game now has meaningful limited choices and consistent resource rules. Next you will combine the whole defence campaign and test it through to a final result.\", \"Keep a low-energy save to revisit fractional boundaries when experimenting with recharge rates.\"],\n },\n questions: {\n learn: { question: \"When should a repair spend its three energy?\", choices: [\"Before checking the station\", \"After every precondition succeeds\", \"Even if the station id is unknown\"], correctChoice: 1, feedback: \"Validate the station and balance first, then apply the health increase and resource cost together.\" },\n predict: { question: \"With 2.9 energy, what should an attempted three-energy repair do?\", choices: [\"Spend 2.9 and repair anyway\", \"Make energy negative\", \"Leave health and energy unchanged\"], correctChoice: 2, feedback: \"The balance is below the full cost, so the action is rejected without partial effects.\" },\n explain: { question: \"What is wrong with charging for an invalid repair and then returning?\", choices: [\"It applies only the cost without the promised effect\", \"It creates a new station\", \"It makes regeneration too precise\"], correctChoice: 0, feedback: \"A partially applied action breaks the resource contract: the player loses energy while receiving no valid repair.\" },\n },\n },\n {\n title: \"The last station\", concepts: [\"Integration\", \"Strategy\", \"Regression evidence\"],\n goals: [\"Run a complete three-wave defence with consistent movement, shields, impacts and resources.\", \"Keep controls usable and restart cleanly after either victory or defeat.\"],\n extension: \"Create an alternative difficulty profile in a separate save. Change one parameter, state its intended effect and rerun movement, impact and resource boundaries before comparing strategies.\",\n activities: {\n learn: [\"The final defence depends on many small contracts working together: targets are valid, movement is bounded, shields expire, impacts happen once and resources are conserved. Combine these into a campaign without replacing real outcomes with a manually assigned score or win state.\", \"A good strategy and a correct simulation are different things. The evaluator checks the rules even when a player loses a difficult attempt.\"],\n predict: [\"Predict what should happen when the final meteor of wave three is intercepted with one living station, and when it impacts the last health point instead. Include status, score, remaining meteors and whether another launch is permitted.\", \"Resolve the meteor's terminal event first, then decide victory or defeat from the resulting world state.\"],\n build: [\"Organise the completed update into clear event and tick paths. Preserve the documented resolution order and use small helpers for movement or collision when useful. Ensure both won and over states remain stable until restart.\", \"Keep helpers focused: moving a projectile should not unexpectedly charge energy or alter station health.\"],\n run: [\"Play all three waves with keyboard targeting and then pointer or touch aiming. Pause to inspect a crowded scene, test repair selection from the station list and restart after both final outcomes. Use the text state if animation is difficult to follow.\", \"The same launch and repair actions should reach your project regardless of which accessible control produced them.\"],\n assess: [\"Run the final saved-project assessment across seeded waves and boundary scenarios. It checks direction, arrival, lifetime, duplicate events, simultaneous contacts, terminal states and valid or rejected resource actions together.\", \"Read a failed rule before changing your strategy. A rule failure must be fixed in the project rather than hidden by selecting an easier wave.\"],\n inspect: [\"Trace the earliest incorrect event in a failing campaign and link it to its teaching mission. Separate a missed defensive shot from a double impact or invalid energy change. Find which invariant first stopped being true.\", \"A late defeat may be correct gameplay; a station losing two health to one meteor is an implementation defect.\"],\n fix: [\"Repair the failing rule, replay its focused case and rerun previously passing wave and resource checks. Save the new source before assessing it, then confirm the final result refers to that exact project version.\", \"Do not hard-code the final status for a known seed. The same rules must work with different supplied meteor paths.\"],\n explain: [\"Choose what makes the completed defence trustworthy, then identify a strategy trade-off between immediate interception and reserving energy for repairs. Use a trace to support the difference between rules and player decisions.\", \"Explain which conditions your tests cover and keep future improvements separate from what the current project actually does.\"],\n reward: [\"Save Last station and complete the final assessment. You have created a defence game with moving threats, timed shields, explicit outcomes and fair resource accounting. Replay any mission or keep a separate experimental difficulty save.\", \"Your account keeps earned completion while you experiment; preserve a stable named version before a larger redesign.\"],\n },\n questions: {\n learn: { question: \"What should determine the final outcome of the defence campaign?\", choices: [\"A hard-coded success after enough redraws\", \"The name of the save slot\", \"The actual remaining threats, completed wave and station health\"], correctChoice: 2, feedback: \"Victory and defeat must follow the simulated world and documented rules, not a display counter or chosen label.\" },\n predict: { question: \"The final meteor is intercepted after wave three completes and one station survives; what follows?\", choices: [\"A won state with the meteor removed\", \"An automatic station impact as well\", \"Unlimited new launches after the result\"], correctChoice: 0, feedback: \"Interception removes the last threat. With the final wave complete and a station alive, the campaign ends in a stable won state.\" },\n explain: { question: \"Which observation is an implementation defect rather than simply a difficult strategy?\", choices: [\"A player aims too late\", \"One meteor damages the same station on several later ticks\", \"A player reserves energy instead of firing\"], correctChoice: 1, feedback: \"Repeated damage from one already processed impact violates the one-terminal-event rule regardless of the player's strategy.\" },\n },\n },\n]);\n"],"mappings":"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","names":[]}