Añadidos juegos

This commit is contained in:
2025-08-21 23:42:55 +02:00
parent ec9c7d8d63
commit 90b2643d8d
46 changed files with 3936 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Tres en Raya vs Máquina</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Tres en Raya vs Máquina</h1>
<div id="board"></div>
<div id="status"></div>
<button id="restart-btn">Reiniciar</button>
<script src="script.js"></script>
</body>
</html>

View File

@@ -0,0 +1,122 @@
const boardDiv = document.getElementById('board');
const statusDiv = document.getElementById('status');
const restartBtn = document.getElementById('restart-btn');
let board, gameOver;
function initGame() {
board = Array(9).fill('');
gameOver = false;
statusDiv.textContent = "Tu turno (X)";
renderBoard();
}
function renderBoard() {
boardDiv.innerHTML = '';
board.forEach((cell, idx) => {
const cellDiv = document.createElement('div');
cellDiv.className = 'cell';
cellDiv.textContent = cell;
cellDiv.onclick = () => handlePlayerMove(idx);
boardDiv.appendChild(cellDiv);
});
}
function handlePlayerMove(idx) {
if (gameOver || board[idx] !== '') return;
board[idx] = 'X';
renderBoard();
if (checkWinner('X')) {
statusDiv.textContent = `¡Tú ganas! 🎉`;
gameOver = true;
return;
}
if (isDraw()) {
statusDiv.textContent = "¡Empate!";
gameOver = true;
return;
}
statusDiv.textContent = "Turno de la máquina (O)";
setTimeout(machineMove, 400);
}
function machineMove() {
if (gameOver) return;
let bestScore = -Infinity;
let move = null;
for (let i = 0; i < 9; i++) {
if (board[i] === '') {
board[i] = 'O';
let score = minimax(board, 0, false);
board[i] = '';
if (score > bestScore) {
bestScore = score;
move = i;
}
}
}
if (move !== null) board[move] = 'O';
renderBoard();
if (checkWinner('O')) {
statusDiv.textContent = `¡La máquina gana! 🤖`;
gameOver = true;
return;
}
if (isDraw()) {
statusDiv.textContent = "¡Empate!";
gameOver = true;
return;
}
statusDiv.textContent = "Tu turno (X)";
}
// Algoritmo Minimax
function minimax(newBoard, depth, isMaximizing) {
if (checkWinnerOnBoard(newBoard, 'O')) return 10 - depth;
if (checkWinnerOnBoard(newBoard, 'X')) return depth - 10;
if (newBoard.every(cell => cell !== '')) return 0;
if (isMaximizing) {
let bestScore = -Infinity;
for (let i = 0; i < 9; i++) {
if (newBoard[i] === '') {
newBoard[i] = 'O';
let score = minimax(newBoard, depth + 1, false);
newBoard[i] = '';
bestScore = Math.max(score, bestScore);
}
}
return bestScore;
} else {
let bestScore = Infinity;
for (let i = 0; i < 9; i++) {
if (newBoard[i] === '') {
newBoard[i] = 'X';
let score = minimax(newBoard, depth + 1, true);
newBoard[i] = '';
bestScore = Math.min(score, bestScore);
}
}
return bestScore;
}
}
function checkWinner(player) {
return checkWinnerOnBoard(board, player);
}
function checkWinnerOnBoard(b, player) {
const wins = [
[0,1,2],[3,4,5],[6,7,8],
[0,3,6],[1,4,7],[2,5,8],
[0,4,8],[2,4,6]
];
return wins.some(combo => combo.every(i => b[i] === player));
}
function isDraw() {
return board.every(cell => cell !== '') && !checkWinner('X') && !checkWinner('O');
}
restartBtn.onclick = initGame;
initGame();

View File

@@ -0,0 +1,122 @@
body {
background: #eef2f3;
font-family: Arial, sans-serif;
text-align: center;
color: #222;
margin: 0;
padding: 0;
}
h1 {
margin-top: 2rem;
color: #3498db;
font-size: 2.2rem;
line-height: 1.2;
}
#board {
display: grid;
grid-template-columns: repeat(3, minmax(60px, 26vw));
grid-template-rows: repeat(3, minmax(60px, 26vw));
gap: 0.7em;
justify-content: center;
margin: 2rem auto 1.2rem auto;
max-width: 95vw;
}
.cell {
width: 100%;
height: 100%;
background: #fff;
border-radius: 6px;
box-shadow: 0 2px 8px #bbb;
font-size: 10em;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s;
}
.cell:hover,
.cell:focus {
background: #f1fafe;
}
#status {
font-size: 1.1em;
min-height: 2em;
margin-bottom: 1em;
word-break: break-word;
}
#restart-btn {
background: #3498db;
color: #fff;
font-size: 1em;
border: none;
border-radius: 7px;
padding: 0.7em 2em;
cursor: pointer;
transition: background 0.2s;
margin-bottom: 2em;
}
#restart-btn:hover,
#restart-btn:focus {
background: #297fb8;
}
/* Celulares */
@media (max-width: 480px) {
h1 {
font-size: 1.3rem;
margin-top: 1rem;
}
#board {
grid-template-columns: repeat(3, minmax(38px, 30vw));
grid-template-rows: repeat(3, minmax(38px, 30vw));
gap: 0.4em;
margin: 1rem auto;
}
.cell {
font-size: 10em;
border-radius: 4px;
}
#status {
font-size: 1em;
min-height: 1.2em;
margin-bottom: 0.7em;
}
#restart-btn {
font-size: 0.95em;
padding: 0.5em 1.1em;
border-radius: 5px;
}
}
/* Tablets */
@media (max-width: 768px) {
h1 {
font-size: 1.6rem;
}
#board {
grid-template-columns: repeat(3, minmax(50px, 22vw));
grid-template-rows: repeat(3, minmax(50px, 22vw));
}
.cell {
font-size: 10em;
}
}
/* Pantallas grandes */
@media (min-width: 1200px) {
#board {
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(3, 100px);
gap: 1em;
}
.cell {
font-size: 3em;
}
}

15
3-en-raya/index.html Normal file
View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Tres en Raya</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Tres en Raya (Tic Tac Toe)</h1>
<div id="board"></div>
<div id="status"></div>
<button id="restart-btn">Reiniciar</button>
<script src="script.js"></script>
</body>
</html>

52
3-en-raya/script.js Normal file
View File

@@ -0,0 +1,52 @@
const boardDiv = document.getElementById('board');
const statusDiv = document.getElementById('status');
const restartBtn = document.getElementById('restart-btn');
let board, currentPlayer, gameOver;
function initGame() {
board = Array(9).fill('');
currentPlayer = 'X';
gameOver = false;
statusDiv.textContent = "Turno de " + currentPlayer;
renderBoard();
}
function renderBoard() {
boardDiv.innerHTML = '';
board.forEach((cell, idx) => {
const cellDiv = document.createElement('div');
cellDiv.className = 'cell';
cellDiv.textContent = cell;
cellDiv.onclick = () => handleCellClick(idx);
boardDiv.appendChild(cellDiv);
});
}
function handleCellClick(idx) {
if (gameOver || board[idx] !== '') return;
board[idx] = currentPlayer;
renderBoard();
if (checkWinner(currentPlayer)) {
statusDiv.textContent = `¡${currentPlayer} gana! 🎉`;
gameOver = true;
} else if (board.every(cell => cell !== '')) {
statusDiv.textContent = "¡Empate!";
gameOver = true;
} else {
currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
statusDiv.textContent = "Turno de " + currentPlayer;
}
}
function checkWinner(player) {
const winCases = [
[0,1,2],[3,4,5],[6,7,8], // Filas
[0,3,6],[1,4,7],[2,5,8], // Columnas
[0,4,8],[2,4,6] // Diagonales
];
return winCases.some(combo => combo.every(i => board[i] === player));
}
restartBtn.onclick = initGame;
initGame();

121
3-en-raya/styles.css Normal file
View File

@@ -0,0 +1,121 @@
body {
background: #eef2f3;
font-family: Arial, sans-serif;
text-align: center;
color: #222;
margin: 0;
padding: 0;
}
h1 {
margin-top: 2rem;
color: #3498db;
font-size: 2.5rem;
line-height: 1.2;
}
/* Board responsive styles */
#board {
display: grid;
grid-template-columns: repeat(3, minmax(60px, 22vw));
grid-template-rows: repeat(3, minmax(60px, 22vw));
gap: 0.8em;
justify-content: center;
margin: 2rem auto 1.5rem auto;
max-width: 95vw;
}
/* Cell responsive styles */
.cell {
width: 100%;
height: 100%;
background: #fff;
border-radius: 0.5em;
box-shadow: 0 2px 8px #bbb;
font-size: 8em;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s;
}
.cell:hover, .cell:focus {
background: #f1fafe;
}
#status {
font-size: 1.15em;
min-height: 2em;
margin-bottom: 1em;
word-break: break-word;
}
#restart-btn {
background: #3498db;
color: #fff;
font-size: 1em;
border: none;
border-radius: 0.7em;
padding: 0.7em 2em;
cursor: pointer;
transition: background 0.2s;
}
#restart-btn:hover,
#restart-btn:focus {
background: #297fb8;
}
/* SMALL DEVICES (phones) */
@media (max-width: 480px) {
h1 {
font-size: 1.5rem;
margin-top: 1rem;
}
#board {
grid-template-columns: repeat(3, minmax(40px, 28vw));
grid-template-rows: repeat(3, minmax(40px, 28vw));
gap: 0.5em;
margin: 1rem auto;
}
.cell {
font-size: 10em;
border-radius: 0.35em;
}
#status {
font-size: 1em;
min-height: 1.2em;
margin-bottom: 0.7em;
}
#restart-btn {
font-size: 0.95em;
padding: 0.6em 1.2em;
}
}
/* TABLETS */
@media (max-width: 768px) {
h1 {
font-size: 2rem;
}
#board {
grid-template-columns: repeat(3, minmax(50px, 22vw));
grid-template-rows: repeat(3, minmax(50px, 22vw));
}
.cell {
font-size: 10em;
}
}
/* BIG SCREENS */
@media (min-width: 1200px) {
#board {
grid-template-columns: repeat(3, 110px);
grid-template-rows: repeat(3, 110px);
gap: 1em;
}
.cell {
font-size: 3em;
}
}

15
4-en-raya/index.html Normal file
View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>4 en Raya vs Máquina</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>4 en Raya vs Máquina</h1>
<div id="board"></div>
<div id="status"></div>
<button id="restart-btn">Reiniciar</button>
<script src="script.js"></script>
</body>
</html>

151
4-en-raya/script.js Normal file
View File

