Tutorial Tic Tac Toe JavaScript: Cómo Programar el Juego Tic Tac Toe Paso a Paso
¡Bienvenido a este tutorial Tic Tac Toe JavaScript! Si eres un desarrollador principiante interesado en el desarrollo juego JavaScript, aquí aprenderás a crear un juego Tic Tac Toe web clásico (Tres en Raya) desde cero, utilizando HTML5, CSS3 y JavaScript.
Este tutorial está diseñado para que entiendas cada paso, desde la estructura básica del tablero hasta la lógica para gestionar turnos, detectar ganadores o empates y reiniciar el juego.
Índice
- 1. Introducción al juego Tic Tac Toe
- 2. Estructura básica en HTML
- 3. Diseño visual con CSS3
- 4. Lógica del juego en JavaScript
- 5. Prueba completa
- 6. Buenas prácticas y conclusiones
1. Introducción al juego Tic Tac Toe
El Tic Tac Toe (o Tres en Raya) es un juego de mesa para dos jugadores donde cada jugador coloca su marca (X o O) en una cuadrícula 3×3. El objetivo es alinear tres marcas propias en horizontal, vertical o diagonal.
Este proyecto es ideal para quienes desean practicar el desarrollo juego JavaScript, combinando manipulación del DOM, eventos, y lógica de programación.
2. Estructura básica en HTML
Primero, crearemos la base del tablero y un área para mensajes y botones.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Juego Tic Tac Toe</title> <link rel="stylesheet" href="styles.css" /> </head> <body> <h1>Juego Tic Tac Toe</h1> <div id="game"> <div id="board"> <!-- 9 casillas del tablero --> <div class="cell" data-index="0"></div> <div class="cell" data-index="1"></div> <div class="cell" data-index="2"></div> <div class="cell" data-index="3"></div> <div class="cell" data-index="4"></div> <div class="cell" data-index="5"></div> <div class="cell" data-index="6"></div> <div class="cell" data-index="7"></div> <div class="cell" data-index="8"></div> </div> <div id="message"></div> <button id="restartBtn">Reiniciar Juego</button> </div> <script src="script.js"></script> </body> </html> |
Explicación:
#board
contiene las 9 casillas del juego.- Cada
.cell
tiene un atributodata-index
del 0 al 8 para identificarla. #message
mostrará el turno, o el resultado.- Un botón para reiniciar el juego.
3. Diseño visual con CSS3
Ahora estilizamos el tablero para que sea atractivo y responsivo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
/* styles.css */ body { font-family: Arial, sans-serif; display: flex; flex-direction: column; align-items: center; padding: 20px; background: #f0f4f8; } h1 { margin-bottom: 20px; color: #333; } #game { display: flex; flex-direction: column; align-items: center; } #board { display: grid; grid-template-columns: repeat(3, 100px); grid-template-rows: repeat(3, 100px); gap: 10px; margin-bottom: 15px; } .cell { background-color: white; border: 2px solid #017acd; border-radius: 8px; font-size: 64px; color: #017acd; display: flex; justify-content: center; align-items: center; cursor: pointer; user-select: none; transition: background-color 0.3s, color 0.3s; } .cell:hover { background-color: #017acd; color: white; } #message { font-size: 20px; margin-bottom: 10px; min-height: 24px; color: #333; } #restartBtn { padding: 10px 20px; background-color: #017acd; border: none; border-radius: 5px; color: white; font-size: 16px; cursor: pointer; transition: background-color 0.3s; } #restartBtn:hover { background-color: #005f9e; } /* Responsive */ @media (max-width: 400px) { #board { grid-template-columns: repeat(3, 80px); grid-template-rows: repeat(3, 80px); gap: 8px; } .cell { font-size: 48px; } } |
Explicación:
- Usamos
grid
para la cuadrícula 3×3. - Cada celda tiene tamaño fijo, con bordes y estilos visuales para interacción.
- Se añaden efectos hover para una mejor experiencia.
- El botón tiene un diseño acorde.
- La responsividad permite que sea móvil-friendly.
4. Lógica del juego en JavaScript
Finalmente, programamos la funcionalidad para que el juego funcione.
4.1 Inicialización y gestión de turnos
Creamos un archivo script.js
y escribimos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
// script.js // Variables principales const cells = document.querySelectorAll('.cell'); const message = document.getElementById('message'); const restartBtn = document.getElementById('restartBtn'); let board = ['', '', '', '', '', '', '', '', '']; // Estado del tablero let currentPlayer = 'X'; // Jugador actual let isGameActive = true; // Control del juego activo // Mensajes estándar const PLAYER_X_WON = '¡Jugador X ganó!'; const PLAYER_O_WON = '¡Jugador O ganó!'; const TIE = 'Empate! No hay más movimientos.'; const TURN = () => `Turno del jugador ${currentPlayer}`; // Actualiza el mensaje en pantalla function updateMessage(text) { message.textContent = text; } // Inicializar el juego function initializeGame() { cells.forEach(cell => { cell.textContent = ''; cell.addEventListener('click', handleCellClick, { once: true }); }); board = ['', '', '', '', '', '', '', '', '']; currentPlayer = 'X'; isGameActive = true; updateMessage(TURN()); } // Llamamos a inicializar al cargar la página initializeGame(); // Manejo click en celdas function handleCellClick(event) { const clickedCell = event.target; const index = parseInt(clickedCell.getAttribute('data-index')); if (!isGameActive || board[index] !== '') { return; } updateCell(clickedCell, index); checkResult(); } // Actualizar celda y estado function updateCell(cell, index) { board[index] = currentPlayer; cell.textContent = currentPlayer; } // Cambiar jugador function changePlayer() { currentPlayer = currentPlayer === 'X' ? 'O' : 'X'; updateMessage(TURN()); } |
Explicación:
board
almacena el estado actual.cells
son los elementos DOM del tablero.- Se controla el jugador actual con
currentPlayer
. - El evento click en cada celda sólo se puede activar una vez para evitar sobreescribir.
- Se actualiza la interfaz y mensaje constantemente.
4.2 Detección de ganador y empate
Añadimos funciones para detectar si alguien gana o si hay empate.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// Combinaciones ganadoras const winningConditions = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6] ]; function checkResult() { let roundWon = false; for (let i = 0; i < winningConditions.length; i++) { const [a, b, c] = winningConditions[i]; if (board[a] === '' || board[b] === '' || board[c] === '') { continue; } if (board[a] === board[b] && board[b] === board[c]) { roundWon = true; break; } } if (roundWon) { updateMessage(currentPlayer === 'X' ? PLAYER_X_WON : PLAYER_O_WON); isGameActive = false; return; } // Comprobar empate if (!board.includes('')) { updateMessage(TIE); isGameActive = false; return; } changePlayer(); } |
Explicación:
winningConditions
define todas las tripletas ganadoras.checkResult()
revisa el tablero para verificar si un jugador ha ganado.- Declara empate si no hay espacios vacíos.
- Cambia el turno si el juego continúa.
4.3 Reinicio del juego
Finalmente, implementamos la función para reiniciar el juego.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
restartBtn.addEventListener('click', () => { initializeGame(); cells.forEach(cell => { // Removemos eventos previos para permitir nuevos clicks cell.replaceWith(cell.cloneNode(true)); }); // Actualizamos referencia a las celdas cells = document.querySelectorAll('.cell'); cells.forEach(cell => { cell.addEventListener('click', handleCellClick, { once: true }); }); }); |
Explicación:
- El botón reinicia el estado y vuelve a asignar eventos.
- Se clonan las celdas para remover listeners antiguos completando un reinicio limpio.
5. Prueba completa
A continuación se muestra un resumen del archivo script.js
completo para que puedas copiar y probar:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
const cells = document.querySelectorAll('.cell'); const message = document.getElementById('message'); const restartBtn = document.getElementById('restartBtn'); let board = ['', '', '', '', '', '', '', '', '']; let currentPlayer = 'X'; let isGameActive = true; const PLAYER_X_WON = '¡Jugador X ganó!'; const PLAYER_O_WON = '¡Jugador O ganó!'; const TIE = 'Empate! No hay más movimientos.'; const TURN = () => `Turno del jugador ${currentPlayer}`; const winningConditions = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6] ]; function updateMessage(text) { message.textContent = text; } function initializeGame() { board = ['', '', '', '', '', '', '', '', '']; currentPlayer = 'X'; isGameActive = true; cells.forEach(cell => { cell.textContent = ''; cell.addEventListener('click', handleCellClick, { once: true }); }); updateMessage(TURN()); } function handleCellClick(event) { const clickedCell = event.target; const index = parseInt(clickedCell.getAttribute('data-index')); if (!isGameActive || board[index] !== '') { return; } updateCell(clickedCell, index); checkResult(); } function updateCell(cell, index) { board[index] = currentPlayer; cell.textContent = currentPlayer; } function changePlayer() { currentPlayer = currentPlayer === 'X' ? 'O' : 'X'; updateMessage(TURN()); } function checkResult() { let roundWon = false; for (let i = 0; i < winningConditions.length; i++) { const [a, b, c] = winningConditions[i]; if ( board[a] === '' || board[b] === '' || board[c] === '' ) { continue; } if (board[a] === board[b] && board[b] === board[c]) { roundWon = true; break; } } if (roundWon) { updateMessage(currentPlayer === 'X' ? PLAYER_X_WON : PLAYER_O_WON); isGameActive = false; return; } if (!board.includes('')) { updateMessage(TIE); isGameActive = false; return; } changePlayer(); } restartBtn.addEventListener('click', initializeGame); initializeGame(); |
6. Buenas prácticas y conclusiones
Buenas prácticas:
- Separar estructura, estilo y lógica: Al usar HTML, CSS y JS separados, mantenemos un código limpio y modular.
- Nombres claros: Utiliza variables y funciones con nombres descriptivos, facilita el mantenimiento.
- Uso de eventos y estado: Controlar el juego con variables de estado (
isGameActive
) y gestionar eventos ayuda a evitar errores. - Experiencia usuario: El botón de reinicio y feedback visual permite juegos repetidos.
- Responsividad: Consideramos dispositivos móviles con CSS adaptable.
Conclusión
Con este tutorial Tic Tac Toe JavaScript has aprendido a construir un juego interactivo clásico desde cero utilizando desarrollo juego JavaScript con HTML5 y CSS3.
Este proyecto refuerza conceptos claves para desarrolladores principiantes interesados en programar Tic Tac Toe paso a paso y avanzar en juegos web.
¡Te animo a personalizar el juego, agregar animaciones o incluso inteligencia artificial para aumentar el desafío!
Si te ha gustado este tutorial Tic Tac Toe JavaScript, comparte tu juego con amigos o en redes, y sigue explorando nuevos desafíos de desarrollo juego JavaScript para mejorar tus habilidades.
¿Quieres recibir más tutoriales paso a paso para principiantes? Suscríbete a nuestro blog y mantente al día con las últimas novedades en desarrollo web.
¡Feliz programación!