From 90b2643d8d745965ec597f092097b0c941a7a882 Mon Sep 17 00:00:00 2001 From: fermdez Date: Thu, 21 Aug 2025 23:42:55 +0200 Subject: [PATCH] =?UTF-8?q?A=C3=B1adidos=20juegos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3-en-raya-computer/index.html | 15 ++ 3-en-raya-computer/script.js | 122 +++++++++++++++ 3-en-raya-computer/styles.css | 122 +++++++++++++++ 3-en-raya/index.html | 15 ++ 3-en-raya/script.js | 52 +++++++ 3-en-raya/styles.css | 121 +++++++++++++++ 4-en-raya/index.html | 15 ++ 4-en-raya/script.js | 151 ++++++++++++++++++ 4-en-raya/styles.css | 132 ++++++++++++++++ adivina/index.html | 21 +++ adivina/script.js | 55 +++++++ adivina/styles.css | 68 +++++++++ buscaminas/index.html | 15 ++ buscaminas/script.js | 134 ++++++++++++++++ buscaminas/styles.css | 149 ++++++++++++++++++ index.html | 269 +++++++++++++++++++++++++++++++++ ladrillos/index.html | 17 +++ ladrillos/script.js | 147 ++++++++++++++++++ ladrillos/styles.css | 115 ++++++++++++++ memoria-v2/index.html | 20 +++ memoria-v2/script.js | 131 ++++++++++++++++ memoria-v2/styles.css | 179 ++++++++++++++++++++++ memoria/index.html | 16 ++ memoria/script.js | 85 +++++++++++ memoria/styles.css | 146 ++++++++++++++++++ piedra-papel-tijera/index.html | 23 +++ piedra-papel-tijera/script.js | 62 ++++++++ piedra-papel-tijera/styles.css | 152 +++++++++++++++++++ pong/index.html | 16 ++ pong/script.js | 125 +++++++++++++++ pong/styles.css | 36 +++++ puzle-numeros/index.html | 15 ++ puzle-numeros/script.js | 97 ++++++++++++ puzle-numeros/styles.css | 133 ++++++++++++++++ reflejos/index.html | 25 +++ reflejos/script.js | 84 ++++++++++ reflejos/styles.css | 129 ++++++++++++++++ serpiente/index.html | 19 +++ serpiente/script.js | 116 ++++++++++++++ serpiente/styles.css | 130 ++++++++++++++++ simon-dice/index.html | 20 +++ simon-dice/script.js | 66 ++++++++ simon-dice/styles.css | 136 +++++++++++++++++ topo/index.html | 18 +++ topo/script.js | 79 ++++++++++ topo/styles.css | 143 ++++++++++++++++++ 46 files changed, 3936 insertions(+) create mode 100644 3-en-raya-computer/index.html create mode 100644 3-en-raya-computer/script.js create mode 100644 3-en-raya-computer/styles.css create mode 100644 3-en-raya/index.html create mode 100644 3-en-raya/script.js create mode 100644 3-en-raya/styles.css create mode 100644 4-en-raya/index.html create mode 100644 4-en-raya/script.js create mode 100644 4-en-raya/styles.css create mode 100644 adivina/index.html create mode 100644 adivina/script.js create mode 100644 adivina/styles.css create mode 100644 buscaminas/index.html create mode 100644 buscaminas/script.js create mode 100644 buscaminas/styles.css create mode 100644 index.html create mode 100644 ladrillos/index.html create mode 100644 ladrillos/script.js create mode 100644 ladrillos/styles.css create mode 100644 memoria-v2/index.html create mode 100644 memoria-v2/script.js create mode 100644 memoria-v2/styles.css create mode 100644 memoria/index.html create mode 100644 memoria/script.js create mode 100644 memoria/styles.css create mode 100644 piedra-papel-tijera/index.html create mode 100644 piedra-papel-tijera/script.js create mode 100644 piedra-papel-tijera/styles.css create mode 100644 pong/index.html create mode 100644 pong/script.js create mode 100644 pong/styles.css create mode 100644 puzle-numeros/index.html create mode 100644 puzle-numeros/script.js create mode 100644 puzle-numeros/styles.css create mode 100644 reflejos/index.html create mode 100644 reflejos/script.js create mode 100644 reflejos/styles.css create mode 100644 serpiente/index.html create mode 100644 serpiente/script.js create mode 100644 serpiente/styles.css create mode 100644 simon-dice/index.html create mode 100644 simon-dice/script.js create mode 100644 simon-dice/styles.css create mode 100644 topo/index.html create mode 100644 topo/script.js create mode 100644 topo/styles.css diff --git a/3-en-raya-computer/index.html b/3-en-raya-computer/index.html new file mode 100644 index 0000000..a9f40b8 --- /dev/null +++ b/3-en-raya-computer/index.html @@ -0,0 +1,15 @@ + + + + + Tres en Raya vs Máquina + + + +