@@ -0,0 +1,151 @@
const ROWS = 6;
const COLS = 7;
const HUMAN = '🟡';
const AI = '❌';
const boardDiv = document.getElementById('board');
const statusDiv = document.getElementById('status');
const restartBtn = document.getElementById('restart-btn');
let board, gameOver, currentPlayer;
function initGame() {
board = Array.from({length: ROWS}, () => Array(COLS).fill(''));
gameOver = false;
currentPlayer = HUMAN;
statusDiv.textContent = "Tu turno (🟡)";
renderBoard();
}
function renderBoard() {
boardDiv.innerHTML = '';
// Creamos todas las celdas primero
for (let row = 0; row < ROWS; row++) {
for (let col = 0; col < COLS; col++) {
const cellDiv = document.createElement('div');
cellDiv.className = 'cell';
cellDiv.textContent = board[row][col];
// Si es turno humano y no está terminado el juego
if (!gameOver && currentPlayer === HUMAN && board[row][col] === '') {
let lowestRow = getLowestEmptyRow(col);
if (lowestRow === row) {
cellDiv.style.cursor = "pointer";
}
}
// El evento click siempre llama para la columna
cellDiv.onclick = () => {
if (!gameOver && currentPlayer === HUMAN) {
playerMove(col);
}
};
boardDiv.appendChild(cellDiv);
}
}
}
function getLowestEmptyRow(col) {
for (let r = ROWS - 1; r >= 0; r--) {
if (board[r][col] === '') return r;
}
return -1;
}
function playerMove(col) {
if (gameOver || currentPlayer !== HUMAN) return;
let row = getLowestEmptyRow(col);
if (row === -1) return; // columna llena
board[row][col] = HUMAN;
renderBoard();
if (checkWinner(HUMAN)) {
statusDiv.textContent = "¡Has ganado! 🎉";
gameOver = true;
return;
}
if (isFull()) {
statusDiv.textContent = "¡Empate!";
gameOver = true;
return;
}
currentPlayer = AI;
statusDiv.textContent = "Turno de la máquina (❌)";
setTimeout(machineMove, 450);
}
function machineMove() {
if (gameOver || currentPlayer !== AI) return;
// IA sencilla: elige columna aleatoria libre
let validCols = [];
for (let c = 0; c < COLS; c++) {
if (board[0][c] === '') validCols.push(c);
}
if (validCols.length === 0) return;
let col = validCols[Math.floor(Math.random() * validCols.length)];
let row = getLowestEmptyRow(col);
board[row][col] = AI;
renderBoard();
if (checkWinner(AI)) {
statusDiv.textContent = "¡La máquina gana! 🤖";
gameOver = true;
return;
}
if (isFull()) {
statusDiv.textContent = "¡Empate!";
gameOver = true;
return;
}
currentPlayer = HUMAN;
statusDiv.textContent = "Tu turno (🟡)";
}
function isFull() {
return board[0].every(cell => cell !== '');
}
function checkWinner(player) {
// Horizontal
for (let r = 0; r < ROWS; r++) {
for (let c = 0; c < COLS - 3; c++) {
if (
board[r][c] === player &&
board[r][c + 1] === player &&
board[r][c + 2] === player &&
board[r][c + 3] === player
) return true;
}
}
// Vertical
for (let c = 0; c < COLS; c++) {
for (let r = 0; r < ROWS - 3; r++) {
if (
board[r][c] === player &&
board[r + 1][c] === player &&
board[r + 2][c] === player &&
board[r + 3][c] === player
) return true;
}
}
// Diagonal descendente \
for (let r = 0; r < ROWS - 3; r++) {
for (let c = 0; c < COLS - 3; c++) {
if (
board[r][c] === player &&
board[r + 1][c + 1] === player &&
board[r + 2][c + 2] === player &&
board[r + 3][c + 3] === player
) return true;
}
}
// Diagonal ascendente /
for (let r = 3; r < ROWS; r++) {
for (let c = 0; c < COLS - 3; c++) {
if (
board[r][c] === player &&
board[r - 1][c + 1] === player &&
board[r - 2][c + 2] === player &&
board[r - 3][c + 3] === player
) return true;
}
}
return false;
}
restartBtn.onclick = initGame;
initGame();

132
4-en-raya/styles.css Normal file
View File

@@ -0,0 +1,132 @@
body {
background: #eef2f3;
font-family: Arial, sans-serif;
text-align: center;
color: #222;
margin: 0;
padding: 0;
}
h1 {
margin-top: 2rem;
color: #d7263d;
font-size: 2.1rem;
line-height: 1.2;
}
/* Tablero responsive */
#board {
display: grid;
grid-template-columns: repeat(7, minmax(35px, 11vw));
grid-template-rows: repeat(6, minmax(35px, 11vw));
gap: 0.6em;
justify-content: center;
margin: 2rem auto 1.3rem auto;
background: #1565c0;
padding: 1.2em 0;
border-radius: 15px;
max-width: 100vw;
box-sizing: border-box;
}
/* Celda responsive */
.cell {
width: 100%;
height: 100%;
background: #fff;
border-radius: 50%;
box-shadow: 0 2px 8px #bbb;
font-size: 4.2em;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.15s;
}
.cell:hover,
.cell:focus {
background: #ffe082;
}
#status {
font-size: 1.1em;
min-height: 2em;
margin-bottom: 1em;
word-break: break-word;
}
#restart-btn {
background: #d7263d;
color: #fff;
font-size: 1em;
border: none;
border-radius: 7px;
padding: 0.7em 2em;
cursor: pointer;
transition: background 0.2s;
margin-bottom: 2em;
}
#restart-btn:hover,
#restart-btn:focus {
background: #920c22;
}
/* Celulares */
@media (max-width: 520px) {
h1 {
font-size: 1.2rem;
margin-top: 1rem;
}
#board {
grid-template-columns: repeat(7, minmax(22px, 12vw));
grid-template-rows: repeat(6, minmax(22px, 12vw));
gap: 0.25em;
padding: 0.5em 0;
border-radius: 10px;
margin: 1rem auto 1rem auto;
max-width: 100vw;
}
.cell {
font-size: 4.2em;
border-radius: 50%;
}
#status {
font-size: 0.97em;
min-height: 1.2em;
margin-bottom: 0.8em;
}
#restart-btn {
font-size: 0.9em;
padding: 0.45em 1em;
border-radius: 5px;
margin-bottom: 1em;
}
}
/* Tablets */
@media (max-width: 900px) {
h1 {
font-size: 1.4rem;
}
#board {
grid-template-columns: repeat(7, minmax(28px, 11vw));
grid-template-rows: repeat(6, minmax(28px, 11vw));
}
.cell {
font-size: 4.2em;
}
}
/* Pantallas grandes */
@media (min-width: 1200px) {
#board {
grid-template-columns: repeat(7, 64px);
grid-template-rows: repeat(6, 64px);
gap: 1em;
}
.cell {
font-size: 2em;
}
}

21
adivina/index.html Normal file
View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Adivina el Número</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>¡Adivina el número!</h1>
<div id="game-box">
<p>Estoy pensando en un número entre 1 y 100.</p>
<p>¿Puedes adivinarlo en 7 intentos?</p>
<input id="guess-input" type="number" min="1" max="100" placeholder="Tu número" />
<button id="guess-btn">Adivinar</button>
<button id="restart-btn" class="hidden">Jugar de nuevo</button>
<div id="info"></div>
<div id="attempts">Intentos restantes: <span id="attempts-left">7</span></div>
</div>
<script src="script.js"></script>
</body>
</html>

55
adivina/script.js Normal file
View File

@@ -0,0 +1,55 @@
let randomNumber;
let attemptsLeft = 7;
const guessInput = document.getElementById('guess-input');
const guessBtn = document.getElementById('guess-btn');
const restartBtn = document.getElementById('restart-btn');
const infoDiv = document.getElementById('info');
const attemptsSpan = document.getElementById('attempts-left');
function startGame() {
randomNumber = Math.floor(Math.random() * 100) + 1;
attemptsLeft = 7;
attemptsSpan.textContent = attemptsLeft;
infoDiv.textContent = '';
guessInput.value = '';
guessInput.disabled = false;
guessBtn.disabled = false;
restartBtn.classList.add('hidden');
}
function checkGuess() {
const guess = Number(guessInput.value);
if (guess < 1 || guess > 100 || isNaN(guess)) {
infoDiv.textContent = "Por favor ingresa un número válido entre 1 y 100.";
return;
}
attemptsLeft--;
attemptsSpan.textContent = attemptsLeft;
if (guess === randomNumber) {
infoDiv.textContent = "¡Correcto! 🎉 Has adivinado el número.";
endGame(true);
} else if (attemptsLeft === 0) {
infoDiv.textContent = `¡Oh no! Te quedaste sin intentos. El número era ${randomNumber}.`;
endGame(false);
} else if (guess < randomNumber) {
infoDiv.textContent = "Demasiado bajo. Intenta nuevamente.";
} else {
infoDiv.textContent = "Demasiado alto. Intenta nuevamente.";
}
}
function endGame(won) {
guessInput.disabled = true;
guessBtn.disabled = true;
restartBtn.classList.remove('hidden');
}
guessBtn.onclick = checkGuess;
restartBtn.onclick = startGame;
guessInput.onkeydown = (e) => { if (e.key === "Enter") checkGuess(); };
startGame();

68
adivina/styles.css Normal file
View File

@@ -0,0 +1,68 @@
body {
background: #f7f7f7;
font-family: Arial, sans-serif;
text-align: center;
}
h1 {
margin-top: 40px;
color: #222;
}
#game-box {
background: white;
width: 350px;
margin: 50px auto;
padding: 30px 20px;
border-radius: 12px;
box-shadow: 0 0 8px #bbb;
}
input[type="number"] {
width: 100px;
font-size: 1.1em;
padding: 6px;
margin-right: 12px;
margin-bottom: 10px;
}
button {
font-size: 1em;
padding: 7px 20px;
border: none;
border-radius: 5px;
background: #2196f3;
color: white;
cursor: pointer;
margin-bottom: 10px;
}
button:hover {
background: #1769aa;
}
#info {
font-size: 1.2em;
margin-top: 14px;
min-height: 2em;
}
#attempts {
margin-top: 8px;
color: #888;
}
.hidden {
display: none;
}
@media (min-width: 728px) {
body,
#game-box,
input[type="number"],
button,
#info,
#attempts {
width: 95%;
font-size: 130%;
}
}

15
buscaminas/index.html Normal file
View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Buscaminas</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Buscaminas</h1>
<div id="status"></div>
<div id="board"></div>
<button id="restart-btn">Reiniciar</button>
<script src="script.js"></script>
</body>
</html>

134
buscaminas/script.js Normal file
View File

@@ -0,0 +1,134 @@
const SIZE = 10;
const MINES = 15;
const boardDiv = document.getElementById('board');
const statusDiv = document.getElementById('status');
const restartBtn = document.getElementById('restart-btn');
let board, revealed, flagged, gameOver, cellsRevealed;
function initGame() {
board = Array.from({length: SIZE}, () => Array(SIZE).fill(0));
revealed = Array.from({length: SIZE}, () => Array(SIZE).fill(false));
flagged = Array.from({length: SIZE}, () => Array(SIZE).fill(false));
gameOver = false;
cellsRevealed = 0;
statusDiv.textContent = 'Haz clic izquierdo para revelar, clic derecho para marcar.';
placeMines();
calculateNumbers();
renderBoard();
}
function placeMines() {
let placed = 0;
while (placed < MINES) {
const r = Math.floor(Math.random() * SIZE);
const c = Math.floor(Math.random() * SIZE);
if (board[r][c] !== 'M') {
board[r][c] = 'M';
placed++;
}
}
}
function calculateNumbers() {
for (let r = 0; r < SIZE; r++) {
for (let c = 0; c < SIZE; c++) {
if (board[r][c] === 'M') continue;
let count = 0;
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
let nr = r+i, nc = c+j;
if (nr >= 0 && nr < SIZE && nc >=0 && nc < SIZE && board[nr][nc] === 'M') count++;
}
}
board[r][c] = count;
}
}
}
function renderBoard() {
boardDiv.innerHTML = '';
for (let r = 0; r < SIZE; r++) {
for (let c = 0; c < SIZE; c++) {
const cell = document.createElement('div');
cell.classList.add('cell');
if (revealed[r][c]) {
cell.classList.add('revealed');
if (board[r][c] === 'M') {
cell.classList.add('mine');
cell.textContent = '💣';
} else if (board[r][c] > 0) {
cell.textContent = board[r][c];
} else {
cell.textContent = '';
}
} else if (flagged[r][c]) {
cell.classList.add('flag');
cell.textContent = '🚩';
} else {
cell.textContent = '';
}
// Click izquierdo
cell.onmousedown = (e) => {
if (gameOver) return;
if (e.button === 0) revealCell(r, c);
else if (e.button === 2) toggleFlag(r, c);
};
cell.oncontextmenu = (e) => e.preventDefault();
boardDiv.appendChild(cell);
}
}
}
function revealCell(r, c) {
if (revealed[r][c] || flagged[r][c]) return;
revealed[r][c] = true;
cellsRevealed++;
if (board[r][c] === 'M') {
endGame(false);
renderBoard();
return;
} else if (board[r][c] === 0) {
// Revela recursivo
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
let nr = r+i, nc = c+j;
if (nr >= 0 && nr < SIZE && nc >= 0 && nc < SIZE) {
if (!revealed[nr][nc]) revealCell(nr, nc);
}
}
}
}
checkWin();
renderBoard();
}
function toggleFlag(r, c) {
if (revealed[r][c]) return;
flagged[r][c] = !flagged[r][c];
renderBoard();
}
function checkWin() {
if (cellsRevealed === SIZE*SIZE - MINES) {
endGame(true);
}
}
function endGame(won) {
gameOver = true;
// Revela todas las minas si perdiste
if (!won) {
for (let r = 0; r < SIZE; r++) {
for (let c = 0; c < SIZE; c++) {
if (board[r][c] === 'M') revealed[r][c] = true;
}
}
statusDiv.textContent = "¡BOOM! Has perdido 💣";
} else {
statusDiv.textContent = "¡Felicidades! Has ganado 🎉";
}
}
restartBtn.onclick = initGame;
initGame();

