How it works — open the hood
// The snake is stored as a QUEUE: an ordered list of grid cells.
// Index 0 is the HEAD. The last item is the TAIL.
let snake = [
{ row: 10, col: 10 }, // head
{ row: 10, col: 9 },
{ row: 10, col: 8 }, // tail
];
// Every tick (160ms at start), this runs:
function tick() {
// 1. Where does the head go next?
const newHead = {
row: snake[0].row + dir.dr,
col: snake[0].col + dir.dc,
};
// 2. Did we hit a wall or our own body? Game over.
if (hitWall(newHead) || hitSelf(newHead)) { gameOver(); return; }
// 3. PUSH the new head onto the FRONT of the queue.
snake.unshift(newHead);
// 4. Did we eat a berry?
if (atBerry(newHead)) {
// EATING: skip the tail-drop. Queue grows by one. Snake gets longer.
score++;
spawnBerry();
} else {
// NORMAL: POP the tail off the back. Queue stays the same length.
snake.pop();
}
}
// That's the whole trick. unshift = push to front. pop = drop from back.
// When eating, skip the pop — and the queue (the snake) grows.