Tres en Raya vs Máquina

+
+
+ + + + \ No newline at end of file diff --git a/3-en-raya-computer/script.js b/3-en-raya-computer/script.js new file mode 100644 index 0000000..4184c15 --- /dev/null +++ b/3-en-raya-computer/script.js @@ -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(); \ No newline at end of file diff --git a/3-en-raya-computer/styles.css b/3-en-raya-computer/styles.css new file mode 100644 index 0000000..9c18416 --- /dev/null +++ b/3-en-raya-computer/styles.css @@ -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; + } +} \ No newline at end of file diff --git a/3-en-raya/index.html b/3-en-raya/index.html new file mode 100644 index 0000000..1ff83dc --- /dev/null +++ b/3-en-raya/index.html @@ -0,0 +1,15 @@ + + + + + Tres en Raya + + + +

Tres en Raya (Tic Tac Toe)

+
+
+ + + + \ No newline at end of file diff --git a/3-en-raya/script.js b/3-en-raya/script.js new file mode 100644 index 0000000..5e038d4 --- /dev/null +++ b/3-en-raya/script.js @@ -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(); \ No newline at end of file diff --git a/3-en-raya/styles.css b/3-en-raya/styles.css new file mode 100644 index 0000000..c0b6953 --- /dev/null +++ b/3-en-raya/styles.css @@ -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; + } +} \ No newline at end of file diff --git a/4-en-raya/index.html b/4-en-raya/index.html new file mode 100644 index 0000000..9da395d --- /dev/null +++ b/4-en-raya/index.html @@ -0,0 +1,15 @@ + + + + + 4 en Raya vs Máquina + + + +

4 en Raya vs Máquina

+
+
+ + + + \ No newline at end of file diff --git a/4-en-raya/script.js b/4-en-raya/script.js new file mode 100644 index 0000000..81fc7ba --- /dev/null +++ b/4-en-raya/script.js @@ -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(); \ No newline at end of file diff --git a/4-en-raya/styles.css b/4-en-raya/styles.css new file mode 100644 index 0000000..871b737 --- /dev/null +++ b/4-en-raya/styles.css @@ -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; + } +} \ No newline at end of file diff --git a/adivina/index.html b/adivina/index.html new file mode 100644 index 0000000..d4e1c99 --- /dev/null +++ b/adivina/index.html @@ -0,0 +1,21 @@ + + + + + Adivina el Número + + + +

¡Adivina el número!

+
+

Estoy pensando en un número entre 1 y 100.

+

¿Puedes adivinarlo en 7 intentos?

+ + + +
+
Intentos restantes: 7
+
+ + + \ No newline at end of file diff --git a/adivina/script.js b/adivina/script.js new file mode 100644 index 0000000..35b91e7 --- /dev/null +++ b/adivina/script.js @@ -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(); \ No newline at end of file diff --git a/adivina/styles.css b/adivina/styles.css new file mode 100644 index 0000000..885cce5 --- /dev/null +++ b/adivina/styles.css @@ -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%; + } +} \ No newline at end of file diff --git a/buscaminas/index.html b/buscaminas/index.html new file mode 100644 index 0000000..478cb98 --- /dev/null +++ b/buscaminas/index.html @@ -0,0 +1,15 @@ + + + + + Buscaminas + + + +