149
buscaminas/styles.css Normal file
View File

@@ -0,0 +1,149 @@
/* ===== Diseño base ===== */
html {
box-sizing: border-box;
font-size: 16px;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
background: #f2f2f2;
font-family: Arial, sans-serif;
color: #222;
text-align: center;
margin: 0;
min-height: 100vh;
}
/* ===== Encabezado ===== */
h1 {
color: #d7263d;
margin-top: 2rem;
font-size: clamp(2rem, 4vw, 2.5rem);
}
/* ===== Tablero ===== */
#board {
display: grid;
grid-template-columns: repeat(10, 1fr);
gap: 0.7vw;
margin: 2.5rem auto 1.7rem auto;
background: #1565c0;
padding: 2vw;
border-radius: 1rem;
width: 100%;
max-width: 98vw;
}
/* Una celda */
.cell {
aspect-ratio: 1 / 1;
width: 100%;
background: #fff;
border-radius: 0.7rem;
box-shadow: 0 2px 8px #bbb;
font-size: clamp(1rem, 2.5vw, 1.4rem);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.15s;
user-select: none;
}
.cell.revealed { background: #bbdefb; cursor: default; }
.cell.mine { background: #d7263d; color: #fff; }
.cell.flag { background: #ffe082; color: #d7263d; }
/* ===== Estado y botón ===== */
#status {
font-size: 1.1rem;
min-height: 2em;
margin: 0.5em 0 0.7em 0;
}
#restart-btn {
background: #d7263d;
color: #fff;
font-size: clamp(1rem, 2vw, 1.3rem);
border: none;
border-radius: 0.7rem;
padding: 0.5em 2em;
cursor: pointer;
transition: background 0.2s;
margin-bottom: 1.2em;
margin-top: 1em;
}
#restart-btn:hover { background: #920c22; }
/* ========================================
===== Pantallas pequeñas (<720px) ====
======================================== */
@media (max-width: 720px) {
h1 {
margin-top: 3vw;
font-size: clamp(1.3rem, 6vw, 2.1rem);
}
#board {
/* Cambia columnas si el tablero lo permite */
grid-template-columns: repeat(6, 1fr);
padding: 4vw;
border-radius: 4vw;
gap: 2.8vw;
margin-top: 4vw;
margin-bottom: 6vw;
max-width: 99vw;
}
.cell {
font-size: clamp(1.2rem, 8vw, 2.5rem);
border-radius: 3vw;
}
#restart-btn {
font-size: clamp(1rem, 8vw, 2rem);
padding: 3vw 12vw;
border-radius: 3vw;
margin-bottom: 6vw;
margin-top: 3vw;
}
#status {
font-size: clamp(1rem, 7vw, 1.7rem);
}
}
/* ========================================
===== Pantallas muy pequeñas (<400px) ==
======================================== */
@media (max-width: 400px) {
#board {
grid-template-columns: repeat(4, 1fr);
padding: 2vw;
gap: 2vw;
border-radius: 5vw;
}
.cell { font-size: clamp(1rem, 10vw, 2.2rem); }
#restart-btn {
padding: 3vw 4vw;
font-size: clamp(0.9rem, 7vw, 1.5rem);
border-radius: 5vw;
}
}
/* ========================================
===== Pantallas grandes (>1200px) ====
======================================== */
@media (min-width: 1200px) {
#board {
max-width: 700px;
padding: 2rem;
gap: 0.7rem;
}
h1 {
font-size: clamp(2.5rem, 3vw, 3.5rem);
}
.cell {
font-size: clamp(1.3rem, 2vw, 2rem);
}
}
/* :::::::::::::::::::::::::::::: */

269
index.html Normal file
View File

@@ -0,0 +1,269 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>FerMdez - Games</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Montserrat:700,400&display=swap" rel="stylesheet">
<style>
body {
background: linear-gradient(120deg, #9cebfc 0%, #e9defa 100%);
font-family: 'Montserrat', Arial, sans-serif;
color: #242424;
margin: 0;
min-height: 100vh;
}
header {
text-align: center;
padding: 55px 0 22px 0;
}
h1 {
font-size: 2.4em;
font-weight: 700;
margin-bottom: 7px;
color: #3f3d56;
letter-spacing: 2px;
text-shadow: 0 1px 11px #b7d4ed73;
}
p {
color: #444;
font-size: 1.15em;
margin: 0 0 28px 0;
}
main {
max-width: 1100px;
margin: 0 auto;
padding: 20px 16px 60px 16px;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(255px, 1fr));
gap: 32px;
}
.game-card {
background: rgba(255,255,255,0.85);
border-radius: 18px;
box-shadow: 0 6px 32px rgba(51, 101, 160, 0.11), 0 0 0 1.5px #e6e6f9;
padding: 0 0 20px 0;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
transition: box-shadow 0.22s, transform 0.16s;
will-change: transform;
min-height: 290px;
backdrop-filter: blur(3px);
border: 1.5px solid #f4f1fe;
overflow: hidden;
}
.game-card:hover {
box-shadow: 0 12px 32px 0 rgba(39,85,151,0.13), 0 0 0 2.5px #6cb2ed;
transform: translateY(-7px) scale(1.03);
z-index: 2;
cursor: pointer;
}
.game-icon {
font-size: 2.7em;
margin-top: 23px;
margin-bottom: 7px;
filter: grayscale(0.2) drop-shadow(0 3px 6px #caf0f8c6);
pointer-events: none;
}
.game-title {
font-size: 1.15em;
font-weight: 700;
color: #234075;
margin-bottom: 7px;
text-align: center;
letter-spacing: 0.5px;
}
.game-desc {
font-size: 1em;
color: #454545;
margin-bottom: 28px;
margin-top: 0;
text-align: center;
flex-grow: 1;
}
.play-btn {
background: linear-gradient(108deg,#3d5afe 55%,#228be6 110%);
color: #fff;
border: none;
border-radius: 44px;
padding: 13px 0;
font-size: 1.05em;
font-weight: 700;
width: 88%;
box-shadow: 0 2px 12px #b7d4ed99;
cursor: pointer;
transition: background 0.22s, box-shadow 0.16s, transform 0.13s;
text-transform: uppercase;
letter-spacing: 1.1px;
margin-bottom: 0;
margin-top: auto;
outline: none;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
border: 2px solid #dbeafe;
animation: btn-pop 1.1s cubic-bezier(.4,.28,.32,.99) both;
}
.play-btn:hover, .play-btn:focus {
background: linear-gradient(108deg,#0a2459 55%,#195987 110%);
transform: scale(1.055);
box-shadow: 0 7px 22px #3d5afe1a;
border-color: #3d5afe;
outline: none;
}
.play-btn svg {
margin-left: 5px;
width: 1.4em;
height: 1.3em;
vertical-align: middle;
fill: #fff;
pointer-events: none;
}
@media (max-width: 900px) {
.grid { gap: 23px; }
.game-card { min-height: 240px; }
header { padding-bottom: 6px; }
main { padding: 10px 3vw 35px 3vw;}
}
@media (max-width: 660px) {
h1 { font-size: 1.45em; }
.game-title { font-size: 1em;}
.game-desc { font-size: 0.98em;}
.game-card { min-height: 180px;}
}
@keyframes btn-pop { 0%{transform:scale(.98);} 100%{transform:scale(1);} }
</style>
</head>
<body>
<header>
<h1>Fermdez - Juegos</h1>
<p>¡Prueba estos mini juegos creados por <a href="https://fermdez.net/" target="_blank">Fernando Méndez</a>!<br />Elige, juega y supera tus récords.</p>
</header>
<main>
<div class="grid">
<div class="game-card">
<div class="game-icon">🏓</div>
<div class="game-title">Pong Clásico</div>
<div class="game-desc">Juega al clásico Pong contra la máquina usando las flechas.</div>
<a class="play-btn" href="pong/" target="_blank">Jugar
<svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
<div class="game-card">
<div class="game-icon">🔲</div>
<div class="game-title">Simon Dice</div>
<div class="game-desc">Recuerda y repite la secuencia de colores para avanzar de nivel.</div>
<a class="play-btn" href="simon-dice/" target="_blank">Jugar
<svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
<div class="game-card">
<div class="game-icon">🧮</div>
<div class="game-title">Buscaminas</div>
<div class="game-desc">Marca las minas y evita explotarlas en el tablero.</div>
<a class="play-btn" href="buscaminas/" target="_blank">Jugar
<svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
<div class="game-card">
<div class="game-icon">🃏</div>
<div class="game-title">Juego de Memoria</div>
<div class="game-desc">Descubre todas las parejas de cartas y ejercita tu memoria.</div>
<a class="play-btn" href="memoria/" target="_blank">Jugar
<svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
<div class="game-card">
<div class="game-icon">🃏</div>
<div class="game-title">Juego de Memoria Avanzado</div>
<div class="game-desc">Descubre todas las parejas de cartas y ejercita tu memoria.</div>
<a class="play-btn" href="memoria-v2/" target="_blank">Jugar
<svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
<div class="game-card">
<div class="game-icon">⭕❌</div>
<div class="game-title">3 en Raya</div>
<div class="game-desc">Tres en línea clásico: reta a un amigo.</div>
<a class="play-btn" href="3-en-raya/" target="_blank">Jugar
<svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
<div class="game-card">
<div class="game-icon">⭕❌</div>
<div class="game-title">3 en Raya vs Máquina</div>
<div class="game-desc">Tres en línea clásico: reta a una IA invencible.</div>
<a class="play-btn" href="3-en-raya-computer/" target="_blank">Jugar
<svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
<div class="game-card">
<div class="game-icon">🔵🔴🔵🔴</div>
<div class="game-title">4 en Raya vs Máquina</div>
<div class="game-desc">Conecta 4 fichas antes que la máquina en este clásico de estrategia.</div>
<a class="play-btn" href="4-en-raya/" target="_blank">Jugar
<svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
<div class="game-card">
<div class="game-icon">🧩</div>
<div class="game-title">Puzzle de Números</div>
<div class="game-desc">Resuelve el clásico puzzle de 15 piezas deslizantes.</div>
<a class="play-btn" href="puzle-numeros/" target="_blank">Jugar
<svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
<div class="game-card">
<div class="game-icon">🔢</div>
<div class="game-title">Adivina el Número</div>
<div class="game-desc">Resuelve el número que ha pensado la máquina en menos de 7 intentos.</div>
<a class="play-btn" href="puzle-numeros/" target="_blank">Jugar
<svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
<div class="game-card">
<div class="game-icon">🐍</div>
<div class="game-title">Snake Game</div>
<div class="game-desc">Haz crecer la serpiente comiendo puntos, ¡no choques con la pared!</div>
<a class="play-btn" href="snake/index.html" target="_blank">Jugar
<svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
<div class="game-card">
<div class="game-icon">🧱</div>
<div class="game-title">Rompe Ladrillos</div>
<div class="game-desc">Rompe todos los ladrillos controlando la pala y la bola.</div>
<a class="play-btn" href="ladrillos" target="_blank">Jugar
<svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
<div class="game-card">
<div class="game-icon">⏱️</div>
<div class="game-title">Carrera de Reacción</div>
<div class="game-desc">Haz clic cuando la pantalla se ponga verde, ¡mide tu tiempo de reacción!</div>
<a class="play-btn" href="reflejos/" target="_blank">Jugar
<svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
<div class="game-card">
<div class="game-icon">🦦</div>
<div class="game-title">Atrapa el Topo</div>
<div class="game-desc">Haz clic en el topo cuando aparezca y suma puntos.</div>
<a class="play-btn" href="topo/" target="_blank">Jugar
<svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
</div>
</main>
</body>
</html>

17
ladrillos/index.html Normal file
View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Breakout</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Rompe Ladrillos</h1>
<p>Usa el ratón para mover el bloque. Rompe todos los ladrillos para ganar.</p>
<canvas id="gameCanvas" width="540" height="480"></canvas>
<div id="score">Puntuación: <span id="score-value">0</span></div>
<button id="restart-btn">Reiniciar</button>
<div id="game-over-message"></div>
<script src="script.js"></script>
</body>
</html>

147
ladrillos/script.js Normal file
View File

@@ -0,0 +1,147 @@
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreSpan = document.getElementById('score-value');
const restartBtn = document.getElementById('restart-btn');
const gameOverDiv = document.getElementById('game-over-message');
const ballRadius = 8;
let x, y, dx, dy;
const paddleHeight = 12, paddleWidth = 75;
let paddleX;
const brickRowCount = 5, brickColumnCount = 7;
const brickWidth = 60, brickHeight = 20, brickPadding = 10, brickOffsetTop = 30, brickOffsetLeft = 30;
let bricks = [];
let score, gameOver;
function setupBricks() {
bricks = [];
for(let c=0; c<brickColumnCount; c++) {
bricks[c] = [];
for(let r=0; r<brickRowCount; r++) {
bricks[c][r] = { x:0, y:0, status:1 };
}
}
}
function startGame() {
x = canvas.width/2;
y = canvas.height-30;
dx = 3;
dy = -3;
paddleX = (canvas.width-paddleWidth)/2;
score = 0;
scoreSpan.textContent = score;
gameOver = false;
gameOverDiv.textContent = '';
setupBricks();
document.addEventListener("mousemove", mouseMoveHandler);
draw();
}
function mouseMoveHandler(e) {
const rect = canvas.getBoundingClientRect();
let relativeX = e.clientX - rect.left;
if(relativeX > 0 && relativeX < canvas.width) {
paddleX = relativeX - paddleWidth/2;
if(paddleX < 0) paddleX = 0;
if(paddleX + paddleWidth > canvas.width) paddleX = canvas.width - paddleWidth;
}
}
function collisionDetection() {
for(let c=0; c<brickColumnCount; c++) {
for(let r=0; r<brickRowCount; r++) {
const b = bricks[c][r];
if(b.status == 1) {
if(x > b.x && x < b.x+brickWidth && y > b.y && y < b.y+brickHeight) {
dy = -dy;
b.status = 0;
score++;
scoreSpan.textContent = score;
if(score == brickRowCount*brickColumnCount) {
gameOver = true;
gameOverDiv.textContent = "¡Ganaste! 🏆";
return;
}
}
}
}
}
}
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI*2);
ctx.fillStyle = "#f9d923";
ctx.fill();
ctx.closePath();
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, canvas.height-paddleHeight-5, paddleWidth, paddleHeight);
ctx.fillStyle = "#e94560";
ctx.fill();
ctx.closePath();
}
function drawBricks() {
for(let c=0; c<brickColumnCount; c++) {
for(let r=0; r<brickRowCount; r++) {
if(bricks[c][r].status == 1) {
const brickX = (c*(brickWidth+brickPadding))+brickOffsetLeft;
const brickY = (r*(brickHeight+brickPadding))+brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
ctx.fillStyle = "#393e46";
ctx.strokeStyle = "#f9d923";
ctx.fill();
ctx.stroke();
ctx.closePath();
}
}
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBricks();
drawBall();
drawPaddle();
collisionDetection();
// Rebote en las paredes
if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) {
dx = -dx;
}
if(y + dy < ballRadius) {
dy = -dy;
} else if(y + dy > canvas.height-ballRadius-paddleHeight-5) {
// Rebote en la paleta
if(x > paddleX && x < paddleX + paddleWidth) {
dy = -dy;
} else if (y + dy > canvas.height-ballRadius) {
// GAME OVER
gameOver = true;
gameOverDiv.textContent = "¡Game Over! 😢";
return;
}
}
x += dx;
y += dy;
if(!gameOver) {
requestAnimationFrame(draw);
}
}
restartBtn.onclick = function() {
document.removeEventListener("mousemove", mouseMoveHandler);
startGame();
};
// Inicio
startGame();

