Scoring, Levels, and Game Over
A line-clearing board isn't quite a full game yet, it needs a score, a sense of progression as levels speed things up, and a way to know when it's over.
Classic scoring
const LINE_SCORES = [0, 100, 300, 500, 800]; // index = number of lines cleared at once
function scoreForLines(linesCleared: number, level: number): number {
return LINE_SCORES[linesCleared] * (level + 1);
}
Clearing 4 lines at once (a Tetris, the move the game is named after) is worth far more than 4x a single line (800 vs. 100), this is deliberate in the original game design, it rewards setting up multi-line clears instead of clearing lines one at a time. Multiplying by (level + 1) makes higher levels worth more points for the same clear, on top of the game already being faster and harder there.
Leveling up and speeding up
function levelForLines(totalLinesCleared: number): number {
return Math.floor(totalLinesCleared / 10); // level up every 10 lines
}
function speedForLevel(level: number): number {
return Math.max(100, 1000 - level * 75); // ms per tick, capped so it never gets impossibly fast
}
speedForLevel feeds directly into the useGameLoop hook's speedMs from the game loop lesson, as level climbs, the interval between ticks shrinks, gravity pulls pieces down faster. Math.max(100, ...) puts a floor on how fast it can get, without it, the formula would eventually produce a negative or absurdly tiny interval.
Detecting game over
function isGameOver(board: Matrix, newPiece: ActivePiece): boolean {
return hasCollision(board, newPiece, newPiece.row, newPiece.col);
}
The game is over the instant a freshly spawned piece already collides with something at its starting position, meaning the stack has reached all the way to the top, there's nowhere left to put a new piece. Check this right after spawning each new piece in the tick handler from the previous lesson, before letting the game loop continue.
const nextPiece = spawnPiece(randomShapeName());
if (isGameOver(clearedBoard, nextPiece)) {
// stop the interval, show a game over screen
}
TIP
Track totalLinesCleared as a running total across the whole game (not just the current tick), levelForLines needs the cumulative count to know when a level threshold has actually been crossed.