πŸš€ Level 1 Blog: GameLevelSpace β€” CS111 Concepts in Action


This blog traces the CS111 concepts used in building Level 1 of our adventure game. For each concept, we show where we first encountered it in Trimester 1 (snake game or calculator), and then how we applied it β€” more sophisticatedly β€” in the level code.

πŸ“‹ Table of Contents

  1. Control Structures β€” Iteration
  2. Control Structures β€” Conditionals
  3. Data Types β€” Numbers
  4. Data Types β€” Strings
  5. Data Types β€” Booleans
  6. Data Types β€” Arrays
  7. Data Types β€” Objects (JSON)
  8. Operators β€” Mathematical
  9. Operators β€” String Operations
  10. Operators β€” Boolean Expressions
  11. Reflection

πŸ” Control Structures β€” Iteration

Where we learned it: Snake Game (Trimester 1)

In Trimester 1, we used a forEach loop to draw every segment of the snake on the canvas:

// Snake game β€” Trimester 1
snake.forEach(segment => {
    ctx.fillRect(segment.x, segment.y, CELL_SIZE, CELL_SIZE);
});

How we used it in Level 1

In Level 1, iteration is baked into the engine’s game loop, which iterates over this.classes to instantiate every game object at level startup. We defined the array, and the engine loops through it:

// GameLevelSpacelevel3.js β€” this.classes drives the engine's forEach loop
this.classes = [
    { class: GameEnvBackground, data: bgData },
    { class: Player,            data: playerData },
    { class: Npc,               data: npcData1 },
    { class: Barrier,           data: dbarrier_1 },
    { class: Barrier,           data: dbarrier_2 },
    { class: Barrier,           data: dbarrier_3 },
    { class: Barrier,           data: dbarrier_4 },
    { class: Barrier,           data: dbarrier_5 }
];

Growth: In snake, we manually iterated to render. Here we design the data structure that the engine iterates β€” a step up in abstraction. We moved from writing the loop to designing what the loop consumes.


πŸ”€ Control Structures β€” Conditionals

Where we learned it: Calculator (Trimester 1)

Our Trimester 1 calculator used if/else to route between operations:

// Calculator β€” Trimester 1
if (operator === '+') {
    result = a + b;
} else if (operator === '-') {
    result = a - b;
} else if (operator === '*') {
    result = a * b;
} else {
    result = a / b;
}

How we used it in Level 1

In Level 1, we use conditionals inside the NPC’s interact() and reaction() methods to guard against missing systems before calling them:

// GameLevelSpacelevel3.js β€” guarded NPC callbacks
reaction: function() {
    if (this.dialogueSystem) {
        this.showReactionDialogue();
    } else {
        console.log(this.greeting);
    }
},
interact: function() {
    if (this.dialogueSystem) {
        this.showRandomDialogue();
    }
    // Trigger level transition β€” same effect as pressing ESC
    if (this.gameEnv && this.gameEnv.gameControl && this.gameEnv.gameControl.currentLevel) {
        this.gameEnv.gameControl.currentLevel.continue = false;
    }
}

Growth: Calculator conditionals chose between math operations. Here, conditionals implement defensive programming β€” checking that objects exist before calling methods on them, preventing null reference crashes in a live game loop.


πŸ”’ Data Types β€” Numbers

Where we learned it: Snake Game (Trimester 1)

In snake, numbers tracked position and score:

// Snake game β€” Trimester 1
let score = 0;
let headX = 10;
let headY = 10;
const CELL_SIZE = 20;

How we used it in Level 1

Level 1 uses numbers for everything from sprite dimensions and scale factors to initial positions and animation rates:

// GameLevelSpacelevel3.js β€” numeric properties everywhere
const playerData = {
    SCALE_FACTOR:   5,
    STEP_FACTOR:    1000,
    ANIMATION_RATE: 50,
    INIT_POSITION:  { x: 100, y: 300 },
    pixels:         { height: 770, width: 513 },
    // ...
};

// Barrier geometry β€” all numbers
const dbarrier_4 = {
    x: 300, y: 600, width: 400, height: 30,
};

Growth: Snake used 4–5 raw numbers. Level 1 organises dozens of numeric values inside configuration objects, demonstrating that numbers are most powerful when structured.


πŸ”€ Data Types β€” Strings

Where we learned it: Snake Game (Trimester 1)

In snake, strings were used for display text:

