πŸ‘Ύ 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

  1. Control Structures β€” Iteration
  2. Control Structures β€” Conditionals
  3. Control Structures β€” Nested Conditions
  4. Data Types β€” Numbers
  5. Data Types β€” Strings
  6. Data Types β€” Booleans
  7. Data Types β€” Arrays
  8. Data Types β€” Objects (JSON)
  9. Operators β€” Mathematical
  10. Operators β€” String Operations
  11. Operators β€” Boolean Expressions
  12. 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 just Math.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.