0% found this document useful (0 votes)
26 views5 pages

!doctype HTML

The document is an HTML file that implements a simple Snake game using JavaScript and the HTML5 canvas. It includes game controls, a scoring system, and a user interface styled with CSS. The game allows players to control the snake, eat food to grow, and keeps track of the best score using local storage.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
26 views5 pages

!doctype HTML

The document is an HTML file that implements a simple Snake game using JavaScript and the HTML5 canvas. It includes game controls, a scoring system, and a user interface styled with CSS. The game allows players to control the snake, eat food to grow, and keeps track of the best score using local storage.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 5

<!

DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<title>Snake JS — Duver</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
:root {
--bg: #0f1226;
--grid: #1c2140;
--snake: #34d399; /* Verde menta */
--snake-head: #10b981;
--food: #f59e0b; /* Ámbar */
--text: #e5e7eb;
--accent: #60a5fa;
}
* { box-sizing: border-box; }
body {
margin: 0; min-height: 100vh; display: grid; place-items: center;
background: radial-gradient(1200px 600px at 30% -10%, #1a1f3b, transparent),
var(--bg);
color: var(--text); font-family: ui-sans-serif, system-ui, -apple-system,
Segoe UI, Roboto, Arial;
}
.wrap { display: grid; gap: 14px; justify-items: center; }
h1 { margin: 0; font-size: 20px; font-weight: 700; letter-spacing: .3px; color:
var(--accent); }
canvas {
background: repeating-linear-gradient(0deg, var(--grid), var(--grid) 20px,
transparent 20px, transparent 40px),
repeating-linear-gradient(90deg, var(--grid), var(--grid) 20px,
transparent 20px, transparent 40px),
#0b0f24;
box-shadow: 0 8px 30px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.05)
inset;
border-radius: 10px;
}
.hud { display: flex; gap: 16px; align-items: center; font-size: 14px; }
.pill { padding: 6px 10px; border-radius: 999px; background:
rgba(96,165,250,.12); border: 1px solid rgba(96,165,250,.25); }
.keys { opacity: .8; font-size: 12px; }
button {
background: #1f2937; color: var(--text); border: 1px solid #374151; border-
radius: 8px;
padding: 8px 12px; cursor: pointer;
}
button:hover { background: #2b3650; }
@media (max-width: 520px) {
canvas { width: 300px; height: 300px; }
}
</style>
</head>
<body>
<div class="wrap">
<h1>Snake JS</h1>
<canvas id="game" width="400" height="400" aria-label="Tablero del juego"
role="img"></canvas>
<div class="hud">
<div class="pill">Puntos: <span id="score">0</span></div>
<div class="pill">Récord: <span id="best">0</span></div>
<div class="keys">Controles: Flechas/WASD · P = Pausa · R = Reiniciar</div>
<button id="btn">Pausa</button>
</div>
</div>

<script>
// Configuración del tablero
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const SIZE = 20; // tamaño de cada celda (px)
const CELLS = canvas.width / SIZE; // 400/20 = 20 celdas por lado

// HUD
const scoreEl = document.getElementById('score');
const bestEl = document.getElementById('best');
const btn = document.getElementById('btn');

// Estado del juego


let snake, dir, nextDir, food, score, best, speed, tickId, paused, gameOver;

function init() {
snake = [{ x: 10, y: 10 }]; // cabeza en el centro
dir = { x: 1, y: 0 }; // moviendo a la derecha
nextDir = { ...dir };
score = 0;
speed = 120; // ms por tick (menor = más rápido)
paused = false;
gameOver = false;
scoreEl.textContent = '0';
best = Number(localStorage.getItem('snake_best') || 0);
bestEl.textContent = best;
placeFood();
clearInterval(tickId);
tickId = setInterval(loop, speed);
draw();
}

function placeFood() {
// Coloca la comida en una celda libre
while (true) {
const f = { x: rand(0, CELLS - 1), y: rand(0, CELLS - 1) };
if (!snake.some(s => s.x === f.x && s.y === f.y)) { food = f; break; }
}
}

function rand(min, max) {


return Math.floor(Math.random() * (max - min + 1)) + min;
}

function loop() {
if (paused || gameOver) return;

// Actualiza dirección sin permitir giro de 180°


if ((nextDir.x !== -dir.x || nextDir.y !== -dir.y)) dir = nextDir;

const head = { x: snake[0].x + dir.x, y: snake[0].y + dir.y };

// Colisiones con muro


if (head.x < 0 || head.y < 0 || head.x >= CELLS || head.y >= CELLS) {
return end();
}
// Colisión consigo misma
if (snake.some(s => s.x === head.x && s.y === head.y)) {
return end();
}

snake.unshift(head); // mueve la cabeza

// Comer
if (head.x === food.x && head.y === food.y) {
score++;
scoreEl.textContent = score;
if (score % 5 === 0 && speed > 60) { // aumenta dificultad
speed -= 5;
clearInterval(tickId);
tickId = setInterval(loop, speed);
}
placeFood();
} else {
snake.pop(); // avanza (quita cola) si no come
}

draw();
}

function end() {
gameOver = true;
if (score > best) {
best = score;
localStorage.setItem('snake_best', best);
}
bestEl.textContent = best;
// Mensaje de fin de juego
draw();
ctx.fillStyle = 'rgba(0,0,0,.6)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 22px Segoe UI, Arial';
ctx.textAlign = 'center';
ctx.fillText('¡Juego terminado!', canvas.width / 2, canvas.height / 2 - 10);
ctx.font = '16px Segoe UI, Arial';
ctx.fillText('Pulsa R para reiniciar', canvas.width / 2, canvas.height / 2 +
18);
}

function draw() {
// Fondo (el grid está en el CSS)
ctx.clearRect(0, 0, canvas.width, canvas.height);

// Comida
drawCell(food.x, food.y, '#00000022'); // sombra sutil
drawRoundedCell(food.x, food.y, 6, getCss('--food'));

// Serpiente
snake.forEach((seg, i) => {
const color = i === 0 ? getCss('--snake-head') : getCss('--snake');
drawRoundedCell(seg.x, seg.y, 4, color);
});
}

function drawCell(x, y, color) {


ctx.fillStyle = color;
ctx.fillRect(x * SIZE, y * SIZE, SIZE, SIZE);
}

function drawRoundedCell(x, y, r, color) {


const px = x * SIZE, py = y * SIZE, w = SIZE, h = SIZE;
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(px + r, py);
ctx.arcTo(px + w, py, px + w, py + h, r);
ctx.arcTo(px + w, py + h, px, py + h, r);
ctx.arcTo(px, py + h, px, py, r);
ctx.arcTo(px, py, px + w, py, r);
ctx.closePath();
ctx.fill();
}

function getCss(varName) {
return
getComputedStyle(document.documentElement).getPropertyValue(varName).trim();
}

// Controles
window.addEventListener('keydown', (e) => {
const k = e.key.toLowerCase();
const map = {
arrowup: { x: 0, y: -1 }, w: { x: 0, y: -1 },
arrowdown: { x: 0, y: 1 }, s: { x: 0, y: 1 },
arrowleft: { x: -1, y: 0 }, a: { x: -1, y: 0 },
arrowright:{ x: 1, y: 0 }, d: { x: 1, y: 0 }
};
if (map[k]) nextDir = map[k];
if (k === 'p') togglePause();
if (k === 'r') init();
});

btn.addEventListener('click', togglePause);
function togglePause() {
if (gameOver) return;
paused = !paused;
btn.textContent = paused ? 'Reanudar' : 'Pausa';
// Pintar overlay en pausa
if (paused) {
ctx.fillStyle = 'rgba(0,0,0,.45)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 22px Segoe UI, Arial';
ctx.textAlign = 'center';
ctx.fillText('Pausa', canvas.width / 2, canvas.height / 2);
} else {
draw();
}
}

// Iniciar
init();
</script>
</body>
</html>

You might also like