115
ladrillos/styles.css Normal file
View File

@@ -0,0 +1,115 @@
/* Reset y box-sizing global */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* Variables de colores y métricas */
:root {
/* Colores */
--color-bg: #1a1a2e;
--color-text: #e7e7de;
--color-accent: #e94560;
--color-highlight: #f9d923;
--color-canvas-bg: #0f3460;
--color-shadow: rgba(37, 34, 43, 0.8);
/* Sombras y radios */
--radius: 8px;
--shadow: 0 0 16px var(--color-shadow);
/* Tipografía */
--font: 'Arial', sans-serif;
}
/* BODY: centrar y padding responsive */
body {
background: var(--color-bg);
color: var(--color-text);
font-family: var(--font);
font-size: 100%; /* 1rem = 16px base */
line-height: 1.4;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
padding: clamp(1rem, 4vw, 2rem);
}
/* TITULAR */
h1 {
margin-top: clamp(1.5rem, 6vw, 3rem);
color: var(--color-accent);
font-size: clamp(1.75rem, 8vw, 2.75rem);
line-height: 1.2;
}
/* PÁRRAFOS */
p {
color: #fff;
font-size: clamp(0.9rem, 2.5vw, 1.2rem);
margin: clamp(0.5rem, 2vw, 1rem) 0;
}
/* CANVAS: ancho fluido, altura automática para mantener proporción */
canvas {
width: 100%;
max-width: 640px;
aspect-ratio: 640 / 480;
height: auto;
background: var(--color-canvas-bg);
display: block;
margin: clamp(1rem, 4vw, 2rem) auto;
border-radius: var(--radius);
box-shadow: var(--shadow);
}
/* PUNTUACIÓN */
#score {
color: var(--color-highlight);
font-size: clamp(1rem, 2.5vw, 1.5rem);
margin-top: clamp(0.5rem, 2vw, 1rem);
}
/* MENSAJE DE GAME OVER */
#game-over-message {
color: var(--color-accent);
font-size: clamp(1.2rem, 4vw, 1.8rem);
margin-top: clamp(0.75rem, 3vw, 1.5rem);
min-height: 2em;
}
/* BOTÓN REINICIAR */
#restart-btn {
margin-top: clamp(0.75rem, 3vw, 1.5rem);
font-size: clamp(0.9rem, 2.5vw, 1.2rem);
padding: clamp(0.5rem, 2vw, 1rem) clamp(1rem, 4vw, 2rem);
border: none;
border-radius: var(--radius);
background: var(--color-accent);
color: #fafafa;
cursor: pointer;
transition: background 0.2s ease-in-out;
}
#restart-btn:hover {
background: #f0134d;
}
/* MEDIA QUERY (opcional) para pantallas muy pequeñas */
@media (max-width: 320px) {
body {
padding: 1rem;
}
h1 {
font-size: 1.8rem;
}
#restart-btn {
padding: 0.5rem 1rem;
}
}

20
memoria-v2/index.html Normal file
View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Juego de Memoria</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Juego de Memoria Avanzado</h1>
<p>Haz clic sobre las cartas para descubrir y encontrar las parejas.</p>
<div id="hud">
<span id="moves"></span>
<span id="timer"></span>
</div>
<div id="game-board"></div>
<div id="status"></div>
<button id="restart-btn">Reiniciar</button>
<script src="script.js"></script>
</body>
</html>

131
memoria-v2/script.js Normal file
View File

@@ -0,0 +1,131 @@
const symbols = [
'🐶','🌸','⚽','🍕','🎲','🌞','🚗','🍩',
'⭐','🚀','🎮','💎'
];
let cards = [];
let firstCard = null;
let secondCard = null;
let lockBoard = false;
let matches = 0;
let moves = 0;
const maxMoves = 45;
let timer = 120; // segundos
let timerInterval = null;
const boardDiv = document.getElementById("game-board");
const statusDiv = document.getElementById("status");
const restartBtn = document.getElementById("restart-btn");
const movesSpan = document.getElementById("moves");
const timerSpan = document.getElementById("timer");
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 startGame() {
matches = 0;
moves = 0;
timer = 120;
firstCard = null;
secondCard = null;
lockBoard = false;
statusDiv.textContent = '';
updateHUD();
clearInterval(timerInterval);
timerInterval = setInterval(updateTimer, 1000);
cards = [...symbols, ...symbols];
shuffle(cards);
boardDiv.innerHTML = '';
cards.forEach((symbol, idx) => {
const card = document.createElement("div");
card.className = "card";
card.dataset.index = idx;
card.dataset.symbol = symbol;
card.textContent = '';
card.onclick = () => flipCard(card);
boardDiv.appendChild(card);
});
}
function flipCard(card) {
if (lockBoard) return;
if (card.classList.contains('flipped') || card.classList.contains('matched')) return;
card.classList.add('flipped');
card.textContent = card.dataset.symbol;
if (!firstCard) {
firstCard = card;
return; // ¡Esperamos por la segunda carta!
}
if (card === firstCard) return;
secondCard = card;
lockBoard = true;
moves++;
updateHUD();
if (firstCard.dataset.symbol === secondCard.dataset.symbol) {
// Es PAR
firstCard.classList.add('matched');
secondCard.classList.add('matched');
matches++;
setTimeout(() => {
firstCard.classList.add('hide');
secondCard.classList.add('hide');
resetTurn();
// Verifica victoria DESPUÉS de ocultar
if (matches === symbols.length) {
clearInterval(timerInterval);
statusDiv.textContent = `¡Felicidades! Lo lograste en ${moves} movimientos y te sobraron ${timer} segs 🎉`;
lockBoard = true; // Deshabilita el tablero tras terminar
}
}, 800);
} else {
// No es PAR
setTimeout(() => {
firstCard.classList.remove('flipped');
secondCard.classList.remove('flipped');
firstCard.textContent = '';
secondCard.textContent = '';
resetTurn();
}, 900);
}
// DERROTA: por movimientos
if (moves >= maxMoves) {
endGame(false, "Has alcanzado el límite de movimientos. ¡Inténtalo otra vez!");
}
}
function updateHUD() {
movesSpan.textContent = `Movimientos: ${moves} / ${maxMoves}`;
timerSpan.textContent = `Tiempo: ${timer}s`;
}
function updateTimer() {
timer--;
updateHUD();
if (timer <= 0) {
endGame(false, "¡Se acabó el tiempo!");
}
}
function resetTurn() {
firstCard = null;
secondCard = null;
lockBoard = false;
}
function endGame(win, msg) {
lockBoard = true;
clearInterval(timerInterval);
statusDiv.textContent = msg;
}
restartBtn.onclick = startGame;
startGame();

179
memoria-v2/styles.css Normal file
View File