// Snake game β€” Trimester 1
ctx.fillText('Score: ' + score, 10, 20);
ctx.fillText('Game Over!', canvas.width / 2, canvas.height / 2);

How we used it in Level 1

In Level 1, strings serve as asset paths, NPC identifiers, NPC dialogue, and configuration names:

// GameLevelSpacelevel3.js β€” strings as identifiers and asset paths
const bgData = {
    name: "custom_bg",
    src: path + "/images/gamebuilder/bg/alien_planet.jpg",
};

const npcData1 = {
    id: '1',
    greeting: 'Hi, I am a chill guy. Touch the moon but don\'t fly too high.',
    dialogues: [
        'Hi, I am a chill guy. Touch the moon but don\'t fly too high.',
        'Good luck on your adventure!',
        'Time to move to the next level!'
    ],
};

Growth: Snake used strings only for rendering. Here strings are data β€” they route asset loading, identify game objects, and carry narrative content. The path + concatenation pattern also previews template literal usage we expanded in later levels.


βœ… Data Types β€” Booleans

Where we learned it: Snake Game (Trimester 1)

// Snake game β€” Trimester 1
let gameRunning = true;
let ateFood = false;

if (ateFood) {
    score++;
    ateFood = false;
}

How we used it in Level 1

Level 1 uses booleans as flags on game objects to control visibility, hitbox behavior, and level state:

// GameLevelSpacelevel3.js β€” booleans as configuration flags
const dbarrier_1 = {
    visible: true,       // boolean: render or not
    fromOverlay: true,   // boolean: origin tracking flag
    // ...
};

// In the constructor β€” transition guard flag
this.levelTransitionTriggered = false;

// In initialize() β€” engine lifecycle flag
initialize() {
    if (this.gameEnv && this.gameEnv.gameControl) {
        this.gameEnv.gameLevelTransitionTriggered = false;
    }
}

Growth: Snake used one or two booleans as game-state flags. Level 1 uses booleans as configuration properties within objects, making them first-class data rather than standalone variables.


πŸ“¦ Data Types β€” Arrays

Where we learned it: Snake Game (Trimester 1)

The snake body was our first meaningful array:

// Snake game β€” Trimester 1
let snake = [
    { x: 10, y: 10 },
    { x: 9,  y: 10 },
    { x: 8,  y: 10 }
];
snake.unshift(newHead);
snake.pop();

How we used it in Level 1

Level 1 uses arrays in two distinct places β€” the NPC dialogue list and the master class registry:

// GameLevelSpacelevel3.js β€” dialogues array
dialogues: [
    'Hi, I am a chill guy. Touch the moon but don\'t fly too high.',
    'Good luck on your adventure!',
    'Time to move to the next level!'
],

// Master level class registry β€” array of objects
this.classes = [
    { class: GameEnvBackground, data: bgData },
    { class: Player,            data: playerData },
    { class: Npc,               data: npcData1 },
    { class: Barrier,           data: dbarrier_1 },
    // ...
];

Growth: Snake arrays stored position data. Level 1 arrays store heterogeneous objects β€” the this.classes array is essentially a dependency injection list that the game engine processes to bootstrap the entire level.


πŸ—‚οΈ Data Types β€” Objects (JSON)

Where we learned it: Calculator (Trimester 1)

Our calculator stored button configurations as objects:

// Calculator β€” Trimester 1
const buttonConfig = {
    label: '+',
    action: 'add',
    color: '#4CAF50'
};

How we used it in Level 1

Every entity in Level 1 is a rich configuration object. The player sprite alone has a nested orientation object, pixels dimensions, directional animation maps, and a keypress map:

// GameLevelSpacelevel3.js β€” deeply nested configuration objects
const playerData = {
    id: 'playerData',
    src: path + "/images/gamebuilder/sprites/astro.png",
    SCALE_FACTOR: 5,
    INIT_POSITION:  { x: 100, y: 300 },          // nested object
    pixels:         { height: 770, width: 513 },  // nested object
    orientation:    { rows: 4, columns: 4 },       // nested object
    down:      { row: 0, start: 0, columns: 3 },  // animation frame object
    downRight: { row: 1, start: 0, columns: 3, rotate: Math.PI/16 },
    hitbox:    { widthPercentage: 0, heightPercentage: 0 },
    keypress:  { up: 38, left: 37, down: 40, right: 39 }
};

