Saltar al contenido

TicTacToe con Javascript ES6 nativo

Lo básico del juego: ¿Qué sabemos sobre Tic-Tac-Toe?

  1. Sabemos que el campo de juego es normalmente una cuadrícula de 3×3 pero que las cuadrículas de 4×4 también son posibles.
  2. El juego es jugado por dos jugadores, denotado por «X» o «O». Mantengamos este aspecto del juego constante.
  3. Se declara un ganador una vez que llena una fila, columna o diagonal principal con su símbolo.
  4. El juego puede resultar en una victoria/pérdida o un empate.

Diseño de la red

Empezando desde arriba, ahora sabemos que necesitamos crear algún tipo de mapa. Claro, podríamos crear un simple objeto Array compuesto de tres matrices que contengan tres matrices (para un total de 9 cuadrados). Sin embargo, ya podemos imaginarnos el impío lío que eso creará. Sin mencionar el tema de redimensionar nuestra matriz para posteriores e interesantes implementaciones.

En su lugar, deberíamos crear una biblioteca que tome el tamaño de la cuadrícula deseada como argumento e iterar hasta que tengamos un cuadrado representativo en una matriz. Ya que vamos a crear una matriz ordenada, ¿por qué no insertar un objeto como la ranura de juego – de esa manera podemos rastrearlo para la ocupación y el símbolo sin mucho alboroto.

TicTacToe con Javascript ES6 nativo
TicTacToe con Javascript ES6 nativo

P: «¿Por qué no crear un simple objeto con índices de Fila y Columna?»

R: Piensa en todos los bucles que tendrás que hacer para descubrir una sola línea horizontal. Si tenemos un objeto simple, tendremos que hacer un bucle a través de TODAS las entradas sólo para encontrar 3 que coincidan – eso es contraintuitivo, y aunque no es intensivo en recursos, (porque es sólo un 3×3) se podría argumentar que las mejores prácticas tienen prioridad aquí.

Como resultado, vamos a crear GameSlots cuadrados separados que contendrán símbolos y nos permitirán comprobar los nodos llenos.

Mapoteca

Crear un nuevo archivo, map.js (bajo lib/) que tendrá el siguiente contenido (verlo en repl.it)

123456789101112131415161718/** Crear una representación de una cuadrícula cuadrada usando Arrays que coincidirá con nuestro "mapa del juego" */exportfunctioncreateSquare(height){filasconst = altura ||3; /** usar el argumento proporcionado o por defecto a tamaño 3x3 */columnasconst = altura ||3; campo const =[];for(let x =0; x < rows; x++){let row =[];for(let y =0; y < columnas; y++){ /** establecemos la información de fila/columna dentro del slot para que podamos devolver un conjunto de slots en condiciones de ganancia * de esa manera no haremos ningún cambio especial en el objeto del slot en el futuro. */let slot ={ occupied:false, symbol:"", row: x, column: y }; row.push(slot);} field.push(row);}return field;}

javascript

GameHud

Ahora que tenemos un mapa podemos empezar a construir lo que nuestro usuario verá. Antes de entrar en el código, tenemos que diseñar nuestros componentes. Evaluemos qué tipo de Componentes necesitamos, para no terminar repitiendo la funcionalidad.

Imágenes del juego

¿Qué es lo que ya sabemos acerca de cómo se ve Tic-Tac-Toe para un usuario?

  1. Sabemos que los jugadores escriben sus símbolos en el campo de juego.
  2. Sabemos que las líneas se usan para cruzar columnas, filas o diagonales principales llenas para mostrar que el juego ha terminado.
  3. Sabemos de quién es el turno.

De estos tres puntos podemos deducir que necesitaremos algún tipo de funcionalidad de «valor de escritura» en nuestros componentes y una especie de lista para poder obtener información relevante del elemento. Como tenemos dos funcionalidades, crearemos dos archivos para dividir nuestras tareas.

Empieza con un componente base. Como cada componente tiene que tener una representación DOM, podemos decir que cada componente es, al final, «el mismo».

Componentes básicos

Vamos a nuestra carpeta lib/ y creamos el archivo simple-componente.js:

123456789101112131415161718exportclassSimpleComponent{ /** * Esta clase genera una propiedad de elemento que contiene un NodeElement para ser añadido en el DOM. Esta clase debe ser * la base de todos los demás componentes que vayamos a crear y por lo tanto también debe contener su eliminación del DOM. * @paramselector{String} el selector del elemento a crear */constructor(selector){if(!selector)throwError("un SimpleComponente debe estar compuesto por un selector");this.selector= selector.toString();this.element=document.createElement(this.selector);} /** llama a esto cuando quieras eliminar el elemento del DOM */destroy(){document.body.removeChild(this.element);}}

javascript

Ahora estamos listos para extender esta clase y hacer nuestra «funcionalidad de valor de escritura», creando otro archivo dentro de la misma carpeta, writable-component.js. Este archivo extenderá SimpleComponent y tomará el mismo argumento de selección.

El constructor de esta clase se compone de super(selector) para que podamos transmitir el argumento que se proporcionará para crear el elemento que representa este Componente.

1234567891011121314151617import{SimpleComponente}de"./simple-componente";/** Una clase escribible tiene una propiedad alias para cambiar el texto de los elementosContent y extiende SimpleComponent */exportclassWritableComponentextendsSimpleComponent{constructor(selector){super(selector);} /** get proprety establece la función como un getter, se puede usar como writableComponent.textContent */gettextContent(){returnthis.element.textContent;} /** El setter hace que se pueda establecer un valor, writableComponent.textContent = "algún valor" */settextContent(v){return(this.element.textContent= v);}}

javascript

Finalmente, crearemos nuestra Lista-Componente (list-component.js), que tendrá la capacidad de obtenerItem a partir de una propiedad de elementos predefinidos. Como una Lista no debería tener su contenido a través de textContent, este Componente extiende nuevamente a SimpleComponent.

123456789101112131415161718import{SimpleComponente}de"./simple-componente";/** Un componente de la lista es un Componente que su principal funcionalidad es sostener otros componentes que necesitan ser agarrados * en cualquier forma o situación. Hacemos esto asignando cada nuevo componente en una propiedad ``items$0027$0027 para que la sentencia *`getItem`` pueda entonces recuperarlo*/exportclassListComponentextendsSimpleComponent{constructor(selector){super(selector);esto. items=[];}getItem(index){if(typeof index !=="number")throwError("getRow must have a number as an argument");if(index <0|| index >this.items.length)throwError("Out of Bounds");returnthis.items[index];}}

javascript