Mejoras y optimizaciones en general.

This commit is contained in:
2025-10-03 00:05:08 +02:00
parent bd76741bd2
commit d1a7442ffa
32 changed files with 3336 additions and 783 deletions

View File

@@ -1,3 +1,5 @@
'use strict';
// Lista completa con país y siglas según ISO 3166-1 alpha-2
const countryList = [
{ name: "España", code: "ES" }, { name: "Francia", code: "FR" }, { name: "Alemania", code: "DE" }, { name: "Italia", code: "IT" },
@@ -13,88 +15,119 @@ const countryList = [
function getFlagEmoji(code) {
// Las banderas se generan a partir de letras usando unicode, no por siglas textuales
// Solo funcionan para países con código alpha-2 y script latino
if (!code || code.length !== 2) return "";
if (!code || code.length !== 2) return '';
return String.fromCodePoint(
...code.toUpperCase().split("").map(c => 0x1F1E6 + c.charCodeAt(0) - 65)
...code.toUpperCase().split('').map(c => 0x1F1E6 + c.charCodeAt(0) - 65)
);
}
// Para elegir N países aleatorios distintos con bandera
// Elegir N países aleatorios distintos con bandera, sin mutar la lista original
function pickRandomCountries(list, n) {
const pool = list.slice(); // copia de seguridad
const chosen = [];
const used = {};
while (chosen.length < n && list.length > 0) {
let idx = Math.floor(Math.random() * list.length);
let c = list[idx];
let flag = getFlagEmoji(c.code);
if (flag && !used[c.code]) {
chosen.push({name: c.name, code: c.code, flag});
used[c.code] = true;
const used = new Set();
while (chosen.length < n && pool.length > 0) {
const idx = Math.floor(Math.random() * pool.length);
const c = pool[idx];
const flag = getFlagEmoji(c.code);
if (flag && !used.has(c.code)) {
chosen.push({ name: c.name, code: c.code, flag });
used.add(c.code);
}
list.splice(idx,1);
// Eliminar del pool para evitar repetir
pool.splice(idx, 1);
}
return chosen;
}
let flagsDiv = document.getElementById('flags');
let countriesDiv = document.getElementById('countries');
let statusDiv = document.getElementById('status');
let scoreSpan = document.getElementById('score-value');
let scoreTotal = document.getElementById('score-total');
let restartBtn = document.getElementById('restart-btn');
// Referencias DOM
const flagsDiv = document.getElementById('flags');
const countriesDiv = document.getElementById('countries');
const statusDiv = document.getElementById('status');
const scoreSpan = document.getElementById('score-value');
const scoreTotal = document.getElementById('score-total');
const restartBtn = document.getElementById('restart-btn');
const pairsSelect = document.getElementById('pairs-count');
let pairs = [], flags = [], countries = [], selectedFlag = null, selectedCountry = null, score = 0, totalPairs = 12;
// Estado
let pairs = [];
let flags = [];
let countries = [];
let selectedFlag = null;
let selectedCountry = null;
let score = 0;
let totalPairs = 12;
// Utilidades
function shuffle(arr) {
for (let i = arr.length-1; i>0; i--) {
const j = Math.floor(Math.random() * (i+1));
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
}
function startGame() {
let fullList = countryList.slice(); // copia
pairs = pickRandomCountries(fullList, totalPairs);
flags = pairs.map(o=>o);
countries = pairs.map(o=>o);
shuffle(flags); shuffle(countries);
score = 0;
scoreSpan.textContent = score;
scoreTotal.textContent = pairs.length;
selectedFlag = selectedCountry = null;
renderFlags();
renderCountries();
statusDiv.textContent = 'Empareja todas las banderas con su país';
function setStatus(msg) {
statusDiv.textContent = msg;
}
// Renderizado
function renderFlags() {
flagsDiv.innerHTML = '';
// Limpieza segura
flagsDiv.textContent = '';
flags.forEach((p, i) => {
const d = document.createElement('div');
const d = document.createElement('button');
d.type = 'button';
d.className = 'flag';
d.textContent = p.flag;
d.setAttribute('tabindex', 0);
d.onclick = () => selectFlag(i);
d.textContent = p.flag; // seguro (caracter unicode)
d.setAttribute('tabindex', '0');
d.setAttribute('role', 'listitem');
d.setAttribute('aria-label', `Bandera de ${p.name}`);
d.setAttribute('aria-disabled', p.matched ? 'true' : 'false');
d.setAttribute('aria-selected', selectedFlag === i ? 'true' : 'false');
if (p.matched) d.classList.add('matched');
if (selectedFlag === i) d.classList.add('selected');
d.addEventListener('click', () => selectFlag(i));
d.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
selectFlag(i);
}
});
flagsDiv.appendChild(d);
});
}
function renderCountries() {
countriesDiv.innerHTML = '';
countriesDiv.textContent = '';
countries.forEach((p, i) => {
const d = document.createElement('div');
const d = document.createElement('button');
d.type = 'button';
d.className = 'country';
d.textContent = p.name;
d.setAttribute('tabindex', 0);
d.onclick = () => selectCountry(i);
d.textContent = p.name; // seguro
d.setAttribute('tabindex', '0');
d.setAttribute('role', 'listitem');
d.setAttribute('aria-label', `País ${p.name}`);
d.setAttribute('aria-disabled', p.matched ? 'true' : 'false');
d.setAttribute('aria-selected', selectedCountry === i ? 'true' : 'false');
if (p.matched) d.classList.add('matched');
if (selectedCountry === i) d.classList.add('selected');
d.addEventListener('click', () => selectCountry(i));
d.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
selectCountry(i);
}
});
countriesDiv.appendChild(d);
});
}
// Lógica selección
function selectFlag(i) {
if (flags[i].matched) return;
selectedFlag = i;
@@ -110,32 +143,81 @@ function selectCountry(i) {
}
function checkMatch() {
if (selectedFlag === null || selectedCountry === null) return;
const flagObj = flags[selectedFlag];
const countryObj = countries[selectedCountry];
if (!flagObj || !countryObj) return;
if (flagObj.code === countryObj.code) {
flags[selectedFlag].matched = true;
countries[selectedCountry].matched = true;
score++;
scoreSpan.textContent = score;
statusDiv.textContent = '¡Correcto!';
scoreSpan.textContent = String(score);
setStatus('¡Correcto!');
renderFlags();
renderCountries();
if (score === pairs.length) {
statusDiv.textContent = '¡Has emparejado todas las banderas! 🎉';
setStatus('¡Has emparejado todas las banderas! 🎉');
}
} else {
statusDiv.textContent = 'No es correcto, intenta otra vez.';
setStatus('No es correcto, intenta otra vez.');
// Reset de selección tras breve pausa
setTimeout(() => {
statusDiv.textContent = '';
selectedFlag = selectedCountry = null;
setStatus('');
selectedFlag = null;
selectedCountry = null;
renderFlags();
renderCountries();
}, 850);
return;
}
// Reset selección tras acierto
selectedFlag = null;
selectedCountry = null;
}
restartBtn.onclick = startGame;
// Inicio/reinicio
function startGame() {
// Leer número de parejas desde selector, con límites
const desired = pairsSelect && Number(pairsSelect.value) ? Number(pairsSelect.value) : 12;
totalPairs = Math.max(4, Math.min(desired, countryList.length));
const fullList = countryList.slice(); // copia
pairs = pickRandomCountries(fullList, totalPairs);
// Si no se pudieron escoger las deseadas (por restricciones), ajustar total
if (pairs.length < totalPairs) {
totalPairs = pairs.length;
}
flags = pairs.map(o => ({ ...o }));
countries = pairs.map(o => ({ ...o }));
shuffle(flags);
shuffle(countries);
score = 0;
scoreSpan.textContent = String(score);
scoreTotal.textContent = String(pairs.length);
selectedFlag = null;
selectedCountry = null;
renderFlags();
renderCountries();
setStatus('Empareja todas las banderas con su país');
// Enfocar primera bandera para accesibilidad
const firstFlag = flagsDiv.querySelector('.flag');
if (firstFlag) firstFlag.focus();
}
// Listeners (evitar handlers inline)
restartBtn.addEventListener('click', startGame);
if (pairsSelect) {
pairsSelect.addEventListener('change', startGame);
}
// Init
startGame();