Juego de la Culebrita

Juego de la Culebrita

:root {
–bg-color: #ffffff; /* White background */
–primary-color: #000000; /* Black text */
–container-color: #34495e; /* Dark theme for game elements */
–container-alt-color: #4a627a;
–error-color: #e74c3c;
}
body {
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, Roboto, Oxygen, Ubuntu, Cantarell, ‘Open Sans’, ‘Helvetica Neue’, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
min-height: 100vh;
margin: 0;
padding: 10px 0;
background: var(–bg-color);
color: var(–primary-color);
-webkit-tap-highlight-color: transparent;
}
.main-container {
width: 100%;
max-width: 420px;
padding: 10px;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
}
h1 { font-size: 1.8em; text-align: center; border-bottom: 2px solid var(–primary-color); padding-bottom: 10px;}
.header-container { display: flex; align-items: center; justify-content: center; position: relative; width: 100%; margin-bottom: 15px; }
.scoreboard { font-size: 1.5em; text-align: center; flex-grow: 1; }
#muteBtn { position: absolute; right: 0; top: 50%; transform: translateY(-50%); background: none; border: 2px solid var(–primary-color); color: var(–primary-color); border-radius: 5px; cursor: pointer; font-size: 1.2em; padding: 5px 10px; }
.game-container { width: 100%; padding-bottom: 100%; position: relative; border: 5px solid var(–container-color); border-radius: 10px; box-shadow: 0 10px 20px rgba(0,0,0,0.2); }
canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: var(–container-color); border-radius: 5px; }
#d-pad-container { display: none; margin-top: 15px; width: 180px; height: 180px; display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: 1fr 1fr 1fr; gap: 5px; }
.d-pad-btn { background-color: #f0f0f0; color: var(–primary-color); border: 2px solid var(–primary-color); border-radius: 10px; font-size: 2.5em; cursor: pointer; user-select: none; }
#btn-up { grid-column: 2; grid-row: 1; }
#btn-left { grid-column: 1; grid-row: 2; }
#btn-right { grid-column: 3; grid-row: 2; }
#btn-down { grid-column: 2; grid-row: 3; }
.leaderboard-container { margin-top: 20px; width: 100%; text-align: center; color: var(–primary-color);}
#leaderboard-list { list-style: none; padding: 0; max-height: 150px; overflow-y: auto; color: var(–primary-color);}
#leaderboard-list li { display: flex; justify-content: space-between; align-items: center; padding: 5px 10px; background-color: #e0e0e0; border-radius: 5px; margin-bottom: 5px; }
#leaderboard-list li:nth-child(odd) { background-color: #f0f0f0; }
#leaderboard-list li.error { background-color: var(–error-color); justify-content: center; font-weight: bold; color: white; }
.leaderboard-name { flex-grow: 1; text-align: left; }
.leaderboard-score { font-weight: bold; }
.leaderboard-medal { width: 30px; text-align: left; }
@media (hover: none), (pointer: coarse) {
#d-pad-container { display: grid; }
}

Juego de la Culebrita

Puntaje: 0




Mejores Puntajes (Global)

    const API_KEY = ‘$2a$10$r6.X2Hdj1ta49BSXvD2oKOqoeZEOpXSiHPsY6EPQdwJudwwplKxUC’;
    const BIN_ID = ’68a7d65ad0ea881f405fe42d’;

    const canvas = document.getElementById(‘gameCanvas’);
    const ctx = canvas.getContext(‘2d’);
    const scoreElement = document.getElementById(‘score’);
    const muteBtn = document.getElementById(‘muteBtn’);
    const leaderboardList = document.getElementById(‘leaderboard-list’);

    const gridSize = 20;
    canvas.width = 400;
    canvas.height = 400;

    let snake, food, direction, score, gameOver, gameStarted, gameSpeed, audioCtx, beatTimeout;
    let isMuted = false;
    let currentScores = [];

    function setupNewGame() {
    snake = [{ x: 10, y: 10 }];
    food = {};
    direction = ‘right’;
    score = 0;
    gameOver = false;
    gameStarted = false;
    gameSpeed = 200;
    scoreElement.textContent = score;
    clearTimeout(beatTimeout);
    generateFood();
    }

    // — Event Listeners —
    muteBtn.addEventListener(‘click’, toggleMute);
    document.addEventListener(‘keydown’, handleKeyboard);
    document.getElementById(‘btn-up’).addEventListener(‘touchstart’, (e) => handleTouch(e, ‘up’));
    document.getElementById(‘btn-down’).addEventListener(‘touchstart’, (e) => handleTouch(e, ‘down’));
    document.getElementById(‘btn-left’).addEventListener(‘touchstart’, (e) => handleTouch(e, ‘left’));
    document.getElementById(‘btn-right’).addEventListener(‘touchstart’, (e) => handleTouch(e, ‘right’));

    function toggleMute() {
    isMuted = !isMuted;
    muteBtn.textContent = isMuted ? ‘🔇’ : ‘🔊’;
    if (isMuted) clearTimeout(beatTimeout);
    else if (gameStarted && !gameOver) playBeat();
    }

    function handleTouch(e, newDirection) {
    e.preventDefault();
    if (gameOver) return init();
    if (!gameStarted) startGame();
    setDirection(newDirection);
    }

    function handleKeyboard(event) {
    const gameKeys = [‘ArrowUp’, ‘ArrowDown’, ‘ArrowLeft’, ‘ArrowRight’];
    if (gameKeys.includes(event.key)) event.preventDefault();
    if (gameOver && event.key === ‘Enter’) return init();
    if (!gameStarted && gameKeys.includes(event.key)) startGame();
    if (!gameStarted) return;
    const directionMap = { ArrowUp: ‘up’, ArrowDown: ‘down’, ArrowLeft: ‘left’, ArrowRight: ‘right’ };
    if (directionMap[event.key]) setDirection(directionMap[event.key]);
    }

    function setDirection(newDirection) {
    const goingUp = direction === ‘up’, goingDown = direction === ‘down’, goingLeft = direction === ‘left’, goingRight = direction === ‘right’;
    if (newDirection === ‘up’ && !goingDown) direction = ‘up’;
    if (newDirection === ‘down’ && !goingUp) direction = ‘down’;
    if (newDirection === ‘left’ && !goingRight) direction = ‘left’;
    if (newDirection === ‘right’ && !goingLeft) direction = ‘right’;
    }

    function startGame() {
    if (gameStarted) return;
    gameStarted = true;
    if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
    playBeat();
    window.requestAnimationFrame(main);
    }

    function playSound(freq, dur, type, vol = 0.1) {
    if (isMuted || !audioCtx) return;
    const osc = audioCtx.createOscillator(), gain = audioCtx.createGain();
    osc.connect(gain); gain.connect(audioCtx.destination);
    osc.type = type; osc.frequency.setValueAtTime(freq, audioCtx.currentTime);
    gain.gain.setValueAtTime(vol, audioCtx.currentTime);
    osc.start(); osc.stop(audioCtx.currentTime + dur / 1000);
    }

    function playBeat() {
    if (isMuted || gameOver || !gameStarted) return;
    playSound(80, 50, ‘triangle’, 0.08);
    setTimeout(() => playSound(2000, 50, ‘square’, 0.02), gameSpeed);
    beatTimeout = setTimeout(playBeat, gameSpeed * 2);
    }

    function generateFood() {
    food = { x: Math.floor(Math.random() * gridSize), y: Math.floor(Math.random() * gridSize) };
    if (snake.some(segment => segment.x === food.x && segment.y === food.y)) generateFood();
    }

    function draw() {
    ctx.fillStyle = ‘#34495e’;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    if (gameOver) {
    ctx.fillStyle = ‘rgba(0, 0, 0, 0.7)’;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = ‘white’;
    ctx.textAlign = ‘center’;
    ctx.font = ’40px “Segoe UI”, Arial’;
    ctx.fillText(‘Juego Terminado’, canvas.width / 2, canvas.height / 2 – 20);
    ctx.font = ’20px “Segoe UI”, Arial’;
    ctx.fillText(‘Presiona Enter para Reiniciar’, canvas.width / 2, canvas.height / 2 + 20);
    return;
    }
    if (!gameStarted) {
    ctx.fillStyle = ‘rgba(0, 0, 0, 0.7)’;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = ‘white’;
    ctx.textAlign = ‘center’;
    ctx.font = ’30px “Segoe UI”, Arial’;
    ctx.fillText(‘Presiona una Flecha’, canvas.width / 2, canvas.height / 2 – 20);
    ctx.fillText(‘o Toca un Control’, canvas.width / 2, canvas.height / 2 + 20);
    return;
    }
    snake.forEach((segment, i) => {
    ctx.fillStyle = i === 0 ? ‘#27ae60’ : ‘#2ecc71’;
    ctx.fillRect(segment.x * gridSize, segment.y * gridSize, gridSize, gridSize);
    });
    ctx.fillStyle = ‘#e74c3c’;
    ctx.fillRect(food.x * gridSize, food.y * gridSize, gridSize, gridSize);
    }

    function update() {
    if (gameOver || !gameStarted) return;
    const head = { x: snake[0].x, y: snake[0].y };
    switch (direction) {
    case ‘up’: head.y–; break;
    case ‘down’: head.y++; break;
    case ‘left’: head.x–; break;
    case ‘right’: head.x++; break;
    }
    if (head.x = gridSize || head.y = gridSize || snake.some(s => s.x === head.x && s.y === head.y)) {
    endGame();
    return;
    }
    snake.unshift(head);
    if (head.x === food.x && head.y === food.y) {
    score++;
    scoreElement.textContent = score;
    playSound(440, 100, ‘sine’, 0.2);
    if (score > 0 && score % 10 === 0 && gameSpeed > 40) gameSpeed -= 20;
    generateFood();
    } else {
    snake.pop();
    }
    }

    function endGame() {
    gameOver = true;
    clearTimeout(beatTimeout);
    playSound(150, 200, ‘sawtooth’, 0.1);
    saveScore();
    }

    async function saveScore() {
    if (score === 0) return;
    const name = prompt(`Juego Terminado. Puntaje: ${score}.nIngresa tu nombre para guardar:`,’Jugador’);
    if (!name) return;
    leaderboardList.innerHTML = ‘

  1. Actualizando…
  2. ‘;
    const highScores = […currentScores];
    highScores.push({ score, name });
    highScores.sort((a, b) => b.score – a.score).splice(100);
    try {
    const res = await fetch(`https://api.jsonbin.io/v3/b/${BIN_ID}`, {
    method: ‘PUT’,
    headers: { ‘Content-Type’: ‘application/json’, ‘X-Master-Key’: API_KEY },
    body: JSON.stringify(highScores)
    });
    if (!res.ok) throw new Error(`Error al guardar: ${res.statusText}`);
    currentScores = highScores;
    displayLeaderboard();
    } catch (e) {
    console.error(e);
    alert(‘No se pudo guardar el puntaje. Revisa tu conexión o la configuración de las claves.’);
    displayLeaderboard(); // Show previous scores
    }
    }

    async function getScores() {
    try {
    const res = await fetch(`https://api.jsonbin.io/v3/b/${BIN_ID}/latest`, { headers: { ‘X-Master-Key’: API_KEY, ‘X-Bin-Meta’: ‘false’ } });
    if (!res.ok) throw new Error(`Error al cargar: ${res.statusText}`);
    const data = await res.json();
    return Array.isArray(data) ? data : [];
    } catch (e) {
    console.warn(e.message, ‘- Using local backup.’);
    return []; // Return empty array on error
    }
    }

    function displayLeaderboard() {
    leaderboardList.innerHTML = currentScores.map((s, i) => {
    const medal = i === 0 ? ‘🥇’ : i === 1 ? ‘🥈’ : i === 2 ? ‘🥉’ : ”;
    return `

  3. ${medal}${s.name}${s.score}
  4. `;
    }).join(”);
    }

    let lastFrameTime = 0;
    function main(currentTime) {
    if (gameOver) return draw();
    window.requestAnimationFrame(main);
    const elapsed = currentTime – lastFrameTime;
    if (elapsed < gameSpeed) return;
    lastFrameTime = currentTime;
    update();
    draw();
    }

    async function init() {
    setupNewGame();
    leaderboardList.innerHTML = '

  5. Cargando puntajes…
  6. ‘;
    try {
    currentScores = await getScores();
    } catch (e) {
    console.error(e);
    leaderboardList.innerHTML = ‘

  7. No se pudieron cargar los puntajes.
  8. ‘;
    }
    displayLeaderboard();
    draw();
    }

    init();