Buscaminas

+
+
+ + + + \ No newline at end of file diff --git a/buscaminas/script.js b/buscaminas/script.js new file mode 100644 index 0000000..0878572 --- /dev/null +++ b/buscaminas/script.js @@ -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(); \ No newline at end of file diff --git a/buscaminas/styles.css b/buscaminas/styles.css new file mode 100644 index 0000000..61e98f3 --- /dev/null +++ b/buscaminas/styles.css @@ -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); + } +} + +/* :::::::::::::::::::::::::::::: */ \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..1621efd --- /dev/null +++ b/index.html @@ -0,0 +1,269 @@ + + + + + FerMdez - Games + + + + + +
+

Fermdez - Juegos

+

¡Prueba estos mini juegos creados por Fernando Méndez!
Elige, juega y supera tus récords.

+
+
+
+
+
🏓
+
Pong Clásico
+
Juega al clásico Pong contra la máquina usando las flechas.
+ Jugar + + +
+
+
🔲
+
Simon Dice
+
Recuerda y repite la secuencia de colores para avanzar de nivel.
+ Jugar + + +
+
+
🧮
+
Buscaminas
+
Marca las minas y evita explotarlas en el tablero.
+ Jugar + + +
+
+
🃏
+
Juego de Memoria
+
Descubre todas las parejas de cartas y ejercita tu memoria.
+ Jugar + + +
+
+
🃏
+
Juego de Memoria Avanzado
+
Descubre todas las parejas de cartas y ejercita tu memoria.
+ Jugar + + +
+
+
⭕❌
+
3 en Raya
+
Tres en línea clásico: reta a un amigo.
+ Jugar + + +
+
+
⭕❌
+
3 en Raya vs Máquina
+
Tres en línea clásico: reta a una IA invencible.
+ Jugar + + +
+
+
🔵🔴🔵🔴
+
4 en Raya vs Máquina
+
Conecta 4 fichas antes que la máquina en este clásico de estrategia.
+ Jugar + + +
+
+
🧩
+
Puzzle de Números
+
Resuelve el clásico puzzle de 15 piezas deslizantes.
+ Jugar + + +
+
+
🔢
+
Adivina el Número
+
Resuelve el número que ha pensado la máquina en menos de 7 intentos.
+ Jugar + + +
+
+
🐍
+
Snake Game
+
Haz crecer la serpiente comiendo puntos, ¡no choques con la pared!
+ Jugar + + +
+
+
🧱
+
Rompe Ladrillos
+
Rompe todos los ladrillos controlando la pala y la bola.
+ Jugar + + +
+
+
⏱️
+
Carrera de Reacción
+
Haz clic cuando la pantalla se ponga verde, ¡mide tu tiempo de reacción!
+ Jugar + + +
+
+
🦦
+
Atrapa el Topo
+
Haz clic en el topo cuando aparezca y suma puntos.
+ Jugar + + +
+
+
+ + \ No newline at end of file diff --git a/ladrillos/index.html b/ladrillos/index.html new file mode 100644 index 0000000..3d28d39 --- /dev/null +++ b/ladrillos/index.html @@ -0,0 +1,17 @@ + + + + + Breakout + + + +

Rompe Ladrillos

+

Usa el ratón para mover el bloque. Rompe todos los ladrillos para ganar.

+ +
Puntuación: 0
+ +
+ + + \ No newline at end of file diff --git a/ladrillos/script.js b/ladrillos/script.js new file mode 100644 index 0000000..85550de --- /dev/null +++ b/ladrillos/script.js @@ -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 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 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 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(); \ No newline at end of file diff --git a/ladrillos/styles.css b/ladrillos/styles.css new file mode 100644 index 0000000..b834693 --- /dev/null +++ b/ladrillos/styles.css @@ -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; + } +} \ No newline at end of file diff --git a/memoria-v2/index.html b/memoria-v2/index.html new file mode 100644 index 0000000..a8ed940 --- /dev/null +++ b/memoria-v2/index.html @@ -0,0 +1,20 @@ + + + + + Juego de Memoria + + + +