@@ -0,0 +1,179 @@
html {
box-sizing: border-box;
font-size: 16px;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
background: #f0f2f6;
font-family: Arial, sans-serif;
text-align: center;
color: #222;
margin: 0;
min-height: 100vh;
}
h1 {
margin-top: 2.2rem;
color: #3d5a80;
font-size: clamp(1.6rem, 4vw, 2.7rem);
}
#hud {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 1.2rem;
margin: 0.5rem auto;
font-size: clamp(1em, 2vw, 1.15em);
color: #223757;
width: 100%;
max-width: 650px;
padding: 0 1vw;
}
/* ==== Tablero responsive ==== */
#game-board {
margin: 2.1rem auto 1rem auto;
display: grid;
grid-template-columns: repeat(6, minmax(45px, 1fr));
gap: 1rem;
justify-content: center;
max-width: 98vw;
padding: 0 1vw;
}
.card {
aspect-ratio: 1 / 1;
width: 100%;
background: #98c1d9;
color: #293241;
font-size: clamp(1.2rem, 4vw, 2.3rem);
border-radius: 0.8rem;
box-shadow: 0 2px 8px #bbb;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
user-select: none;
transition: background 0.2s, opacity 0.4s;
}
.card.flipped {
background: #e0fbfc;
cursor: default;
}
.card.matched {
background: #3d5a80;
color: #fff;
opacity: 0.7;
cursor: default;
}
.card.hide {
opacity: 0;
pointer-events: none;
transition: opacity 0.45s;
}
#status {
font-size: clamp(1em, 2vw, 1.2em);
min-height: 2em;
margin-bottom: 1em;
color: #3d5a80;
}
#restart-btn {
background: #3d5a80;
color: #fff;
font-size: clamp(1em, 2vw, 1.16em);
border: none;
border-radius: 0.6rem;
padding: 0.5em 2em;
cursor: pointer;
transition: background 0.2s;
margin-bottom: 1.3em;
}
#restart-btn:hover {
background: #223757;
}
/* ==== Pantallas móviles ==== */
@media (max-width: 600px) {
h1 {
margin-top: 5vw;
font-size: clamp(1.3rem, 7vw, 2.1rem);
}
#hud {
font-size: clamp(1em, 5vw, 1.25em);
gap: 4vw;
max-width: 98vw;
padding: 0 2vw;
}
#game-board {
grid-template-columns: repeat(2, 1fr);
gap: 5vw;
padding: 2vw;
margin-top: 5vw;
margin-bottom: 5vw;
max-width: 98vw;
}
.card {
border-radius: 5vw;
font-size: clamp(1.2rem, 11vw, 3rem);
}
#restart-btn {
font-size: clamp(1em, 6vw, 1.3em);
border-radius: 5vw;
padding: 3vw 10vw;
margin-bottom: 5vw;
}
#status {
font-size: clamp(1em, 7vw, 1.3em);
margin-bottom: 4vw;
}
}
/* ==== Tablets o pantallas intermedias ==== */
@media (min-width: 601px) and (max-width: 900px) {
#game-board {
grid-template-columns: repeat(4, 1fr);
gap: 2vw;
}
.card {
font-size: clamp(1.3em, 6vw, 2.6rem);
}
}
/* ==== Pantallas muy grandes ==== */
@media (min-width: 1200px) {
#hud {
font-size: clamp(1.1em, 1vw, 1.3em);
max-width: 800px;
}
#game-board {
max-width: 700px;
grid-template-columns: repeat(6, 1fr);
gap: 1.5rem;
}
.card {
font-size: clamp(2rem, 2vw, 3rem);
}
}
/* ==== Extra pequeñas ==== */
@media (max-width: 350px) {
#game-board {
grid-template-columns: repeat(1, 1fr);
gap: 2vw;
padding: 1vw;
}
.card { font-size: clamp(1rem, 23vw, 1.8rem); }
#hud {
font-size: clamp(1em, 9vw, 1.1em);
gap: 2vw;
}
}

16
memoria/index.html Normal file
View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Juego de Memoria</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Juego de Memoria</h1>
<p>Haz clic en las cartas para darles la vuelta y encuentra las parejas.</p>
<div id="game-board"></div>
<div id="info"></div>
<button id="reset-btn">Reiniciar</button>
<script src="script.js"></script>
</body>
</html>

85
memoria/script.js Normal file
View File

@@ -0,0 +1,85 @@
const symbols = ['🍎','🍌','🍒','🍇','🍉','🍑','🍊','🍓'];
let cards = [];
let firstCard = null;
let secondCard = null;
let lockBoard = false;
let matches = 0;
const gameBoard = document.getElementById('game-board');
const infoDiv = document.getElementById('info');
const resetBtn = document.getElementById('reset-btn');
function shuffle(array) {
// Fisher-Yates shuffle
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 setupBoard() {
matches = 0;
firstCard = null;
secondCard = null;
lockBoard = false;
infoDiv.textContent = '';
// Duplica los símbolos y los mezcla
cards = [...symbols, ...symbols];
shuffle(cards);
gameBoard.innerHTML = '';
cards.forEach((symbol, idx) => {
const cardEl = document.createElement('div');
cardEl.className = 'card';
cardEl.dataset.index = idx;
cardEl.dataset.symbol = symbol;
cardEl.textContent = '';
cardEl.onclick = () => flipCard(cardEl);
gameBoard.appendChild(cardEl);
});
}
function flipCard(cardEl) {
if (lockBoard) return;
if (cardEl.classList.contains('flipped') || cardEl.classList.contains('matched')) return;
cardEl.classList.add('flipped');
cardEl.textContent = cardEl.dataset.symbol;
if (!firstCard) {
firstCard = cardEl;
} else if(!secondCard && cardEl !== firstCard) {
secondCard = cardEl;
lockBoard = true;
if (firstCard.dataset.symbol === secondCard.dataset.symbol) {
// ¡Es pareja!
firstCard.classList.add('matched');
secondCard.classList.add('matched');
matches++;
resetFlipped(700);
if (matches === symbols.length) {
infoDiv.textContent = '¡Felicidades! Has encontrado todas las parejas 🎉';
}
} else {
// No es pareja, voltea las cartas después de un momento
setTimeout(() => {
firstCard.classList.remove('flipped');
secondCard.classList.remove('flipped');
firstCard.textContent = '';
secondCard.textContent = '';
resetFlipped(0);
}, 900);
}
}
}
function resetFlipped(delay) {
setTimeout(() => {
firstCard = null;
secondCard = null;
lockBoard = false;
}, delay);
}
resetBtn.onclick = setupBoard;
setupBoard();

146
memoria/styles.css Normal file
View File

@@ -0,0 +1,146 @@
/* ==== Reset y base ==== */
html {
box-sizing: border-box;
font-size: 16px;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
background: #eef2f3;
font-family: Arial, sans-serif;
text-align: center;
margin: 0;
min-height: 100vh;
color: #364f6b;
}
/* ==== Encabezado ===== */
h1 {
margin-top: 2rem;
color: #364f6b;
font-size: clamp(1.8rem, 4vw, 2.7rem);
}
/* ==== Info & botón ==== */
#info {
font-size: clamp(1rem, 2.2vw, 1.35em);
min-height: 2em;
margin-bottom: 1.2em;
}
#reset-btn {
background: #fc5185;
color: #fff;
font-size: clamp(1em, 2vw, 1.2em);
border: none;
border-radius: 0.7rem;
padding: 0.5em 2em;
cursor: pointer;
transition: background 0.2s;
margin-bottom: 1.5em;
}
#reset-btn:hover { background: #f31349; }
/* ==== Tablero ==== */
#game-board {
margin: 2.2rem auto 1.2rem auto;
display: grid;
grid-template-columns: repeat(4, minmax(60px,1fr));
gap: 1rem;
justify-content: center;
max-width: 98vw;
}
/* ==== Tarjetas ==== */
.card {
aspect-ratio: 1 / 1;
width: 100%; /* para que rellenen su columna */
background: #3fc1c9;
color: #364f6b;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0.8rem;
box-shadow: 0 2px 8px #bbb;
font-size: clamp(1.5rem, 4vw, 2.5rem);
cursor: pointer;
user-select: none;
transition: background 0.2s, color 0.2s, opacity 0.2s;
}
.card.flipped {
background: #fc5185;
color: #fff;
cursor: default;
}
.card.matched {
background: #364f6b;
color: #fff;
cursor: default;
opacity: 0.7;
}
/* ==== MÓVILES: ancho máximo y menos columnas ==== */
@media (max-width: 600px) {
h1 {
margin-top: 3vw;
font-size: clamp(1.3rem, 7vw, 2.2rem);
}
#game-board {
grid-template-columns: repeat(2, 1fr);
gap: 4vw;
margin-top: 4vw;
margin-bottom: 6vw;
padding: 2vw;
max-width: 98vw;
}
.card {
border-radius: 5vw;
font-size: clamp(1.2rem, 12vw, 2.7rem);
}
#reset-btn {
font-size: clamp(1rem, 7vw, 1.7em);
border-radius: 5vw;
padding: 3vw 12vw;
margin-bottom: 4vw;
}
#info {
font-size: clamp(1rem, 6vw, 1.4em);
}
}
/* ==== Phablets/tablets: más columnas ==== */
@media (min-width: 601px) and (max-width: 900px) {
#game-board {
grid-template-columns: repeat(3, 1fr);
gap: 2vw;
}
.card {
font-size: clamp(1.3rem, 5vw, 2.2rem);
}
}
/* ==== Pantallas grandes (>1200px): tablero más ancho ==== */
@media (min-width: 1200px) {
#game-board {
max-width: 500px;
grid-template-columns: repeat(4, 1fr);
gap: 1.5rem;
}
.card {
font-size: clamp(2rem, 2vw, 3rem);
}
}
/* ==== Extra pequeñas (<350px): modo apilado ==== */
@media (max-width: 350px) {
#game-board {
grid-template-columns: repeat(1, 1fr);
gap: 2vw;
padding: 1vw;
}
.card { font-size: clamp(1rem, 20vw, 2rem); }
}
/* ::::::::::::::::::::::::::: */

View File

@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Piedra, Papel o Tijera</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Piedra, Papel o Tijera</h1>
<div id="scoreboard">
Tú: <span id="user-score">0</span> |
Máquina: <span id="computer-score">0</span>
</div>
<div id="choices">
<button data-choice="piedra">🪨 Piedra</button>
<button data-choice="papel">📄 Papel</button>
<button data-choice="tijera">✂️ Tijera</button>
</div>
<div id="result"></div>
<button id="reset-btn">Reiniciar</button>
<script src="script.js"></script>
</body>
</html>

View File

@@ -0,0 +1,62 @@
const userScoreSpan = document.getElementById('user-score');
const computerScoreSpan = document.getElementById('computer-score');
const resultDiv = document.getElementById('result');
const choiceButtons = document.querySelectorAll('#choices button');
const resetBtn = document.getElementById('reset-btn');
let userScore = 0;
let computerScore = 0;
const choices = ['piedra', 'papel', 'tijera'];
function computerPlay() {
const idx = Math.floor(Math.random() * 3);
return choices[idx];
}
function playRound(userChoice) {
const computerChoice = computerPlay();
let resultMsg = `Tu elección: ${emoji(userChoice)} ${capitalize(userChoice)}<br>
Computadora: ${emoji(computerChoice)} ${capitalize(computerChoice)}<br>`;
if (userChoice === computerChoice) {
resultMsg += "<strong>¡Empate!</strong>";
} else if (
(userChoice === 'piedra' && computerChoice === 'tijera') ||
(userChoice === 'papel' && computerChoice === 'piedra') ||
(userChoice === 'tijera' && computerChoice === 'papel')
) {
userScore++;
userScoreSpan.textContent = userScore;
resultMsg += "<strong>¡Ganaste esta ronda! 🎉</strong>";
} else {
computerScore++;
computerScoreSpan.textContent = computerScore;
resultMsg += "<strong>La computadora gana esta ronda.</strong>";
}
resultDiv.innerHTML = resultMsg;
}
function emoji(choice) {
if (choice === 'piedra') return '🪨';
if (choice === 'papel') return '📄';
if (choice === 'tijera') return '✂️';
}
function capitalize(word) {
return word.charAt(0).toUpperCase() + word.slice(1);
}
choiceButtons.forEach(btn => {
btn.onclick = () => playRound(btn.getAttribute('data-choice'));
});
resetBtn.onclick = () => {
userScore = 0;
computerScore = 0;
userScoreSpan.textContent = userScore;
computerScoreSpan.textContent = computerScore;
resultDiv.textContent = '';
};

View File

