const container = document.getElementById("snake-container"); const debug_container = document.getElementById("debug"); const start_button = document.getElementById("begin"); const ROWS = 16; const COLUMNS = 16; const INITIAL_LENGTH = 0; for (let row = 0; row < ROWS; row++) { let span = document.createElement("span"); span.className = "row"; span.id = row; for (let column = 0; column < COLUMNS; column++) { let checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.checked = false; checkbox.disabled = true; checkbox.id = `${row}/${column}`; checkbox.addEventListener('click', (event) => event.preventDefault()); span.appendChild(checkbox) } container.append(span) }; function set_checkbox(x, y, value) { let checkbox = document.getElementById(`${y}/${x}`); checkbox.checked = value; checkbox.disabled = !value; } let queue = [{ x: 6, y: 4, }, { x: 6, y: 5 }]; let snake_length = 0; let key_pressed = false; let direction = "ArrowRight"; let apple; let game_over = true; document.addEventListener("keydown", (e) => { const validDirections = [ "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight" ]; if (!validDirections.includes(e.key)) { return; } if (key_pressed) return; key_pressed = true; const opposites = { ArrowUp: "ArrowDown", ArrowDown: "ArrowUp", ArrowLeft: "ArrowRight", ArrowRight: "ArrowLeft" }; if (e.key !== opposites[direction]) { direction = e.key; } }); function get_checkbox(x, y) { return document.getElementById(`${y}/${x}`); } function trim_snake(queue, snake_length) { while (queue.length > snake_length) { let tail = queue.pop(); set_checkbox(tail.x, tail.y, false) }; return queue; } function render_snake(queue) { queue.forEach(dot => { set_checkbox(dot.x, dot.y, true); get_checkbox(dot.x, dot.y).className = "snake-body"; }); let head = queue[0]; get_checkbox(head.x, head.y).className = "snake-head"; } function handle_input(queue) { const head = queue[0]; switch (direction) { case "ArrowLeft": queue.unshift({ x: head.x - 1, y: head.y }); break; case "ArrowRight": queue.unshift({ x: head.x + 1, y: head.y }); break; case "ArrowUp": queue.unshift({ x: head.x, y: head.y - 1 }); break; case "ArrowDown": queue.unshift({ x: head.x, y: head.y + 1 }); break; } } function new_apple(queue) { if (!apple || (queue[0].x === apple.x && queue[0].y === apple.y)) { do { apple = { x: Math.floor(Math.random() * COLUMNS), y: Math.floor(Math.random() * ROWS) }; } while ( queue.some(segment => segment.x === apple.x && segment.y === apple.y ) ); let checkbox = get_checkbox(apple.x, apple.y); checkbox.className = "apple"; checkbox.checked = true; checkbox.disabled = false; snake_length++; } } function validate_snake(queue) { const head = queue[0]; if ( head.x < 0 || head.x >= COLUMNS || head.y < 0 || head.y >= ROWS ) game_over = true; } function init_game() { document.querySelectorAll('input[type="checkbox"]').forEach((checkbox) => { checkbox.checked = false; checkbox.className = ""; checkbox.disabled = true; }); queue = [{ x: 0, y: 0, }, { x: 1, y: 0 }]; snake_length = INITIAL_LENGTH; key_pressed = false; direction = "ArrowRight"; apple = undefined; new_apple(queue); game_over = false; } function game() { if ( queue.some((obj, i) => queue.findIndex(o => JSON.stringify(o) === JSON.stringify(obj)) !== i ) || game_over ) { game_over = true; debug_container.textContent = "You lost :(\nScore: " + snake_length; return; } key_pressed = false; handle_input(queue); validate_snake(queue); if (game_over) { debug_container.textContent = "You lost :(\nScore: " + snake_length; return; } trim_snake(queue, snake_length); new_apple(queue); render_snake(queue); debug_container.textContent = `Score: ${snake_length}`; } start_button.addEventListener("click", (button) => { init_game(); }); setInterval(game, 200) // 5 fps (ms = 1000 / (x)FPS)