Space Game Lvl 3
πΎ Level 3 Blog: Alien Chase β CS111 Concepts in Action
Level 3 was the most complex level we built. It introduced autonomous AI behavior, a countdown survival timer, rage mode acceleration, and a full caught/win/restart overlay system. This is where the CS111 fundamentals β loops, conditionals, objects, math β all converged into something that felt genuinely intelligent. This blog traces every requirement.
π Table of Contents
- Control Structures β Iteration
- Control Structures β Conditionals
- Control Structures β Nested Conditions
- Data Types β Numbers
- Data Types β Strings
- Data Types β Booleans
- Data Types β Arrays
- Data Types β Objects (JSON)
- Operators β Mathematical
- Operators β String Operations
- Operators β Boolean Expressions
- Reflection
π Control Structures β Iteration
Where we learned it: Snake Game (Trimester 1)
// Snake game β Trimester 1
// The entire game was an iteration β setInterval driving the game loop
const gameLoop = setInterval(() => {
update();
draw();
}, 1000 / FPS);
How we used it in Level 3
Level 3 has two independent setInterval loops running simultaneously β the chase AI loop and the survival countdown:
// GameLevelstuck_final.js β Chase AI loop (~30fps)
const AlienChaseAI = {
start() {
// ...
setTimeout(() => {
this.intervalId = setInterval(() => this._tick(), CHASE_CONFIG.TICK_MS); // 33ms
}, 500);
},
stop() {
if (this.intervalId !== null) {
clearInterval(this.intervalId);
this.intervalId = null;
}
}
};
// SurvivalManager β countdown loop (every 100ms)
_startCountdown() {
if (this.countdownId !== null) return;
this.countdownId = setInterval(() => {
if (this.frozen) return;
this.remaining = Math.max(0, this.remaining - 100);
this._updateHUD();
if (this.remaining <= 0) {
clearInterval(this.countdownId);
this.showWin();
}
}, 100);
}
Growth: Snake had one setInterval that did everything. Level 3 has two concurrent intervals with independent responsibilities, pause/resume controls, and proper cleanup. This is the game loop pattern fully realised.
π Control Structures β Conditionals
Where we learned it: Calculator (Trimester 1)
// Calculator β Trimester 1
if (operator === '+') { result = a + b; }
else if (operator === '-') { result = a - b; }
How we used it in Level 3
The HUD timer display uses a conditional to switch to tenths-of-a-second precision in the final 3 seconds β a UX decision expressed as a conditional:
// GameLevelstuck_final.js β conditional precision formatting
_updateHUD() {
const secs = this.remaining / 1000;
const display = secs <= 3
? `β± ${secs.toFixed(1)}s` // last 3 seconds: tenths of a second
: `β± ${Math.ceil(secs)}s`; // normal: whole seconds
el.textContent = display;
if (secs <= 3) {
el.style.color = '#ff9900'; // warning orange
el.style.textShadow = '0 0 14px #ff9900';
} else {
el.style.color = '#00ffcc'; // normal teal
el.style.textShadow = '0 0 10px #00ffcc';
}
}
The rage mode visual also uses a conditional to avoid resetting color while the timer is already in warning state:
// Only revert to teal if timer isn't in its own warning state
_setRageVisual(raging) {
if (raging) {
hud.style.color = '#ff4444';
} else if ((SurvivalManager.remaining / 1000) > 3) {
hud.style.color = '#00ffcc';
}
}
Growth: Calculator conditionals selected math operations. Level 3 conditionals control simultaneous visual states β rage mode and the countdown warning can both want to color the HUD, and the conditional arbitrates between them.
πͺ Control Structures β Nested Conditions
Where we learned it: Snake Game (Trimester 1)
// Snake game β Trimester 1
if (gameRunning) {
if (headX === food.x && headY === food.y) {
score++;
spawnFood();
}
}
How we used it in Level 3
The chase AI _tick() method is the most complex nested condition structure in any of the three levels. It has layered stillness detection logic:
// GameLevelstuck_final.js β nested stillness detection in _tick()
_tick() {
if (this.paused) return; // outer guard
const playerCanvas = this._findCanvas('playerData');
const npcCanvas = this._findCanvas('Alien');
if (!playerCanvas || !npcCanvas) return; // safety guard
if (this.lastPlayerPos !== null) { // level 1: have a previous position
if (moved < CHASE_CONFIG.STILL_RADIUS) { // level 2: player barely moved
if (this.stillSince === null) this.stillSince = now; // start timer
if ((now - this.stillSince) >= CHASE_CONFIG.STILL_THRESHOLD_MS) { // level 3: still long enough?
if (this.currentSpeed !== CHASE_CONFIG.RAGE_SPEED) { // level 4: not already raging?
this.currentSpeed = CHASE_CONFIG.RAGE_SPEED;
this._setRageVisual(true);
}
}
} else { // player moved
if (this.stillSince !== null) { // was raging β reset
this.stillSince = null;
this.currentSpeed = CHASE_CONFIG.BASE_SPEED;
this._setRageVisual(false);
}
}
}
}
Growth: Snake nested conditions were 2β3 levels deep. Level 3βs chase AI reaches 4 levels of nesting to implement a state machine β paused β canvas found β position compared β stillness timed β rage triggered.
π’ Data Types β Numbers
Where we learned it: Snake Game (Trimester 1)
// Snake game β Trimester 1
const CELL_SIZE = 20;
let score = 0;
score += 10;
How we used it in Level 3
Level 3 has the most numeric tuning constants of any level β organised in dedicated configuration objects:
// GameLevelstuck_final.js β numeric tuning constants
const INTERACTION_CONFIG = {
SURVIVE_MS: 20000, // 20 seconds in milliseconds
CATCH_COOLDOWN_MS: 1200 // grace period after restart
};
const CHASE_CONFIG = {
TICK_MS: 33, // ~30fps
BASE_SPEED: 1.2, // pixels per tick β normal
RAGE_SPEED: 3.8, // pixels per tick β rage
STILL_THRESHOLD_MS: 3000, // ms before rage triggers
STILL_RADIUS: 4, // px β threshold for 'not moving'
STOP_RADIUS: 18, // px β alien stops this close to player
};
// Numbers accumulating in the chase offset
this.offsetX += normX * this.currentSpeed;
this.offsetY += normY * this.currentSpeed;
npcCanvas.style.transform = `translate(${this.offsetX}px, ${this.offsetY}px)`;
Growth: Snake numbers were constants for grid size. Level 3 numbers are behavioral tuning parameters β changing RAGE_SPEED from 3.8 to 5.0 makes the level harder. Numbers define the difficulty curve.
π€ Data Types β Strings
Where we learned it: Snake Game (Trimester 1)
// Snake game β Trimester 1
statusEl.textContent = 'Game Over!';
How we used it in Level 3
Strings serve as both CSS values and DOM identifiers, managed entirely through constants:
// GameLevelstuck_final.js β string constants as IDs
const INTERACTION_CONFIG = {
HUD_ELEMENT_ID: 'alien-planet-hud',
CAUGHT_OVERLAY_ID: 'alien-planet-caught',
WIN_OVERLAY_ID: 'alien-planet-win',
};
// Used consistently for lookup and creation
let hud = document.getElementById(INTERACTION_CONFIG.HUD_ELEMENT_ID);
if (!hud) {
hud = document.createElement('div');
hud.id = INTERACTION_CONFIG.HUD_ELEMENT_ID;
}
// NPC dialogue strings carry the game's narrative
dialogues: [
"Hah! Got you! You can't advance to the next level. Press E to restart.",
"Every second you stay near me, you lose more score!",
"Your energy is draining... can you escape in time?"
],
// CSS transform string updated every 33ms
npcCanvas.style.transform = `translate(${this.offsetX}px, ${this.offsetY}px)`;
Growth: Snake strings were one-off display messages. Level 3 uses strings as configuration keys stored in constants, ensuring the same string is used everywhere an element is created or looked up β eliminating typo bugs.
β Data Types β Booleans
Where we learned it: Snake Game (Trimester 1)
// Snake game β Trimester 1
let gameRunning = true;
let isPaused = false;
How we used it in Level 3
Level 3 has the most boolean state of any level β spread across both AlienChaseAI and SurvivalManager:
// GameLevelstuck_final.js β multiple boolean flags across systems
const AlienChaseAI = {
paused: false, // is the chase loop frozen?
// ...
pause() { this.paused = true; },
resume() { /* ... */ this.paused = false; }
};
const SurvivalManager = {
frozen: false, // is the timer/game frozen (caught or won)?
// ...
caught() {
if (this.frozen) return; // idempotency check
this.frozen = true;
AlienChaseAI.pause();
},
reset() {
this.frozen = false;
AlienChaseAI.resume();
}
};
// In _tick() β early exit on boolean check
_tick() {
if (this.paused) return;
// ...
}
Growth: Snake had two booleans. Level 3 uses booleans to synchronize two independent systems β when SurvivalManager.frozen goes true, it sets AlienChaseAI.paused true. Coordinated boolean state is the simplest form of inter-system communication.
π¦ Data Types β Arrays
Where we learned it: Snake Game (Trimester 1)
// Snake game β Trimester 1
let snake = [{ x: 5, y: 5 }, { x: 4, y: 5 }];
How we used it in Level 3
The _findCanvas method queries all canvases and iterates with for...of, and Object.assign applies an array-like set of style properties:
// GameLevelstuck_final.js β iterating over canvas elements
_findCanvas(id) {
let el = document.getElementById(id);
if (el) return el;
const all = document.querySelectorAll('canvas'); // NodeList (array-like)
for (const c of all) {
if (c.id === id) return c;
}
return null;
}
// Object.assign applies an object of properties β treats them as an array-like set
Object.assign(hud.style, {
position: 'fixed', top: '16px', right: '24px',
color: '#00ffcc', fontSize: '26px', fontWeight: 'bold',
// ...
});
// NPC dialogue array
dialogues: [
"Hah! Got you! Press E to restart.",
"Every second you stay near me, you lose more score!",
"Your energy is draining... can you escape in time?"
],
Growth: Snake iterated arrays to render. Level 3 iterates DOM NodeLists β the same for...of pattern now works on browser APIs, not just JavaScript arrays, showing the generality of iteration.
ποΈ Data Types β Objects (JSON)
Where we learned it: Calculator (Trimester 1)
// Calculator β Trimester 1
const state = { currentValue: '', pendingOp: null };
How we used it in Level 3
Level 3 introduces the namespace pattern β using object literals as modules with state and methods:
// GameLevelstuck_final.js β objects as full modules (namespace pattern)
const AlienChaseAI = {
// === STATE ===
intervalId: null,
paused: false,
offsetX: 0,
offsetY: 0,
currentSpeed: CHASE_CONFIG.BASE_SPEED,
lastPlayerPos: null,
stillSince: null,
// === METHODS ===
start() { /* ... */ },
stop() { /* ... */ },
pause() { this.paused = true; },
resume() { /* ... */ },
_findCanvas(id) { /* ... */ },
_setRageVisual(raging) { /* ... */ },
_tick() { /* ... */ } // 60+ lines of chase AI logic
};
Growth: Calculator had a flat state object. Level 3 uses object literals as self-contained modules with internal state, public methods, and private helpers β essentially a class without the class keyword. This is the bridge between raw objects and OOP.
β Operators β Mathematical
Where we learned it: Calculator (Trimester 1)
// Calculator β Trimester 1
result = Math.sqrt(parseFloat(input));
result = Math.pow(a, b);
How we used it in Level 3
The chase AI uses 2D vector math every tick β the Pythagorean theorem to compute distance, and normalization to get a unit direction vector:
// GameLevelstuck_final.js β vector math in _tick()
// Euclidean distance (Pythagorean theorem)
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < CHASE_CONFIG.STOP_RADIUS) return; // too close β stop
// Normalize to unit vector (divide by magnitude)
const normX = dx / dist;
const normY = dy / dist;
// Scale by speed and accumulate
this.offsetX += normX * this.currentSpeed;
this.offsetY += normY * this.currentSpeed;
// Stillness detection β distance moved since last tick
const moved = Math.sqrt(
Math.pow(px - this.lastPlayerPos.x, 2) +
Math.pow(py - this.lastPlayerPos.y, 2)
);
// Countdown tick with Math.max to avoid going below 0
this.remaining = Math.max(0, this.remaining - 100);
Growth: Calculator used Math.sqrt and Math.pow for user-requested calculations. Level 3 uses them to implement vector physics β the math is the AI. The Pythagorean theorem makes the alien chase you.
π Operators β String Operations
Where we learned it: Snake Game (Trimester 1)
// Snake game β Trimester 1
el.textContent = 'Score: ' + score;
How we used it in Level 3
String operations update the HUD every 100ms and apply the CSS transform every 33ms:
// GameLevelstuck_final.js β template literals for HUD and motion
// HUD text β ternary + toFixed() string method
const display = secs <= 3
? `β± ${secs.toFixed(1)}s` // 'β± 2.4s'
: `β± ${Math.ceil(secs)}s`; // 'β± 15s'
// CSS transform string β updated every tick
npcCanvas.style.transform = `translate(${this.offsetX}px, ${this.offsetY}px)`;
// Caught overlay innerHTML with embedded HTML and string interpolation
el.innerHTML = 'β CAUGHT!<br>' +
'<span style="font-size:20px;color:#fff;">Press E near the Alien to restart</span>';
// Object.assign with CSS property strings
Object.assign(hud.style, {
color: '#00ffcc',
textShadow: '0 0 10px #00ffcc',
border: '1px solid #00ffcc55'
});
Growth: Snake had static strings. Level 3 uses live string generation β template literals running inside a 33ms interval, generating a new CSS transform string 30 times per second to move the alien.
β‘ Operators β Boolean Expressions
Where we learned it: Snake Game (Trimester 1)
// Snake game β Trimester 1
if (newDir === 'LEFT' && direction !== 'RIGHT') {
direction = newDir; // can't reverse directly into yourself
}
How we used it in Level 3
The catch cooldown guard uses a compound boolean combining null check, &&, and a time comparison β all in one condition:
// GameLevelstuck_final.js β compound boolean guards
// Cooldown: prevent re-catch immediately after restart
caught() {
if (this.frozen) return; // simple boolean guard
// Compound: lastResetTime exists AND cooldown hasn't expired
if (this.lastResetTime !== null &&
Date.now() - this.lastResetTime < INTERACTION_CONFIG.CATCH_COOLDOWN_MS) return;
this.frozen = true;
// ...
}
// Chase tick β stop condition using < operator
if (dist < CHASE_CONFIG.STOP_RADIUS) return;
// Rage trigger β comparison of elapsed stillness time
if ((now - this.stillSince) >= CHASE_CONFIG.STILL_THRESHOLD_MS) {
if (this.currentSpeed !== CHASE_CONFIG.RAGE_SPEED) {
this.currentSpeed = CHASE_CONFIG.RAGE_SPEED;
}
}
Growth: Snake used && to prevent direction reversal (one concern). Level 3 uses compound boolean expressions to implement game-design rules β the cooldown prevents frustrating instant re-catches, and the stillness threshold rewards active play. Boolean logic became game design.
π Reflection
Level 3 is where everything came together. The alien isnβt random β it has a behavioral state machine powered by CS111 fundamentals: iteration drives the loop, math moves the sprite, booleans track state, conditionals switch between modes, and objects encapsulate it all.
| CS111 Concept | Trimester 1 Form | Level 3 Form | Growth |
|---|---|---|---|
| Iteration | Single game loop setInterval |
Two concurrent setInterval loops with pause/resume |
Concurrent, controllable iteration |
| Conditionals | Route user input | Arbitrate competing visual states (rage vs countdown) | State-arbitration conditionals |
| Nested Conditions | 2β3 level depth | 4-level stillness state machine in _tick() |
Full AI state machine |
| Numbers | Grid size / score | Behavioral tuning constants (speed, radius, timing) | Numbers as game design |
| Strings | Display messages | Constant-keyed DOM IDs + live CSS transform strings | Strings as infrastructure |
| Booleans | Game running flags | Cross-system synchronization (frozen β paused) |
Booleans as inter-system signals |
| Arrays | Snake body | NodeList iteration with for...of |
Arrays β browser APIs |
| Objects | Flat state objects | Full namespace modules with state + methods | Objects as modules |
| Math Operators | User calculations | 2D vector math: distance, normalization, accumulation | Math as physics engine |
| String Ops | Static concatenation | Live template literals in 33ms render loop | Strings as animated output |
| Boolean Expressions | Prevent direction reversal | Cooldown guard + rage threshold + stop radius | Boolean expressions as game rules |
The Through-Line
Looking across all three levels:
- Level 1 taught us that data structures do work β configuration objects replace imperative setup code.
- Level 2 taught us to program defensively β assume everything might be null, guard every access.
- Level 3 taught us that algorithms are CS111 β the Pythagorean theorem in
_tick()is justMath.sqrt(dx*dx + dy*dy), which is exactly the kind of math we first used in the calculator. The difference is that now it moves a monster toward you at 30 frames per second.
Every concept we learned in Trimester 1 showed up here. We didnβt leave them behind β we leveled them up.