Growth: Calculator had flat single-level objects. Level 1 introduced deeply nested JSON where objects contain objects, creating a declarative specification language for game entities.


βž• Operators β€” Mathematical

Where we learned it: Calculator (Trimester 1)

// Calculator β€” Trimester 1
function calculate(a, op, b) {
    return op === '+' ? a + b
         : op === '-' ? a - b
         : op === '*' ? a * b
         : a / b;
}

How we used it in Level 1

Mathematical operators appear in sprite animation calculations β€” specifically using Math.PI with division to compute rotation angles, and Math.min to safely clamp NPC row indices:

// GameLevelSpacelevel3.js β€” math operators in sprite configuration
downRight: { row: 1, start: 0, columns: 3, rotate: Math.PI/16 },  // division
downLeft:  { row: 0, start: 0, columns: 3, rotate: -Math.PI/16 }, // negation

// NPC row clamping with Math.min
right:  { row: Math.min(1, 4 - 1), start: 0, columns: 3 },  // subtraction inside Math.min
left:   { row: Math.min(2, 4 - 1), start: 0, columns: 3 },
up:     { row: Math.min(3, 4 - 1), start: 0, columns: 3 },

Growth: Calculator operators computed user-requested results. Here math operators solve engine configuration problems β€” preventing sprite sheet index out-of-bounds errors and computing radian rotation values.


πŸ”— Operators β€” String Operations

Where we learned it: Snake Game (Trimester 1)

// Snake game β€” Trimester 1
scoreDisplay.textContent = 'Score: ' + score;
statusMsg = 'Game Over! Final score: ' + score;

How we used it in Level 1

Level 1 uses string concatenation to build every asset path dynamically from the engine-provided path variable:

// GameLevelSpacelevel3.js β€” path concatenation for all assets
const bgData = {
    src: path + "/images/gamebuilder/bg/alien_planet.jpg",
};

const playerData = {
    src: path + "/images/gamebuilder/sprites/astro.png",
};

const npcData1 = {
    src: path + "/images/gamify/chillguy.png",
};

Growth: Snake concatenated strings for display. Level 1 uses concatenation for dynamic resource resolution β€” the path prefix lets the same level file work across different deployment environments.


⚑ Operators β€” Boolean Expressions

Where we learned it: Snake Game (Trimester 1)

// Snake game β€” Trimester 1
if (headX < 0 || headX >= COLS || headY < 0 || headY >= ROWS) {
    gameOver = true;
}

How we used it in Level 1

The interact() callback chains three && checks before triggering the level transition β€” a compound guard that ensures all required objects exist:

// GameLevelSpacelevel3.js β€” chained && guards in interact()
interact: function() {
    if (this.dialogueSystem) {
        this.showRandomDialogue();
    }
    // Three-part compound boolean β€” all must be truthy
    if (this.gameEnv && this.gameEnv.gameControl && this.gameEnv.gameControl.currentLevel) {
        this.gameEnv.gameControl.currentLevel.continue = false;
    }
},

Growth: Snake used || for boundary checks. Level 1 uses && chaining to safely traverse an object graph β€” short-circuit evaluation means if gameEnv is null, the chain stops before accessing .gameControl, preventing runtime errors.


πŸ’­ Reflection

Level 1 was our entry point into the game engine β€” a relatively simple level focused on getting the basic structure right. Looking back at it through the CS111 lens:

CS111 Concept Trimester 1 Form Level 1 Form Growth
Iteration Render loop over snake segments this.classes array consumed by engine loop Designing what the loop processes
Conditionals Choose math operator Guard object existence before method calls Defensive programming
Numbers Position + score vars Nested numeric config (scale, animation, hitbox) Data organisation
Strings Display text Asset paths + NPC dialogue Strings as functional data
Booleans Game state flags Configuration property flags Booleans inside objects
Arrays Snake body segments Class registry + dialogue lists Arrays of structured objects
Objects Flat button config Deeply nested entity specs Config-as-language pattern
Math Operators Arithmetic calculations Rotation angles + index clamping Math serving engine requirements
String Ops Score display concat Dynamic path resolution Environment-portable assets
Boolean Expressions Boundary OR checks Object-graph AND traversal Short-circuit safety

The biggest conceptual jump from Trimester 1 to Level 1 was realising that data structures do work. Instead of writing imperative code to set up each game object, we declaratively defined configuration objects and handed them to the engine. The CS111 fundamentals didn’t disappear β€” they moved into data.