@@ -0,0 +1,152 @@
html {
box-sizing: border-box;
font-size: 16px;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
background: #fdf6e3;
font-family: Arial, sans-serif;
text-align: center;
color: #222;
margin: 0;
min-height: 100vh;
}
/* Encabezado */
h1 {
margin-top: 2.7rem;
color: #222;
font-size: clamp(1.5rem, 4vw, 2.7rem);
}
/* Marcador */
#scoreboard {
font-size: clamp(1.05em, 2.5vw, 1.2em);
margin: 0.9em 0 1.8em 0;
}
/* Botones de elección */
#choices {
display: flex;
flex-wrap: wrap;
gap: 1em;
justify-content: center;
margin-bottom: 2em;
}
#choices button {
font-size: clamp(1em, 3vw, 1.3em);
margin: 0 0.4em;
padding: 0.9em 2em;
border: none;
border-radius: 0.7em;
background: #6ab04c;
color: #fff;
cursor: pointer;
transition: background 0.2s;
min-width: 100px;
max-width: 90vw;
box-shadow: 0 2px 6px #eaeaea;
}
#choices button:hover {
background: #218c5a;
}
/* Resultado */
#result {
font-size: clamp(1.05em, 2vw, 1.2em);
margin: 1.1em 0 1.7em 0;
min-height: 2em;
}
/* Reset */
#reset-btn {
font-size: clamp(1em, 2vw, 1.15em);
padding: 0.6em 2em;
border: none;
border-radius: 0.5em;
background: #f76707;
color: #fff;
cursor: pointer;
transition: background 0.2s;
margin-top: 1.1em;
box-shadow: 0 2px 6px #eaeaea;
}
#reset-btn:hover {
background: #c44210;
}
/* ======= Pantallas pequeñas (móviles) ======= */
@media (max-width: 600px) {
h1 {
margin-top: 5vw;
font-size: clamp(1.2rem, 7vw, 2rem);
}
#scoreboard {
font-size: clamp(1em, 6vw, 1.35em);
margin-top: 2vw;
margin-bottom: 5vw;
}
#choices {
gap: 3vw;
margin-bottom: 5vw;
}
#choices button {
font-size: clamp(1em, 6vw, 2em);
padding: 1.2em 6vw;
border-radius: 7vw;
min-width: 48vw;
}
#result {
font-size: clamp(1em, 7vw, 1.5em);
margin-bottom: 6vw;
}
#reset-btn {
font-size: clamp(1em, 8vw, 2em);
padding: 1em 15vw;
border-radius: 8vw;
margin-top: 5vw;
}
}
/* ===== Pantallas muy grandes ===== */
@media (min-width: 1200px) {
h1 {
font-size: clamp(2rem, 2vw, 3rem);
}
#scoreboard {
font-size: clamp(1.2em, 2vw, 1.5em);
margin-bottom: 2.5em;
}
#choices button {
font-size: clamp(1.2em, 2vw, 1.5em);
padding: 1em 3em;
border-radius: 1em;
min-width: 120px;
max-width: 320px;
}
#reset-btn {
font-size: clamp(1.12em, 2vw, 1.25em);
padding: 0.8em 2.5em;
border-radius: 0.8em;
}
}
/* ===== Pantallas muy pequeñas (<350px) ===== */
@media (max-width: 350px) {
#choices {
flex-direction: column;
gap: 5vw;
}
#choices button {
min-width: 60vw;
padding: 1em 7vw;
}
#reset-btn {
padding: 1em 8vw;
}
}

16
pong/index.html Normal file
View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Pong Game</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Pong Clásico</h1>
<p>Usa las flechas arriba/abajo para mover tu pala. ¡No dejes que la bola pase!</p>
<canvas id="pong" width="600" height="400"></canvas>
<div id="score"></div>
<button id="restart-btn">Reiniciar</button>
<script src="script.js"></script>
</body>
</html>

125
pong/script.js Normal file
View File

@@ -0,0 +1,125 @@
const canvas = document.getElementById('pong');
const ctx = canvas.getContext('2d');
const scoreDiv = document.getElementById('score');
const restartBtn = document.getElementById('restart-btn');
const w = canvas.width, h = canvas.height;
const paddleHeight = 80, paddleWidth = 14;
let playerY = h/2 - paddleHeight/2, aiY = h/2 - paddleHeight/2;
let playerScore = 0, aiScore = 0;
let ball = {
x: w/2, y: h/2,
size: 12,
speed: 5,
velX: 5,
velY: -4
};
let up = false, down = false;
let running = true;
// Dibujo de todo (juego, paletas, bola, marcador)
function draw() {
ctx.clearRect(0, 0, w, h);
ctx.fillStyle = "#f2e9e4";
// Jugador (izq)
ctx.fillRect(16, playerY, paddleWidth, paddleHeight);
// AI (der)
ctx.fillRect(w-16-paddleWidth, aiY, paddleWidth, paddleHeight);
// Bola
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.size, 0, 2 * Math.PI);
ctx.fillStyle = "#f6c90e";
ctx.fill();
// Red central
for(let i=15;i<h;i+=30){
ctx.fillRect(w/2-2,i,4,14);
}
// Score
scoreDiv.innerHTML = `<b>Tú:</b> ${playerScore} &nbsp; | &nbsp; <b>Máquina:</b> ${aiScore}`;
}
function gameLoop() {
if (!running) return;
// Jugador
if (up) playerY -= 7;
if (down) playerY += 7;
playerY = Math.max(0, Math.min(h-paddleHeight, playerY));
// AI: sigue la bola
if (aiY + paddleHeight/2 < ball.y - 12) aiY += 5;
else if (aiY + paddleHeight/2 > ball.y + 12) aiY -= 5;
aiY = Math.max(0, Math.min(h-paddleHeight, aiY));
// Bola
ball.x += ball.velX;
ball.y += ball.velY;
// Colisión con pared arriba/abajo
if (ball.y - ball.size < 0 || ball.y + ball.size > h) ball.velY *= -1;
// Colisión con paleta jugador
if (
ball.x - ball.size < 16 + paddleWidth &&
ball.y > playerY && ball.y < playerY + paddleHeight
) {
ball.velX = Math.abs(ball.velX);
ball.velY += (Math.random() - 0.5) * 2.5;
}
// Colisión con paleta AI
if (
ball.x + ball.size > w-16-paddleWidth &&
ball.y > aiY && ball.y < aiY + paddleHeight
) {
ball.velX = -Math.abs(ball.velX);
ball.velY += (Math.random() - 0.5) * 2.5;
}
// Gol jugador
if (ball.x - ball.size < 0) {
aiScore++;
resetBall();
}
// Gol máquina
if (ball.x + ball.size > w) {
playerScore++;
resetBall();
}
draw();
requestAnimationFrame(gameLoop);
}
function resetBall() {
ball.x = w/2; ball.y = h/2;
ball.velX = (Math.random() > 0.5 ? 5 : -5);
ball.velY = (Math.random() - 0.5) * 7;
running = false;
setTimeout(() => {
running = true;
requestAnimationFrame(gameLoop);
}, 900);
}
document.addEventListener('keydown', e => {
if (e.key === 'ArrowUp') up = true;
if (e.key === 'ArrowDown') down = true;
});
document.addEventListener('keyup', e => {
if (e.key === 'ArrowUp') up = false;
if (e.key === 'ArrowDown') down = false;
});
restartBtn.onclick = () => {
playerScore = aiScore = 0;
playerY = h/2 - paddleHeight/2;
aiY = h/2 - paddleHeight/2;
resetBall();
running = true;
requestAnimationFrame(gameLoop);
};
draw();
gameLoop();

36
pong/styles.css Normal file
View File

@@ -0,0 +1,36 @@
body {
background: #22223b;
font-family: Arial, sans-serif;
color: #f2e9e4;
text-align: center;
}
h1 {
margin-top: 32px;
color: #9599b2;
}
#pong {
background: #232946;
display: block;
margin: 28px auto 12px auto;
border-radius: 10px;
box-shadow: 0 2px 12px #bbb;
}
#score {
font-size: 1.2em;
margin-bottom: 10px;
color: #f6c90e;
}
#restart-btn {
background: #4a4e69;
color: #fff;
font-size: 1em;
border: none;
border-radius: 8px;
padding: 7px 25px;
cursor: pointer;
transition: background 0.2s;
margin-bottom: 20px;
}
#restart-btn:hover {
background: #232946;
}

15
puzle-numeros/index.html Normal file
View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Puzzle de Números</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Puzzle de Números</h1>
<div id="board"></div>
<div id="status"></div>
<button id="restart-btn">Reiniciar</button>
<script src="script.js"></script>
</body>
</html>

97
puzle-numeros/script.js Normal file
View File

@@ -0,0 +1,97 @@
const SIZE = 4;
const boardDiv = document.getElementById('board');
const statusDiv = document.getElementById('status');
const restartBtn = document.getElementById('restart-btn');
let board, won;
function initGame() {
board = [];
won = false;
let numbers = Array.from({length: SIZE*SIZE}, (_, i) => i);
do {
shuffle(numbers);
} while(!isSolvable(numbers));
// Llenar el tablero
for (let r=0; r<SIZE; r++) {
let row = [];
for (let c=0; c<SIZE; c++)
row.push(numbers[r*SIZE + c]);
board.push(row);
}
statusDiv.textContent = "Ordena las fichas del 1 al 15";
renderBoard();
}
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]];
}
}
// Verifica si el puzzle es resoluble (paridad)
function isSolvable(arr) {
let invCount = 0;
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 0) continue;
for (let j = i+1; j < arr.length; j++) {
if (arr[j] !== 0 && arr[i] > arr[j]) invCount++;
}
}
let emptyRow = Math.floor(arr.indexOf(0) / SIZE);
return (invCount + emptyRow) % 2 === 0;
}
function renderBoard() {
boardDiv.innerHTML = '';
for (let r = 0; r < SIZE; r++) {
for (let c = 0; c < SIZE; c++) {
const val = board[r][c];
const tile = document.createElement('div');
tile.className = 'tile' + (val === 0 ? ' empty' : '');
tile.textContent = val === 0 ? '' : val;
if (val !== 0 && !won) {
tile.onclick = () => moveTile(r, c);
}
boardDiv.appendChild(tile);
}
}
if (checkWin() && !won) {
won = true;
statusDiv.textContent = "¡Felicidades! Puzzle resuelto 🎉";
}
}
function moveTile(r, c) {
let neighbours = [
[r-1, c],
[r+1, c],
[r, c-1],
[r, c+1]
];
for (let [nr, nc] of neighbours) {
if (nr >=0 && nr < SIZE && nc >=0 && nc < SIZE && board[nr][nc] === 0) {
// Intercambia
[board[nr][nc], board[r][c]] = [board[r][c], board[nr][nc]];
renderBoard();
return;
}
}
}
function checkWin() {
let n = 1;
for (let r = 0; r < SIZE; r++) {
for (let c = 0; c < SIZE; c++) {
if (r === SIZE-1 && c === SIZE-1) {
if (board[r][c] !== 0) return false;
} else {
if (board[r][c] !== n++) return false;
}
}
}
return true;
}
restartBtn.onclick = initGame;
initGame();

133
puzle-numeros/styles.css Normal file
View File

@@ -0,0 +1,133 @@
html {
box-sizing: border-box;
font-size: 16px;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
background: #f4f4f4;
font-family: Arial, sans-serif;
text-align: center;
color: #222;
margin: 0;
min-height: 100vh;
}
/* Encabezado */
h1 {
margin-top: 2rem;
color: #3498db;
font-size: clamp(1.6rem, 4vw, 2.7rem);
}
/* Tablero responsive */
#board {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(4, 1fr);
gap: 0.7rem;
justify-content: center;
align-items: center;
margin: 2.2rem auto 1.3rem auto;
width: 100%;
max-width: 410px;
aspect-ratio: 1 / 1;
background: #e9ecef;
border-radius: 1.2rem;
padding: 1.2rem;
box-shadow: 0 2px 14px #bbb4;
}
/* Ficha */
.tile {
background: #fff;
border-radius: 0.6em;
box-shadow: 0 2px 8px #bbb;
font-size: clamp(1.1em, 5vw, 2em);
font-weight: bold;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.15s;
user-select: none;
min-width: 0; /* Para que flex funcionen perfecto en grid */
min-height: 0;
aspect-ratio: 1 / 1;
/* width y height no son necesarias, grid y aspect-ratio lo ajustan */
}
.tile.empty {
background: #bdd4ea;
cursor: default;
box-shadow: none;
}
/* Estado y botón */
#status {
font-size: clamp(1em, 2vw, 1.15em);
min-height: 2em;
margin-bottom: 1em;
}
#restart-btn {
background: #3498db;
color: #fff;
font-size: clamp(1em, 2vw, 1.15em);
border: none;
border-radius: 0.7em;
padding: 0.5em 2em;
cursor: pointer;
transition: background 0.2s;
margin-bottom: 1.4em;
box-shadow: 0 2px 8px #3498db33;
}
#restart-btn:hover {
background: #217dbb;
}
/* ==== Pantallas pequeñas/móviles ==== */
@media (max-width: 728px) {
h1 {
margin-top: 6vw;
font-size: clamp(1.1em, 7vw, 2.1em);
}
#board {
max-width: 96vw;
padding: 2vw;
gap: 3vw;
border-radius: 6vw;
margin-top: 6vw;
margin-bottom: 6vw;
}
.tile {
font-size: clamp(1em, 10vw, 2.6em);
border-radius: 5vw;
}
#restart-btn {
font-size: clamp(1em, 8vw, 1.5em);
padding: 1em 13vw;
border-radius: 5vw;
margin-bottom: 5vw;
}
#status {
font-size: clamp(1em, 7vw, 1.3em);
margin-bottom: 4vw;
}
}
/* ==== Pantallas muy pequeñas ==== */
@media (max-width: 350px) {
#board {
padding: 1vw;
gap: 2vw;
border-radius: 9vw;
}
.tile { font-size: clamp(0.9em, 15vw, 1.6em); }
#restart-btn {
padding: 0.7em 3vw;
border-radius: 8vw;
font-size: clamp(0.9em, 13vw, 1.1em);
}
}