Juego de Memoria Avanzado

+

Haz clic sobre las cartas para descubrir y encontrar las parejas.

+
+ + +
+
+
+ + + + \ No newline at end of file diff --git a/memoria-v2/script.js b/memoria-v2/script.js new file mode 100644 index 0000000..d70c3d9 --- /dev/null +++ b/memoria-v2/script.js @@ -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(); \ No newline at end of file diff --git a/memoria-v2/styles.css b/memoria-v2/styles.css new file mode 100644 index 0000000..6de676b --- /dev/null +++ b/memoria-v2/styles.css @@ -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; + } +} \ No newline at end of file diff --git a/memoria/index.html b/memoria/index.html new file mode 100644 index 0000000..0f3a34a --- /dev/null +++ b/memoria/index.html @@ -0,0 +1,16 @@ + + + + + Juego de Memoria + + + +

Juego de Memoria

+

Haz clic en las cartas para darles la vuelta y encuentra las parejas.

+
+
+ + + + \ No newline at end of file diff --git a/memoria/script.js b/memoria/script.js new file mode 100644 index 0000000..8a9090e --- /dev/null +++ b/memoria/script.js @@ -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(); \ No newline at end of file diff --git a/memoria/styles.css b/memoria/styles.css new file mode 100644 index 0000000..e803b39 --- /dev/null +++ b/memoria/styles.css @@ -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); } +} + +/* ::::::::::::::::::::::::::: */ \ No newline at end of file diff --git a/piedra-papel-tijera/index.html b/piedra-papel-tijera/index.html new file mode 100644 index 0000000..56b96f5 --- /dev/null +++ b/piedra-papel-tijera/index.html @@ -0,0 +1,23 @@ + + + + + Piedra, Papel o Tijera + + + +

Piedra, Papel o Tijera

