files
Juegos/memoria/script.js

228 lines
6.0 KiB
JavaScript

'use strict';
// Sรญmbolos disponibles (>= 18 รบnicos para niveles altos)
const SYMBOLS = [
'๐ŸŽ','๐ŸŒ','๐Ÿ’','๐Ÿ‡','๐Ÿ‰','๐Ÿ‘','๐ŸŠ','๐Ÿ“',
'๐Ÿฅ','๐Ÿ','๐Ÿฅฅ','๐Ÿ','๐Ÿˆ','๐Ÿ','๐Ÿ”','๐Ÿ•',
'๐ŸŽˆ','โญ','๐ŸŒ™','โ˜€๏ธ','โšฝ','๐Ÿ€','๐ŸŽฒ','๐ŸŽต',
'๐Ÿถ','๐Ÿฑ','๐Ÿผ','๐Ÿธ','๐ŸฆŠ','๐Ÿฆ','๐Ÿฆ„','๐Ÿฏ',
'๐Ÿš—','๐Ÿš€','โœˆ๏ธ','๐Ÿšฒ','๐Ÿ๏ธ','๐Ÿฐ'
];
const DIFFICULTIES = {
facil: { pairs: 6 },
normal: { pairs: 8 },
dificil: { pairs: 12 },
extremo: { pairs: 18 }
};
// Estado del juego
let deck = []; // array de sรญmbolos duplicados y mezclados
let firstCard = null;
let secondCard = null;
let lockBoard = false;
let matches = 0;
let moves = 0;
let totalPairs = DIFFICULTIES.normal.pairs;
let timerId = null;
let startTime = 0;
// DOM
const gameBoard = document.getElementById('game-board');
const infoDiv = document.getElementById('info');
const resetBtn = document.getElementById('reset-btn');
const difficultySelect = document.getElementById('difficulty');
const movesSpan = document.getElementById('moves');
const timeSpan = document.getElementById('time');
const bestSpan = document.getElementById('best-score');
// Utils
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
function pickSymbols(n) {
const pool = SYMBOLS.slice();
shuffle(pool);
return pool.slice(0, n);
}
function formatTime(seconds) {
const s = Math.max(0, Math.floor(seconds));
return `${s}s`;
}
function bestKey(diff) {
return `memory_best_time_${diff}`;
}
function getBest(diff) {
const v = localStorage.getItem(bestKey(diff));
return v ? parseInt(v, 10) : null;
}
function setBestIfBetter(diff, timeSec) {
const prev = getBest(diff);
if (prev === null || timeSec < prev) {
localStorage.setItem(bestKey(diff), String(timeSec));
}
const best = getBest(diff);
if (bestSpan) bestSpan.textContent = best !== null ? `${best}s` : 'โ€”';
}
function startTimer() {
stopTimer();
startTime = Date.now();
timerId = setInterval(() => {
const elapsed = (Date.now() - startTime) / 1000;
if (timeSpan) timeSpan.textContent = formatTime(elapsed);
}, 250);
}
function stopTimer() {
if (timerId) {
clearInterval(timerId);
timerId = null;
}
}
function gridColumnsFor(totalCards) {
// Aproxima columnas en funciรณn del total para formar rejilla compacta
if (totalCards <= 12) return 4;
if (totalCards <= 16) return 4;
if (totalCards <= 24) return 6;
if (totalCards <= 30) return 6;
return 6;
}
function setupBoard() {
// Leer dificultad
const diff = (difficultySelect && difficultySelect.value) || 'normal';
const conf = DIFFICULTIES[diff] || DIFFICULTIES.normal;
totalPairs = conf.pairs;
// Reiniciar estado
matches = 0;
moves = 0;
firstCard = null;
secondCard = null;
lockBoard = false;
if (movesSpan) movesSpan.textContent = String(moves);
if (infoDiv) infoDiv.textContent = '';
// Construir mazo
const chosen = pickSymbols(totalPairs);
deck = [...chosen, ...chosen];
shuffle(deck);
// Render tablero accesible
gameBoard.textContent = '';
const totalCards = deck.length;
const cols = gridColumnsFor(totalCards);
gameBoard.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
deck.forEach((symbol, idx) => {
const cardEl = document.createElement('button');
cardEl.type = 'button';
cardEl.className = 'card';
cardEl.dataset.index = String(idx);
cardEl.dataset.symbol = symbol;
cardEl.textContent = '';
cardEl.setAttribute('role', 'gridcell');
cardEl.setAttribute('aria-label', 'Carta de memoria');
cardEl.setAttribute('aria-disabled', 'false');
cardEl.addEventListener('click', () => flipCard(cardEl));
cardEl.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
flipCard(cardEl);
}
});
gameBoard.appendChild(cardEl);
});
// Mostrar mejor tiempo
const best = getBest(diff);
if (bestSpan) bestSpan.textContent = best !== null ? `${best}s` : 'โ€”';
// Arrancar temporizador
startTimer();
// Foco inicial
const first = gameBoard.querySelector('.card');
if (first) first.focus();
}
function flipCard(cardEl) {
if (lockBoard) return;
if (cardEl.classList.contains('flipped') || cardEl.classList.contains('matched')) return;
// Voltear carta
cardEl.classList.add('flipped');
cardEl.textContent = cardEl.dataset.symbol;
cardEl.setAttribute('aria-disabled', 'true');
if (!firstCard) {
firstCard = cardEl;
return;
}
if (!secondCard && cardEl !== firstCard) {
secondCard = cardEl;
lockBoard = true;
moves++;
if (movesSpan) movesSpan.textContent = String(moves);
if (firstCard.dataset.symbol === secondCard.dataset.symbol) {
// Pareja encontrada
firstCard.classList.add('matched');
secondCard.classList.add('matched');
matches++;
setTimeout(() => {
resetSelection();
if (matches === totalPairs) {
// Fin de partida
const elapsed = Math.floor((Date.now() - startTime) / 1000);
if (infoDiv) infoDiv.textContent = `ยกFelicidades! ๐ŸŽ‰ Parejas: ${totalPairs} ยท Movimientos: ${moves} ยท Tiempo: ${elapsed}s`;
stopTimer();
const diff = (difficultySelect && difficultySelect.value) || 'normal';
setBestIfBetter(diff, elapsed);
}
}, 400);
} else {
// No es pareja, desvoltear tras un momento
setTimeout(() => {
unflip(firstCard);
unflip(secondCard);
resetSelection();
}, 700);
}
}
}
function unflip(el) {
if (!el) return;
el.classList.remove('flipped');
el.textContent = '';
el.setAttribute('aria-disabled', 'false');
}
function resetSelection() {
firstCard = null;
secondCard = null;
lockBoard = false;
}
// Listeners
resetBtn.addEventListener('click', () => {
stopTimer();
setupBoard();
});
if (difficultySelect) {
difficultySelect.addEventListener('change', () => {
stopTimer();
setupBoard();
});
}
// Init
setupBoard();