: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 = ‘
‘;
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 `
`;
}).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 = '
‘;
try {
currentScores = await getScores();
} catch (e) {
console.error(e);
leaderboardList.innerHTML = ‘
‘;
}
displayLeaderboard();
draw();
}
init();