25
reflejos/index.html Normal file
View File

@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Juego: ¡Haz click al Círculo!</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>¡Haz click al Círculo!</h1>
<div id="game-info">
<span>Puntuación: <span id="score">0</span></span> |
<span>Tiempo: <span id="timer">20</span> s</span>
</div>
<div id="game-area"></div>
<button id="start-btn">Empezar Juego</button>
<div id="game-over" class="hidden">
<h2>¡Juego Terminado!</h2>
<p>Tu puntuación fue: <span id="final-score"></span></p>
<button onclick="startGame()">Jugar de nuevo</button>
</div>
<script src="script.js"></script>
</body>
</html>

84
reflejos/script.js Normal file
View File

@@ -0,0 +1,84 @@
const gameArea = document.getElementById('game-area');
const scoreSpan = document.getElementById('score');
const timerSpan = document.getElementById('timer');
const startBtn = document.getElementById('start-btn');
const gameOverDiv = document.getElementById('game-over');
const finalScoreSpan = document.getElementById('final-score');
let score = 0;
let timer = 20; // duración del juego en segundos
let interval, timeout;
let playing = false;
function startGame() {
score = 0;
timer = 20;
scoreSpan.textContent = score;
timerSpan.textContent = timer;
gameArea.innerHTML = '';
gameOverDiv.classList.add('hidden');
startBtn.disabled = true;
playing = true;
// Iniciar contador regresivo
interval = setInterval(() => {
timer--;
timerSpan.textContent = timer;
if (timer <= 0) {
endGame();
}
}, 1000);
spawnCircle();
}
function endGame() {
playing = false;
clearInterval(interval);
clearTimeout(timeout);
gameArea.innerHTML = '';
startBtn.disabled = false;
// Mostrar resultado final
finalScoreSpan.textContent = score;
gameOverDiv.classList.remove('hidden');
}
function spawnCircle() {
if (!playing || timer <= 0) return;
// Crear círculo y posicionarlo aleatoriamente
const circle = document.createElement('div');
circle.className = 'circle';
// Cálculo de posición dentro de gameArea
const areaRect = gameArea.getBoundingClientRect();
const radius = 25; // la mitad de 50px
const maxLeft = gameArea.clientWidth - circle.offsetWidth;
const maxTop = gameArea.clientHeight - circle.offsetHeight;
const left = Math.floor(Math.random() * maxLeft);
const top = Math.floor(Math.random() * maxTop);
circle.style.left = left + 'px';
circle.style.top = top + 'px';
// Evento de clic
circle.onclick = function() {
score++;
scoreSpan.textContent = score;
gameArea.removeChild(circle);
spawnCircle();
};
gameArea.appendChild(circle);
// Si no lo clickean en 1.2s, desaparece y aparece otro
timeout = setTimeout(() => {
if (playing && gameArea.contains(circle)) {
gameArea.removeChild(circle);
spawnCircle();
}
}, 1200);
}
startBtn.onclick = startGame;

129
reflejos/styles.css Normal file
View File

@@ -0,0 +1,129 @@
html {
box-sizing: border-box;
font-size: 16px;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
font-family: Arial, sans-serif;
text-align: center;
background: #f2f2f2;
margin: 0;
min-height: 100vh;
color: #222;
}
/* Info del juego */
#game-info {
font-size: clamp(1em, 2vw, 1.2em);
margin-bottom: 1em;
}
/* Área de juego */
#game-area {
width: 100vw;
max-width: 420px;
height: 100vw;
max-height: 420px;
margin: 2.2rem auto 1.3rem auto;
background: #fff;
border: 2px solid #2196f3;
position: relative;
overflow: hidden;
border-radius: 0.9em;
box-shadow: 0 2px 14px #bbb4;
aspect-ratio: 1/1;
}
/* Círculo responsive */
.circle {
width: clamp(38px, 12vw, 54px);
height: clamp(38px, 12vw, 54px);
background: #4caf50;
border-radius: 50%;
position: absolute;
cursor: pointer;
box-shadow: 0 3px 10px #999;
transition: background 0.2s;
touch-action: manipulation;
}
.circle:active {
background: #388e3c;
}
/* Botón Start */
#start-btn {
font-size: clamp(1em, 3vw, 1.15em);
padding: 0.7em 2em;
margin-top: 1em;
cursor: pointer;
background: #2196f3;
color: white;
border: none;
border-radius: 0.45em;
box-shadow: 0 2px 8px #2196f366;
transition: background 0.2s;
}
#start-btn:hover,
#start-btn:active {
background: #1976d2;
}
/* Game Over */
#game-over {
margin-top: 1.3em;
font-size: clamp(1em, 2vw, 1.15em);
}
/* Oculto */
.hidden {
display: none !important;
}
/* ==== Pantallas móviles ==== */
@media (max-width: 728px) {
#game-area {
max-width: 98vw;
max-height: 98vw;
border-radius: 6vw;
box-shadow: 0 2px 8px #2196f350;
margin-top: 5vw;
margin-bottom: 4vw;
}
.circle {
width: clamp(33px, 20vw, 54px);
height: clamp(33px, 20vw, 54px);
}
#start-btn {
font-size: clamp(1em, 8vw, 1.5em);
padding: 1em 14vw;
border-radius: 5vw;
margin-top: 4vw;
}
#game-info {
font-size: clamp(1em, 6vw, 1.25em);
margin-bottom: 4vw;
}
}
/* ==== Pantallas muy pequeñas ==== */
@media (max-width: 340px) {
#game-area {
max-width: 94vw;
max-height: 94vw;
border-radius: 13vw;
}
.circle {
width: clamp(22px, 33vw, 38px);
height: clamp(22px, 33vw, 38px);
}
#start-btn {
font-size: clamp(0.9em, 10vw, 1.2em);
padding: 0.5em 8vw;
border-radius: 8vw;
}
}
/* ::::::::::::::::::::::::::::::::: */

19
serpiente/index.html Normal file
View File

@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Snake Game</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Juego de la Serpiente</h1>
<p>Usa las flechas del teclado para mover la serpiente. ¡Come y crece!</p>
<div id="game-container">
<canvas id="gameCanvas" width="400" height="400"></canvas>
</div>
<div id="score">Puntaje: <span id="score-value">0</span></div>
<button id="restart-btn">Reiniciar</button>
<div id="game-over-message"></div>
<script src="script.js"></script>
</body>
</html>

116
serpiente/script.js Normal file
View File

@@ -0,0 +1,116 @@
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreSpan = document.getElementById('score-value');
const restartBtn = document.getElementById('restart-btn');
const gameOverDiv = document.getElementById('game-over-message');
// Parámetros del juego
const gridSize = 20; // Tamaño de cada celda
const tileCount = 20; // El área es de 20x20 celdas
let snake, direction, food, score, gameInterval, gameOver;
function startGame() {
snake = [
{x: 10, y: 10},
{x: 9, y: 10},
{x: 8, y: 10}
];
direction = 'right';
score = 0;
gameOver = false;
scoreSpan.textContent = score;
gameOverDiv.textContent = '';
placeFood();
clearInterval(gameInterval);
gameInterval = setInterval(gameLoop, 100);
}
function placeFood() {
food = {
x: Math.floor(Math.random() * tileCount),
y: Math.floor(Math.random() * tileCount)
};
// Asegúrate que la comida no aparece en la serpiente
if (snake.some(segment => segment.x === food.x && segment.y === food.y)) {
placeFood();
}
}
function gameLoop() {
// Mueve la serpiente
const head = {x: snake[0].x, y: snake[0].y};
switch (direction) {
case 'left': head.x--; break;
case 'up': head.y--; break;
case 'right': head.x++; break;
case 'down': head.y++; break;
}
// Choca con pared
if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) {
endGame();
return;
}
// Choca consigo mismo
if (snake.some(seg => seg.x === head.x && seg.y === head.y)) {
endGame();
return;
}
snake.unshift(head);
// Come la comida
if (head.x === food.x && head.y === food.y) {
score++;
scoreSpan.textContent = score;
placeFood();
} else {
snake.pop();
}
drawGame();
}
function drawGame() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Dibuja la comida
ctx.fillStyle = "#fc5185";
ctx.fillRect(food.x * gridSize, food.y * gridSize, gridSize, gridSize);
// Dibuja la serpiente
for (let i = 0; i < snake.length; i++) {
ctx.fillStyle = i === 0 ? "#ffd700" : "#145991";
ctx.fillRect(snake[i].x * gridSize, snake[i].y * gridSize, gridSize, gridSize);
}
}
function changeDirection(e) {
if (gameOver) return;
switch (e.key) {
case "ArrowLeft":
if (direction !== "right") direction = "left";
break;
case "ArrowUp":
if (direction !== "down") direction = "up";
break;
case "ArrowRight":
if (direction !== "left") direction = "right";
break;
case "ArrowDown":
if (direction !== "up") direction = "down";
break;
}
}
function endGame() {
clearInterval(gameInterval);
gameOver = true;
gameOverDiv.textContent = "¡Fin del juego! Puntaje final: " + score;
}
window.addEventListener('keydown', changeDirection);
restartBtn.onclick = startGame;
// Inicia el juego
startGame();

130
serpiente/styles.css Normal file
View File

@@ -0,0 +1,130 @@
html {
box-sizing: border-box;
font-size: 16px;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
background: #142850;
font-family: Arial, sans-serif;
color: #fafafa;
text-align: center;
margin: 0;
min-height: 100vh;
}
/* Encabezado */
h1 {
margin-top: 2.2rem;
color: #00a8cc;
font-size: clamp(1.5rem, 4vw, 2.7rem);
}
/* Contenedor del juego */
#game-container {
margin: 2rem auto;
display: block;
background: #27496d;
border-radius: 1em;
box-shadow: 0 2px 10px #1119;
padding: 1.1em;
max-width: 98vw;
width: 100%;
}
/* Canvas responsive */
canvas {
background: #dae1e7;
display: block;
margin: 0 auto;
border-radius: 0.65em;
width: 100%;
max-width: 420px;
aspect-ratio: 1/1;
height: auto;
box-shadow: 0 2px 10px #27496d44;
}
/* Score */
#score {
font-size: clamp(1.1em, 2vw, 1.4em);
margin-top: 1em;
color: #ffd700;
}
/* Game over mensaje */
#game-over-message {
font-size: clamp(1.1em, 3vw, 1.5em);
color: #fc5185;
margin-top: 1em;
min-height: 2em;
}
/* Botón restart */
#restart-btn {
margin-top: 1.1em;
font-size: clamp(1em, 2vw, 1.18em);
padding: 0.6em 2em;
border: none;
border-radius: 0.6em;
background: #00a8cc;
color: #fafafa;
cursor: pointer;
transition: background 0.2s;
box-shadow: 0 2px 8px #00a8cc44;
}
#restart-btn:hover,
#restart-btn:active {
background: #145991;
}
/* ==== Pantallas móviles ==== */
@media (max-width: 600px) {
h1 {
margin-top: 5vw;
font-size: clamp(1.2rem, 7vw, 2em);
}
#game-container {
border-radius: 6vw;
padding: 5vw;
margin-top: 6vw;
margin-bottom: 8vw;
box-shadow: 0 2px 10px #1c254080;
}
canvas {
max-width: 99vw;
border-radius: 5vw;
}
#score {
font-size: clamp(1em, 8vw, 1.6em);
margin-top: 4vw;
}
#game-over-message {
font-size: clamp(1em, 8vw, 1.3em);
margin-top: 4vw;
}
#restart-btn {
font-size: clamp(1em, 8vw, 1.45em);
padding: 1em 12vw;
border-radius: 6vw;
margin-top: 4vw;
}
}
/* ==== Pantallas muy pequeñas ==== */
@media (max-width: 340px) {
#game-container {
padding: 2vw;
border-radius: 11vw;
}
canvas {
border-radius: 10vw;
}
#restart-btn {
font-size: clamp(0.9em, 8vw, 1.14em);
padding: 0.9em 3vw;
border-radius: 10vw;
}
}

