Space Game Lvl 2
π§© Level 2 Blog: Alien Maze β CS111 Concepts in Action
Level 2 was a major complexity jump. The maze introduced invisible barriers, a DOM-based glow effect, a run timer, and a victory screen. This blog walks through every CS111 requirement and shows how the concept graduated from Trimester 1 into this level.
π 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
for (let i = 1; i < snake.length; i++) {
if (snake[0].x === snake[i].x && snake[0].y === snake[i].y) {
gameOver = true;
}
}
How we used it in Level 2
The _findPlayer static method iterates over multiple possible object stores to locate the player, using a for...of loop with an array of candidate sources:
// GameLevel2.js β _findPlayer iterates over object stores
static _findPlayer(gameEnv) {
const sources = [
gameEnv?.gameObjects,
gameEnv?.objects,
gameEnv?.gameControl?.gameObjects,
].filter(Boolean); // removes undefined/null entries
for (const list of sources) {
const arr = Array.isArray(list) ? list : Object.values(list);
const found = arr.find(o =>
o?.data?.id === 'playerData' || o?.id === 'playerData'
);
if (found) return found;
}
return null;
}
Growth: Snake used for to check self-collision. Level 2 uses for...of with .filter() and .find() to search a dynamic, heterogeneous set of object stores β iterating over uncertainty rather than a known array.
π Control Structures β Conditionals
Where we learned it: Calculator (Trimester 1)
// Calculator β Trimester 1
if (input === '=') {
displayResult();
} else if (input === 'C') {
clearDisplay();
} else {
appendToDisplay(input);
}
How we used it in Level 2
The _glowBarrier helper uses an early-return conditional to prevent duplicate DOM glow elements β a common defensive pattern in game loops:
// GameLevel2.js β early-exit conditional in _glowBarrier
function _glowBarrier(barrierInstance) {
const glowId = 'barrier-glow-' + barrierInstance.data.id;
if (document.getElementById(glowId)) return; // guard: already glowing
// ... create and append the glow div
setTimeout(() => { glow.style.opacity = '0'; }, 300);
setTimeout(() => { glow.remove(); }, 820);
}
Also, the _showVictoryScreen uses a conditional to format the elapsed time string:
// GameLevel2.js β conditional for time display
const timeStr = elapsedSeconds !== null
? `${Math.floor(elapsedSeconds / 60)}:${String(elapsedSeconds % 60).padStart(2, '0')}`
: 'β';
Growth: Calculator conditionals handled user intent. Level 2 conditionals enforce game-state integrity β preventing duplicate DOM elements and handling null timer states gracefully.
πͺ Control Structures β Nested Conditions
Where we learned it: Snake Game (Trimester 1)
// Snake game β Trimester 1
if (gameRunning) {
if (direction === 'RIGHT') {
if (headX + 1 >= COLS) {
gameRunning = false;
} else {
headX++;
}
}
}
How we used it in Level 2
The onCollide barrier handler nests player-finding logic, position reset, and velocity zeroing inside a found-player check:
// GameLevel2.js β nested conditions in barrier onCollide
onCollide: function () {
_glowBarrier(this);
const player = GameLevel2._findPlayer(gameEnv);
if (player) { // outer: player exists?
const init = player.data?.INIT_POSITION ?? { x: 100, y: 300 };
player.x = init.x;
player.y = init.y;
if (player.position) { // inner: has .position object?
player.position.x = init.x;
player.position.y = init.y;
}
if (player.velocity) { // inner: has .velocity object?
player.velocity.x = 0;
player.velocity.y = 0;
}
}
GameLevel2._showRestartFlash();
}
Growth: Snake nested conditions checked game state and then direction. Level 2 nests to handle optional properties on a live game object whose exact shape depends on which engine version is running.
π’ Data Types β Numbers
Where we learned it: Snake Game (Trimester 1)
// Snake game β Trimester 1
const CELL_SIZE = 20;
const FPS = 8;
let score = 0;
How we used it in Level 2
Numbers are everywhere in Level 2 β from maze wall proportions (as decimals) to DOM animation timings:
// GameLevel2.js β fractional positions for proportional barrier layout
const mazeTop = makeBarrier('maze_top', 0.20, 0.15, 0.60, 0.02);
const mazeWall1 = makeBarrier('maze_wall_1', 0.30, 0.25, 0.02, 0.30);
const mazeWall5 = makeBarrier('maze_wall_5', 0.60, 0.25, 0.02, 0.35);
// Numbers used for DOM animation timing (ms)
setTimeout(() => { glow.style.opacity = '0'; }, 300);
setTimeout(() => { glow.remove(); }, 820);
// Victory screen β time computation with numbers
const elapsed = GameLevel2._startTime
? Math.floor((Date.now() - GameLevel2._startTime) / 1000)
: null;
Growth: Snake used whole-number grid positions. Level 2 uses fractional proportional coordinates (0.0β1.0) so the maze scales to any screen size β a key step toward resolution-independent design.
π€ Data Types β Strings
Where we learned it: Snake Game (Trimester 1)
// Snake game β Trimester 1
const direction = 'RIGHT';
if (direction === 'UP') { headY--; }
How we used it in Level 2
Strings carry the entire popup UI β HTML template literals construct rich multi-element DOM popups:
// GameLevel2.js β string as full HTML template (startup popup)
overlay.innerHTML = `
<div style="background:linear-gradient(145deg,#0d1b2a,#1b2d45); ...">
<div style="font-size:48px;margin-bottom:12px;">π€</div>
<h2 style="font-size:1.6rem;color:#4fc3f7;">ALIEN MAZE</h2>
<p>Level 2 Briefing</p>
<p>Touch a hidden wall and it will
<strong style="color:#f48fb1;">glow red β then you restart.</strong>
</p>
<button id="maze-start-btn">START MISSION</button>
</div>
`;
Strings also serve as unique DOM element IDs for safe lookup:
const glowId = 'barrier-glow-' + barrierInstance.data.id;
if (document.getElementById(glowId)) return;
Growth: Snake strings were direction labels. Level 2 strings are full UI documents β template literal HTML is a string operation that builds entire interface elements at runtime.
β Data Types β Booleans
Where we learned it: Snake Game (Trimester 1)
// Snake game β Trimester 1
let gameRunning = true;
if (!gameRunning) return;
How we used it in Level 2
Level 2 uses a static boolean _startTime flag (null = not started, number = started) and a transition guard boolean:
// GameLevel2.js β static timer flag
GameLevel2._startTime = null; // null = timer not running; Date.now() = running
// In the victory screen handler β transition guard
setTimeout(() => {
if (this.gameEnv && this.gameEnv.gameControl &&
!this.gameEnv.gameLevelTransitionTriggered) { // boolean NOT guard
this.gameEnv.gameLevelTransitionTriggered = true;
this.gameEnv.gameControl.currentLevel.continue = false;
}
}, 4000);
// barrier visible flag
const mazeTop = makeBarrier(...);
// { visible: false, ... } β boolean hides barrier visually while keeping collision
Growth: Snake used one gameRunning boolean. Level 2 uses booleans for idempotency β the !gameLevelTransitionTriggered guard prevents the level-transition from firing twice even if the handler runs multiple times.
π¦ Data Types β Arrays
Where we learned it: Snake Game (Trimester 1)
// Snake game β Trimester 1
let snake = [{ x: 5, y: 5 }];
snake.unshift(newHead);
if (!ateFood) snake.pop();
How we used it in Level 2
The _findPlayer method builds a temporary array of candidate object stores and filters it:
// GameLevel2.js β array of object stores, filtered and searched
const sources = [
gameEnv?.gameObjects,
gameEnv?.objects,
gameEnv?.gameControl?.gameObjects,
].filter(Boolean); // removes any undefined entries
for (const list of sources) {
const arr = Array.isArray(list) ? list : Object.values(list);
const found = arr.find(o => o?.data?.id === 'playerData' || o?.id === 'playerData');
if (found) return found;
}
The NPC dialogue and the full class registry also use arrays:
dialogues: ['You made it through the maze! Ready for the next level?'],
this.classes = [
{ class: GameEnvBackground, data: bgData },
{ class: Player, data: playerData },
// ... 11 total entries
];
Growth: Snake arrays were mutable data structures we modified each frame. Level 2 uses arrays as search spaces β .filter() and .find() replace manual for-loop searching.
ποΈ Data Types β Objects (JSON)
Where we learned it: Calculator (Trimester 1)
// Calculator β Trimester 1
const state = { currentInput: '', operator: '', result: 0 };
How we used it in Level 2
The makeBarrier factory function returns a fresh barrier object β a factory pattern β making object creation reusable:
// GameLevel2.js β factory function returns configuration objects
const makeBarrier = (id, x, y, w, h) => ({
id,
x, y,
width: w,
height: h,
visible: false,
hitbox: { widthPercentage: 0.0, heightPercentage: 0.0 },
onCollide: function () {
_glowBarrier(this);
const player = GameLevel2._findPlayer(gameEnv);
// ... reset player position
GameLevel2._showRestartFlash();
}
});
// All 10 barriers created from the same factory
const mazeTop = makeBarrier('maze_top', 0.20, 0.15, 0.60, 0.02);
const mazeWall1 = makeBarrier('maze_wall_1', 0.30, 0.25, 0.02, 0.30);
Growth: Calculator objects stored UI state. Level 2 objects include methods as properties (onCollide) β functions are values in JavaScript, and embedding them in objects is the foundation of OOP.
β Operators β Mathematical
Where we learned it: Calculator (Trimester 1)
// Calculator β Trimester 1
result = parseFloat(a) + parseFloat(b);
result = parseFloat(a) % parseFloat(b); // modulo
How we used it in Level 2
Math operators compute the elapsed time display β division, modulo, and Math.floor working together:
// GameLevel2.js β elapsed time formatting with math operators
const elapsed = Math.floor((Date.now() - GameLevel2._startTime) / 1000);
// Inside _showVictoryScreen:
const timeStr = `${Math.floor(elapsedSeconds / 60)}:${String(elapsedSeconds % 60).padStart(2, '0')}`;
// β minutes (division) β seconds (modulo)
The glow effect uses DOM coordinate math:
// GameLevel2.js β math operators for DOM overlay positioning
const bx = barrierInstance.x ?? 0;
const by = barrierInstance.y ?? 0;
const cr = canvas.getBoundingClientRect();
glow.style.left = `${cr.left + bx}px`; // addition
glow.style.top = `${cr.top + by}px`; // addition
Growth: Calculator math was the product being computed. Level 2 math is infrastructure β converting milliseconds to M:SS format and mapping canvas coordinates to screen coordinates.
π Operators β String Operations
Where we learned it: Snake Game (Trimester 1)
// Snake game β Trimester 1
document.title = 'Snake β Score: ' + score;
How we used it in Level 2
Template literals are the dominant string pattern β used for CSS strings, HTML content, and the formatted timer:
// GameLevel2.js β template literals throughout
// CSS positioning via template literals
glow.style.cssText = `
position: fixed;
left: ${cr.left + bx}px;
top: ${cr.top + by}px;
width: ${bw}px;
height: ${bh}px;
border: 3px solid rgba(255, 80, 80, 1);
box-shadow: 0 0 18px 6px rgba(255, 60, 60, 0.8);
`;
// String method: padStart for zero-padded seconds
String(elapsedSeconds % 60).padStart(2, '0')
// Dynamic DOM ID via concatenation
const glowId = 'barrier-glow-' + barrierInstance.data.id;
Growth: Snake used simple + concatenation. Level 2 uses template literals with expressions (${...}) and string methods like .padStart() β a full upgrade to modern JavaScript string tooling.
β‘ Operators β Boolean Expressions
Where we learned it: Snake Game (Trimester 1)
// Snake game β Trimester 1
if (headX < 0 || headX >= COLS || headY < 0 || headY >= ROWS) {
endGame();
}
How we used it in Level 2
Level 2 uses the nullish coalescing (??) and optional chaining (?.) operators alongside classic && chains:
// GameLevel2.js β modern boolean/nullish operators
// ?? (nullish coalescing): use right side if left is null/undefined
const init = player.data?.INIT_POSITION ?? { x: 100, y: 300 };
const bx = barrierInstance.x ?? 0;
const bw = barrierInstance.width ?? 20;
// ?. (optional chaining): short-circuit on null/undefined without throwing
gameEnv?.gameObjects
gameEnv?.gameControl?.gameObjects
// Classic && guard chain
if (this.gameEnv && this.gameEnv.gameControl &&
!this.gameEnv.gameLevelTransitionTriggered) { ... }
Growth: Snake used || for boundary conditions. Level 2 introduces ?? and ?. β modern JavaScript operators that make null-safe property access concise and expressive.
π Reflection
Level 2 was the hardest level to build because everything was invisible β the maze walls, the collision logic, and the player feedback all had to be engineered from scratch without the game engine providing visual help.
| CS111 Concept | Trimester 1 Form | Level 2 Form | Growth |
|---|---|---|---|
| Iteration | Self-collision loop | for...of over heterogeneous object stores |
Iterating unknown structures |
| Conditionals | Route button input | Early-exit guard + ternary formatting | Defensive + functional conditionals |
| Nested Conditions | Direction + boundary | Player existence β property existence β reset | Multi-layer null safety |
| Numbers | Grid coordinates | Fractional proportional coordinates + timings | Resolution-independent math |
| Strings | Direction labels | Full HTML template literals | Strings as UI documents |
| Booleans | Game running flag | Idempotency guard + null-as-boolean pattern | Booleans for state safety |
| Arrays | Snake body | Candidate search arrays with .filter() / .find() |
Arrays as search spaces |
| Objects | Flat state object | Factory-pattern objects with embedded methods | Functions as object values |
| Math Operators | Arithmetic results | Time formatting (division + modulo) + DOM offsets | Math as infrastructure |
| String Ops | + concatenation |
Template literals + .padStart() |
Modern string tooling |
| Boolean Expressions | || boundary checks |
?? nullish coalescing + ?. optional chaining |
Modern null-safety operators |
The invisible maze was a metaphor for the work: you canβt see the walls until you hit them, and the same was true of the bugs. Every CS111 concept had to be applied defensively β assuming the game object might be undefined, the timer might not have started, the canvas might not exist yet. Defensive programming is CS111 in survival mode.