Control Structures
Control Structures - CS111 Review
| Back | Index | Next |
|---|---|---|
| OOP | Index | Data Types |
Control Structures
Click a category to see examples from Heist.exe.
Control Structures in Heist.exe
Control structures determine how, when, and how many times code runs. In Heist.exe, they’re what make the Guard bounce between walls, prevent gems from being collected twice, and build the entire scene at level start from a single array.
How They Work Together
flowchart TD
A([HEIST Loop Starts]) --> B{Is game running?}
B -->|No| Z([Exit])
B -->|Yes| C["Loop through gameObjects"]
C --> D{Is it a Guard?}
D -->|No| G["Update object"]
D -->|Yes| E{Player Destroyed?}
E -->|Yes| G
E -->|No| F{Collision detected?}
F -->|No| H["stayWithinCanvas"]
H --> I{Hit a wall?}
I -->|No| G
I -->|Yes| J["Reverse velocity"]
F -->|Yes| K["destroy player"]
J --> G
K --> G
G --> C
The Three Core Structures
🔁 Iteration — Repeat — execute code multiple times based on a condition or collection
Exactly what it is: A loop repeats a block of code multiple times. Without loops, if you wanted to create 17 game objects, you’d have to write new Guard(...); new Guard(...); new Gem(...); seventeen times. With a loop, you iterate through a list once and create all 17 objects in one pass.
From Heist.exe: HeistL2.js uses forEach to instantiate all game objects at level setup:
// HeistL2.js — this.classes is a 17-entry array
this.classes = [
{ class: GameEnvBackground, data: image_data_bg },
{ class: HeistPlayer, data: sprite_data_mc },
{ class: Gem, data: gem_data_1 },
{ class: Gem, data: gem_data_2 },
{ class: Guard, data: sprite_data_guard1 },
{ class: Guard, data: sprite_data_guard2 },
{ class: Barrier, data: border_top },
{ class: Barrier, data: border_bottom },
{ class: Barrier, data: border_left },
{ class: Barrier, data: border_right },
// ... 7 more entries
];
// Game engine iterates this.classes:
this.classes.forEach(({ class: Class, data }) => {
new Class(data, gameEnv); // Run once per entry: 17 times total
});
// Without forEach, you'd have to write:
// new GameEnvBackground(image_data_bg, gameEnv);
// new HeistPlayer(sprite_data_mc, gameEnv);
// new Gem(gem_data_1, gameEnv);
// new Gem(gem_data_2, gameEnv);
// ... 13 more identical lines with different variables
Each frame, the game engine loops through all gameObjects and calls update():
// Pseudo-code: every frame
gameObjects.forEach(obj => {
obj.update(); // Guard bounces and checks collision
// Gem checks if collected
// Player checks for keypresses
});
| Loop | Use when… |
|---|---|
for |
You know the count upfront: for (let i = 0; i < 17; i++) |
while |
You repeat until a condition changes: while (isPlaying) { ... } |
forEach |
You’re walking through an array: array.forEach(item => ...) |
Conditions — Decide — branch code based on true/false checks
Exactly what it is: An if / else statement checks whether something is true right now, then runs different code depending on the answer. It’s a fork in the road: if the condition is true, go down one path; otherwise, go down another.
From Heist.exe: Gem.js gates collection on the permanentlyCollected flag:
// Gem.js — collect() method checks state before running
collect() {
if (this.permanentlyCollected) return; // Already collected? Exit immediately
this.permanentlyCollected = true; // Mark as collected
this.collected = true;
if (this.gameEnv) {
if (!this.gameEnv.stats) this.gameEnv.stats = { coinsCollected: 0 };
this.gameEnv.stats.coinsCollected = (this.gameEnv.stats.coinsCollected || 0) + this.value;
}
console.log(`Gem collected! +${this.value}`);
}
Guard.js checks player state before triggering collision:
// Guard.js — update() method — lines 12-28
update() {
this.draw();
// Only check collision if player hasn't been destroyed yet
if (!this.playerDestroyed && this.collisionChecks()) { // Two conditions must both be true
this.handleCollisionEvent();
}
this.position.y += this.velocity.y;
this.stayWithinCanvas();
}
Nested Conditions — Refine — place checks inside checks to handle layered logic
Exactly what it is: A nested condition is an if statement inside another if statement. It lets you handle more complex scenarios where you need to decide based on multiple conditions in order.
From Heist.exe: Guard.stayWithinCanvas() (lines 33-53) has four separate boundary blocks. Each uses a nested structure to check both position and velocity:
// Guard.js — stayWithinCanvas() — lines 33-53
stayWithinCanvas() {
// Check bottom boundary
if (this.position.y + this.height > this.gameEnv.innerHeight) {
this.position.y = this.gameEnv.innerHeight - this.height;
this.velocity.y *= -1; // Bounce: reverse direction
console.log(this.velocity.y);
}
// Check top boundary
if (this.position.y < 0) {
this.position.y = 1;
this.velocity.y *= -1; // Bounce: reverse direction
console.log(this.velocity.y);
}
// Check right boundary
if (this.position.x + this.width > this.gameEnv.innerWidth) {
this.position.x = this.gameEnv.innerWidth - this.width;
this.velocity.x = 0; // Stop horizontal movement
}
// Check left boundary
if (this.position.x < 0) {
this.position.x = 0;
this.velocity.x = 0; // Stop horizontal movement
}
}
More complex nesting — HeistPlayer.handleCollisionReaction(): Multiple levels of checks to determine which direction to push the player:
// heistPlayer.js — lines 23-45: Nested conditions for collision resolution
if (touchPoints.right && !touchPoints.left) {
// Player hit barrier from the left (barrier is on player's right)
this.position.x = barrierLeft - this.width - BUFFER;
this.velocity.x = 0;
} else if (touchPoints.left && !touchPoints.right) {
// Player hit barrier from the right (barrier is on player's left)
this.position.x = barrierRight + BUFFER;
this.velocity.x = 0;
}
if (touchPoints.bottom && !touchPoints.top) {
// Player hit barrier from above (barrier below)
this.position.y = barrierTop - this.height - BUFFER;
this.velocity.y = 0;
} else if (touchPoints.top && !touchPoints.bottom) {
// Player hit barrier from below (barrier above)
this.position.y = barrierBottom + BUFFER;
this.velocity.y = 0;
}
Quick Reference
| Structure | Keyword | Purpose |
|---|---|---|
| Iteration | forEach, for, while |
Repeat actions |
| Condition | if, else if, else |
Branch on truth |
| Nested condition | if inside if |
Layer decisions |
Rule of thumb: If something needs to repeat, use a loop. If something needs to choose, use a condition. If the choice depends on another choice, nest them.