20
simon-dice/index.html Normal file
View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Simon Dice</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Simon Dice</h1>
<div id="game-container">
<div class="color-btn" id="green"></div>
<div class="color-btn" id="red"></div>
<div class="color-btn" id="yellow"></div>
<div class="color-btn" id="blue"></div>
</div>
<div id="status"></div>
<button id="start-btn">Empezar</button>
<script src="script.js"></script>
</body>
</html>

66
simon-dice/script.js Normal file
View File

@@ -0,0 +1,66 @@
const colors = ['green', 'red', 'yellow', 'blue'];
let sequence = [];
let playerSequence = [];
let level = 0;
let waitingInput = false;
const statusDiv = document.getElementById('status');
const startBtn = document.getElementById('start-btn');
colors.forEach(color => {
document.getElementById(color).onclick = () => handleColorClick(color);
});
startBtn.onclick = startGame;
function startGame() {
sequence = [];
playerSequence = [];
level = 0;
statusDiv.textContent = '';
nextLevel();
}
function nextLevel() {
level++;
statusDiv.textContent = `Nivel ${level}`;
playerSequence = [];
sequence.push(colors[Math.floor(Math.random() * 4)]);
showSequence(0);
}
function showSequence(idx) {
waitingInput = false;
if (idx < sequence.length) {
const color = sequence[idx];
flashColor(color);
setTimeout(() => showSequence(idx + 1), 700);
} else {
setTimeout(() => {
waitingInput = true;
statusDiv.textContent = `Tu turno (Nivel ${level})`;
}, 400);
}
}
function flashColor(color) {
const el = document.getElementById(color);
el.classList.add('active');
setTimeout(() => el.classList.remove('active'), 400);
}
function handleColorClick(color) {
if (!waitingInput) return;
playerSequence.push(color);
flashColor(color);
const idx = playerSequence.length - 1;
if (color !== sequence[idx]) {
statusDiv.textContent = `¡Fallaste! Llegaste al nivel ${level}.`;
waitingInput = false;
return;
}
if (playerSequence.length === sequence.length) {
waitingInput = false;
setTimeout(nextLevel, 900);
}
}

136
simon-dice/styles.css Normal file
View File

@@ -0,0 +1,136 @@
html {
box-sizing: border-box;
font-size: 16px;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
background: #e1f5fe;
font-family: Arial, sans-serif;
text-align: center;
color: #222;
margin: 0;
min-height: 100vh;
}
/* Encabezado */
h1 {
margin-top: 2rem;
color: #01579b;
font-size: clamp(1.3rem, 5vw, 2.5rem);
}
/* Panel de botones Simon */
#game-container {
margin: 2.2rem auto 1rem auto;
width: 100%;
max-width: 340px;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 1fr);
gap: 1.2em;
justify-content: center;
align-items: center;
padding: 0.8em;
border-radius: 1.2em;
background: #ffffffdd;
box-shadow: 0 2px 14px #bbbe;
aspect-ratio: 1 / 1; /* el panel siempre es cuadrado */
}
/* Botón Simon */
.color-btn {
width: 100%;
height: 100%;
border-radius: 1.1em;
box-shadow: 0 2px 8px #bbb;
cursor: pointer;
transition: filter 0.2s, border 0.2s;
filter: brightness(0.85);
border: 3px solid #fafafa;
min-width: 0;
min-height: 0;
aspect-ratio: 1 / 1;
/* background colors asignados por id */
}
#green { background: #43a047;}
#red { background: #e53935;}
#yellow { background: #fbc02d;}
#blue { background: #1e88e5;}
.color-btn.active {
filter: brightness(1.1);
border: 3px solid #333;
}
/* Estado y botón inicio */
#status {
font-size: clamp(1em, 2vw, 1.15em);
min-height: 2em;
margin-bottom: 1em;
color: #01579b;
}
#start-btn {
background: #01579b;
color: #fff;
font-size: clamp(1em, 2vw, 1.13em);
border: none;
border-radius: 0.6em;
padding: 0.5em 2em;
cursor: pointer;
transition: background 0.2s;
box-shadow: 0 2px 8px #01579b33;
margin-bottom: 1.4em;
}
#start-btn:hover,
#start-btn:active {
background: #263238;
}
/* ==== Pantallas pequeñas/móviles ==== */
@media (max-width: 600px) {
h1 {
margin-top: 6vw;
font-size: clamp(1.1em, 7vw, 2em);
}
#game-container {
max-width: 96vw;
gap: 6vw;
border-radius: 6vw;
padding: 2vw;
}
.color-btn {
border-radius: 5vw;
box-shadow: 0 2px 6px #bbb;
}
#start-btn {
font-size: clamp(1em, 8vw, 1.4em);
padding: 1em 13vw;
border-radius: 5vw;
margin-bottom: 6vw;
}
#status {
font-size: clamp(1em, 7vw, 1.3em);
margin-bottom: 5vw;
}
}
/* ==== Pantallas muy pequeñas ==== */
@media (max-width: 350px) {
#game-container {
max-width: 93vw;
gap: 3vw;
padding: 1vw;
border-radius: 10vw;
}
.color-btn {
border-radius: 8vw;
}
#start-btn {
padding: 0.8em 3vw;
border-radius: 8vw;
font-size: clamp(0.9em, 13vw, 1.1em);
}
}

18
topo/index.html Normal file
View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Atrapa el Topo</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Atrapa el Topo</h1>
<p>Haz clic en el topo cuando aparezca. ¡Consigue la mejor puntuación en 30 segundos!</p>
<div id="score">Puntaje: <span id="score-value">0</span></div>
<div id="timer">Tiempo: <span id="timer-value">30</span>s</div>
<div id="grid"></div>
<button id="start-btn">¡Empezar!</button>
<div id="end-message"></div>
<script src="script.js"></script>
</body>
</html>

79
topo/script.js Normal file
View File

@@ -0,0 +1,79 @@
const gridDiv = document.getElementById('grid');
const scoreSpan = document.getElementById('score-value');
const timerSpan = document.getElementById('timer-value');
const startBtn = document.getElementById('start-btn');
const endMessageDiv = document.getElementById('end-message');
const TOTAL_TIME = 30;
const GRID_SIZE = 16;
const MOLE_SYMBOL = '🦦';
let score = 0, moleIdx = null, timer = TOTAL_TIME, intervalId, moleTimeoutId, playing = false;
function createGrid() {
gridDiv.innerHTML = '';
for (let i = 0; i < GRID_SIZE; i++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.idx = i;
cell.onclick = () => hitCell(i);
gridDiv.appendChild(cell);
}
}
function startGame() {
score = 0;
timer = TOTAL_TIME;
scoreSpan.textContent = score;
timerSpan.textContent = timer;
endMessageDiv.textContent = '';
startBtn.disabled = true;
playing = true;
createGrid();
spawnMole();
intervalId = setInterval(() => {
timer--;
timerSpan.textContent = timer;
if (timer <= 0) endGame();
}, 1000);
}
function endGame() {
playing = false;
clearInterval(intervalId);
clearTimeout(moleTimeoutId);
startBtn.disabled = false;
removeMole();
endMessageDiv.innerHTML = `¡Fin del juego! Tu puntaje: <b>${score}</b>`;
}
function spawnMole() {
removeMole();
if (!playing) return;
moleIdx = Math.floor(Math.random() * GRID_SIZE);
const cell = gridDiv.children[moleIdx];
cell.classList.add('mole');
cell.textContent = MOLE_SYMBOL;
moleTimeoutId = setTimeout(spawnMole, Math.floor(Math.random() * 700) + 600); // aparecer entre 0.6-1.3s
}
function removeMole() {
if (moleIdx !== null) {
const cell = gridDiv.children[moleIdx];
cell.classList.remove('mole');
cell.textContent = '';
moleIdx = null;
}
}
function hitCell(idx) {
if (!playing) return;
if (idx === moleIdx) {
score++;
scoreSpan.textContent = score;
removeMole();
}
}
startBtn.onclick = startGame;
createGrid();

143
topo/styles.css Normal file
View File

@@ -0,0 +1,143 @@
html {
box-sizing: border-box;
font-size: 16px;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
background: #fae3d9;
font-family: Arial, sans-serif;
text-align: center;
color: #22223b;
margin: 0;
min-height: 100vh;
}
/* Encabezado */
h1 {
margin-top: 2rem;
color: #9a8c98;
font-size: clamp(1.5rem, 5vw, 2.7rem);
}
/* Score y timer flexibles */
#score, #timer {
font-size: clamp(1em, 2vw, 1.18em);
margin-bottom: 0.8em;
}
/* Tablero de celdas adaptativo y cuadrado */
#grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(4, 1fr);
gap: 1em;
justify-content: center;
margin: 1.6em auto 0.8em auto;
width: 100%;
max-width: 360px;
aspect-ratio: 1/1;
background: #f5ebe3;
border-radius: 1.2em;
padding: 1em;
box-shadow: 0 2px 10px #bbb7;
}
/* Celdas circulares */
.cell {
background: #fff;
border-radius: 50%;
box-shadow: 0 2px 10px #bbb;
display: flex;
align-items: center;
justify-content: center;
font-size: clamp(1.5em, 6vw, 2.4em);
cursor: pointer;
transition: background 0.2s;
min-width: 0;
min-height: 0;
aspect-ratio: 1/1;
}
.cell.mole {
background: #b5838d;
}
/* Botón start flexible */
#start-btn {
background: #9a8c98;
color: #fff;
font-size: clamp(1em, 2vw, 1.18em);
border: none;
border-radius: 0.6em;
padding: 0.6em 2em;
cursor: pointer;
box-shadow: 0 2px 8px #b5838d44;
transition: background 0.2s;
margin-bottom: 1.3em;
}
#start-btn:hover, #start-btn:active {
background: #22223b;
color: #fff;
}
/* Mensaje final */
#end-message {
font-size: clamp(1em, 2vw, 1.15em);
margin-top: 1.1em;
min-height: 2em;
color: #b5838d;
}
/* ==== Pantallas móviles ==== */
@media (max-width: 600px) {
h1 {
margin-top: 6vw;
font-size: clamp(1.1em, 7vw, 2.1em);
}
#grid {
max-width: 97vw;
gap: 4vw;
padding: 2vw;
border-radius: 7vw;
margin-top: 6vw;
margin-bottom: 6vw;
}
.cell {
font-size: clamp(1.1em, 10vw, 2.8em);
border-radius: 50%;
}
#start-btn {
font-size: clamp(1em, 8vw, 1.5em);
padding: 1em 13vw;
border-radius: 5vw;
margin-bottom: 6vw;
}
#score, #timer {
font-size: clamp(1em, 7vw, 1.3em);
margin-bottom: 3vw;
}
#end-message {
font-size: clamp(1em, 7vw, 1.3em);
margin-top: 5vw;
}
}
/* ==== Pantallas muy pequeñas ==== */
@media (max-width: 350px) {
#grid {
max-width: 92vw;
gap: 2vw;
border-radius: 13vw;
padding: 1vw;
}
.cell {
font-size: clamp(0.8em, 12vw, 1.2em);
}
#start-btn {
font-size: clamp(0.9em, 10vw, 1.1em);
padding: 0.7em 3vw;
border-radius: 8vw;
}
}