Why functions reduce bugs
If the "compute jump velocity" step were written in five places, fixing a bug meant fixing it in five spots, and you'd surely miss one. A function is defined once and called many.
function canJumpHere(ground) {
return ground != null && Math.abs(ground - player.y) < 20;
}
// ...called from jump(), from landing(), from the collision pass...
When the jump feels slightly off, you fix it in one place and every caller benefits. This is the single biggest reason games stay maintainable as they grow to dozens of files.
The game's math.js file is exactly this philosophy: a handful of tiny, single-purpose, fully-reused functions.
Parameters as inputs, return as output
Think of a function as a small machine: parameters go in one end, the returned value comes out the other.
// The game maps a travelled distance to a scroll speed.
const speed = speedAt(distance); // distance in -> speed out
The cleanest functions have a clear input and a clear output and do one thing. They're easy to test, easy to reuse, and easy to understand months later. Functions that do five things at once are hard to test and hard to trust.
A common pattern is the guard-clause function: return early when an input is invalid, so the rest of the body can assume it's valid.
function safeSqrt(x) {
if (x < 0) return 0; // guard: negative input
return Math.sqrt(x);
}
The ground loop in Group 5 uses this exact pattern with continue inside a loop, early skip, then clean logic.