+
+ Tú: 0 | + Máquina: 0 +
+
+ + + +
+
+ + + + \ No newline at end of file diff --git a/piedra-papel-tijera/script.js b/piedra-papel-tijera/script.js new file mode 100644 index 0000000..523177e --- /dev/null +++ b/piedra-papel-tijera/script.js @@ -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)}
+ Computadora: ${emoji(computerChoice)} ${capitalize(computerChoice)}
`; + + if (userChoice === computerChoice) { + resultMsg += "¡Empate!"; + } else if ( + (userChoice === 'piedra' && computerChoice === 'tijera') || + (userChoice === 'papel' && computerChoice === 'piedra') || + (userChoice === 'tijera' && computerChoice === 'papel') + ) { + userScore++; + userScoreSpan.textContent = userScore; + resultMsg += "¡Ganaste esta ronda! 🎉"; + } else { + computerScore++; + computerScoreSpan.textContent = computerScore; + resultMsg += "La computadora gana esta ronda."; + } + + 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 = ''; +}; \ No newline at end of file diff --git a/piedra-papel-tijera/styles.css b/piedra-papel-tijera/styles.css new file mode 100644 index 0000000..3cf4831 --- /dev/null +++ b/piedra-papel-tijera/styles.css @@ -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; + } +} \ No newline at end of file diff --git a/pong/index.html b/pong/index.html new file mode 100644 index 0000000..92b186b --- /dev/null +++ b/pong/index.html @@ -0,0 +1,16 @@ + + + + + Pong Game + + + +

Pong Clásico

+

Usa las flechas arriba/abajo para mover tu pala. ¡No dejes que la bola pase!

+ +
+ + + + \ No newline at end of file diff --git a/pong/script.js b/pong/script.js new file mode 100644 index 0000000..95b57d0 --- /dev/null +++ b/pong/script.js @@ -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;iTú: ${playerScore}   |   Máquina: ${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(); \ No newline at end of file diff --git a/pong/styles.css b/pong/styles.css new file mode 100644 index 0000000..5436e23 --- /dev/null +++ b/pong/styles.css @@ -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; +} \ No newline at end of file diff --git a/puzle-numeros/index.html b/puzle-numeros/index.html new file mode 100644 index 0000000..d5b8522 --- /dev/null +++ b/puzle-numeros/index.html @@ -0,0 +1,15 @@ + + + + + Puzzle de Números + + + +

Puzzle de Números

+
+
+ + + + \ No newline at end of file diff --git a/puzle-numeros/script.js b/puzle-numeros/script.js new file mode 100644 index 0000000..8a1ff03 --- /dev/null +++ b/puzle-numeros/script.js @@ -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 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(); \ No newline at end of file diff --git a/puzle-numeros/styles.css b/puzle-numeros/styles.css new file mode 100644 index 0000000..ea19a26 --- /dev/null +++ b/puzle-numeros/styles.css @@ -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); + } +} \ No newline at end of file diff --git a/reflejos/index.html b/reflejos/index.html new file mode 100644 index 0000000..6ecf42b --- /dev/null +++ b/reflejos/index.html @@ -0,0 +1,25 @@ + + + + + Juego: ¡Haz click al Círculo! + + + +

¡Haz click al Círculo!

+
+ Puntuación: 0 | + Tiempo: 20 s +
+
+ + + + + + + \ No newline at end of file diff --git a/reflejos/script.js b/reflejos/script.js new file mode 100644 index 0000000..6557e67 --- /dev/null +++ b/reflejos/script.js @@ -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; \ No newline at end of file diff --git a/reflejos/styles.css b/reflejos/styles.css new file mode 100644 index 0000000..68c6971 --- /dev/null +++ b/reflejos/styles.css @@ -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; + } +} + +/* ::::::::::::::::::::::::::::::::: */ \ No newline at end of file diff --git a/serpiente/index.html b/serpiente/index.html new file mode 100644 index 0000000..e9bba17 --- /dev/null +++ b/serpiente/index.html @@ -0,0 +1,19 @@ + + + + + Snake Game + + + +

Juego de la Serpiente

+

Usa las flechas del teclado para mover la serpiente. ¡Come y crece!

+
+ +
+
Puntaje: 0
+ +
+ + + \ No newline at end of file diff --git a/serpiente/script.js b/serpiente/script.js new file mode 100644 index 0000000..fed2014 --- /dev/null +++ b/serpiente/script.js @@ -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(); \ No newline at end of file diff --git a/serpiente/styles.css b/serpiente/styles.css new file mode 100644 index 0000000..2eff2dd --- /dev/null +++ b/serpiente/styles.css @@ -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; + } +} \ No newline at end of file diff --git a/simon-dice/index.html b/simon-dice/index.html new file mode 100644 index 0000000..56d84c5 --- /dev/null +++ b/simon-dice/index.html @@ -0,0 +1,20 @@ + + + + + Simon Dice + + + +

Simon Dice

+
+
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/simon-dice/script.js b/simon-dice/script.js new file mode 100644 index 0000000..7164dde --- /dev/null +++ b/simon-dice/script.js @@ -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); + } +} \ No newline at end of file diff --git a/simon-dice/styles.css b/simon-dice/styles.css new file mode 100644 index 0000000..5da3a20 --- /dev/null +++ b/simon-dice/styles.css @@ -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); + } +} \ No newline at end of file diff --git a/topo/index.html b/topo/index.html new file mode 100644 index 0000000..949558e --- /dev/null +++ b/topo/index.html @@ -0,0 +1,18 @@ + + + + + Atrapa el Topo + + + +

Atrapa el Topo

+

Haz clic en el topo cuando aparezca. ¡Consigue la mejor puntuación en 30 segundos!

+
Puntaje: 0
+
Tiempo: 30s
+
+ +
+ + + \ No newline at end of file diff --git a/topo/script.js b/topo/script.js new file mode 100644 index 0000000..b404a02 --- /dev/null +++ b/topo/script.js @@ -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: ${score}`; +} + +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(); \ No newline at end of file diff --git a/topo/styles.css b/topo/styles.css new file mode 100644 index 0000000..31eaa78 --- /dev/null +++ b/topo/styles.css @@ -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; + } +} \ No newline at end of file