├── README.md ├── calculator ├── index.html └── src │ ├── js │ ├── domElements.js │ ├── index.js │ └── operations.js │ └── styles │ └── styles.css ├── githubProfile ├── README.md ├── index.html └── src │ ├── domElements.js │ ├── index.js │ └── styles.css ├── passwordGenerator ├── README.md ├── index.html └── src │ ├── index.js │ └── styles.css └── ticTacToe ├── README.md ├── index.html └── src ├── index.js └── styles.css /README.md: -------------------------------------------------------------------------------- 1 | # PlatziChallenge: Crea tu portafolio como javaScript developer 2 | 3 | En este repositorio encontrarás una serie de proyectos para fortalecer tu portafolio como **JavaScript developer**, la rama **main** tendrá una serie de proyectos los cuales necesitan ser arreglados, en el apartados de issues de este mismo repositorio podrás ver todo lo que hace falta arreglar. 4 | 5 | Aquí abajo tienes la lista de proyectos que se actualizarán constantemente 6 | 7 | ## Password generator 8 | 9 | Generador de contraseñas las siguientes características: 10 | 11 | - Poder copiar la contraseña 12 | - Definir el total de caracteres que pueda llegar a tener nuestra contraseña 13 | - Generar contraseñas con solo numeros 14 | - Generar contraseñas con solo letras 15 | - Generar contraseñas con solo simbolos 16 | - Generar contraseñas con combinaciones de todo lo anterior 17 | - Generar contraseñas con palabras 18 | 19 | Tecnologías 20 | 21 | - HTML 22 | - CSS 23 | - JS 24 | - Axios (Opcional) 25 | 26 | Issues: 27 | 28 | - [Mejora el algoritmo de creación de contraseñas](https://github.com/LeoCode0/PlatziChallenge--js-dev/issues/3) 💻 29 | - [¡Las clases son un desastre! Implementa BEM para solucionar esto](https://github.com/LeoCode0/PlatziChallenge--js-dev/issues/4) 🎨 30 | - [Mejora la UI](https://github.com/LeoCode0/PlatziChallenge--js-dev/issues/5) ✨ 31 | - [Implementa librerías](https://github.com/LeoCode0/PlatziChallenge--js-dev/issues/6) 🌐 32 | - [Implementa el principio de responsabilidad única en donde sea necesario](https://github.com/LeoCode0/PlatziChallenge--js-dev/issues/14) 33 | - [Escribe el README de los proyectos](https://github.com/LeoCode0/PlatziChallenge--js-dev/issues/13) 34 | ## Tic tac toe / tres en raya / el gato / ta te ti 🎮 35 | 36 | ¿Recuerdas ese famoso juego donde tenías que poner circulitos (o) y equis (x)? El objetivo era básicamente obtener 3 símbolos iguales en línea ya sea vertical, horizontal o diagonal. Pues vamos a recrearlo desde 0 en web usando HTML, CSS y **JavaScript**. 37 | 38 | Especificamente contará con las siguientes características 👇 39 | 40 | - Poder escoger el símbolo que queremos usar (o) - (x) 41 | - Dibujar en el DOM el símbolo deseado 42 | - Poder declarar al ganador 43 | - Tener multijugador local 44 | - Tener un algoritmo que juegue contra nosotros 45 | - Tener historial de victorias / derrotas 46 | - Tener contador de victorias 47 | 48 | Tecnologías 49 | 50 | - HTML 51 | - CSS 52 | - JS 53 | 54 | Issues: 55 | 56 | - [Crea un marcador que te indique las victorias de cada jugador](https://github.com/LeoCode0/PlatziChallenge--js-dev/issues/16) 57 | - [Implementa el multijugador local y online](https://github.com/LeoCode0/PlatziChallenge--js-dev/issues/15) 58 | - [Implementa el principio de responsabilidad única en donde sea necesario](https://github.com/LeoCode0/PlatziChallenge--js-dev/issues/14) 59 | - [Escribe el README de los proyectos](https://github.com/LeoCode0/PlatziChallenge--js-dev/issues/13) 60 | 61 | ## Calculator 62 | 63 | Calculadora con JavaScript utilizando el paradigma de programación funcional 64 | 65 | Este proyecto contará con las siguientes características 66 | 67 | - Poder efectuar todas las operaciones aritmeticas (suma, resta, multiplicación, división) 68 | - Una UI con la cuál interactuar 69 | - Mostrar resultados en pantalla 70 | - Guardar historial de resultados 71 | 72 | Tecnologías 73 | 74 | - HTML 75 | - CSS 76 | - JS 77 | 78 | Issues: 79 | 80 | ## Github profile 81 | 82 | 🚧WIP🚧 83 | -------------------------------------------------------------------------------- /calculator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Calculator 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 | -------------------------------------------------------------------------------- /calculator/src/js/domElements.js: -------------------------------------------------------------------------------- 1 | const screen = document.querySelector(".calculator--input") 2 | const numberButtons = document.querySelectorAll(".calculator--number") 3 | const add = document.querySelector("#add") 4 | const sub = document.querySelector("#subtract") 5 | const multi = document.querySelector("#multi") 6 | const divider = document.querySelector("#divider") 7 | 8 | const equal = document.querySelector("#equal") 9 | const del = document.querySelector("#del") 10 | const delAll = document.querySelector("#del-all") 11 | -------------------------------------------------------------------------------- /calculator/src/js/index.js: -------------------------------------------------------------------------------- 1 | import * as calculator from "./operations.js"; 2 | 3 | let result = 0 4 | let currentNumber = "" 5 | let currentOperation 6 | let last 7 | 8 | numberButtons.forEach(button => { 9 | button.addEventListener("click", (event) => { 10 | printCharacter(event) 11 | currentNumber += event.target.textContent 12 | }) 13 | }) 14 | 15 | // ❌ 16 | 17 | add.addEventListener("click", (event) => { 18 | printCharacter(event) 19 | if(currentOperation){ 20 | result += currentOperation(currentNumber) 21 | currentOperation = undefined 22 | }else{ 23 | currentOperation = calculator.sum(currentNumber) 24 | } 25 | currentNumber = "" 26 | }) 27 | 28 | sub.addEventListener("click", (event) => { 29 | printCharacter(event) 30 | if(currentOperation){ 31 | result -= currentOperation(currentNumber) 32 | currentOperation = undefined 33 | }else{ 34 | currentOperation = calculator.subtract(currentNumber) 35 | } 36 | currentNumber = "" 37 | }) 38 | 39 | divider.addEventListener("click", (event) => { 40 | printCharacter(event) 41 | if(currentOperation){ 42 | result /= currentOperation(currentNumber) 43 | currentOperation = undefined 44 | }else{ 45 | currentOperation = calculator.division(currentNumber) 46 | } 47 | currentNumber = "" 48 | }) 49 | 50 | multi.addEventListener("click", (event) => { 51 | printCharacter(event) 52 | if(currentOperation){ 53 | result = 1 54 | result *= currentOperation(currentNumber) 55 | currentOperation = undefined 56 | }else{ 57 | currentOperation = calculator.multiply(currentNumber) 58 | } 59 | currentNumber = "" 60 | last = "multi" 61 | }) 62 | 63 | del.addEventListener("click", () => { 64 | let newValue = calculator.deleteLastCharacter(screen) 65 | currentNumber = newValue 66 | if(result > 0){ 67 | result = Number(newValue) 68 | } 69 | screen.value = newValue 70 | }) 71 | 72 | delAll.addEventListener("click", () => { 73 | result = 0 74 | currentNumber = "" 75 | currentOperation = undefined 76 | screen.value = "" 77 | }) 78 | 79 | equal.addEventListener("click", () => { 80 | // ❌ 81 | if(currentOperation){ 82 | switch (last) { 83 | case "multi": 84 | result = currentOperation(currentNumber) 85 | break; 86 | default: 87 | break; 88 | } 89 | currentOperation = undefined 90 | currentNumber = "" 91 | } 92 | // ❌ 93 | if(currentNumber !== ""){ 94 | result *= Number(currentNumber) 95 | currentNumber = "" 96 | } 97 | screen.value = result 98 | }) 99 | 100 | function printCharacter(event){ 101 | // ❌ 102 | const target = event.target 103 | 104 | screen.value += target.textContent 105 | } 106 | -------------------------------------------------------------------------------- /calculator/src/js/operations.js: -------------------------------------------------------------------------------- 1 | export function sum(num){ 2 | return function (anotherNum){ 3 | return Number(num) + Number(anotherNum) 4 | } 5 | } 6 | 7 | export function subtract(num){ 8 | return function (anotherNum){ 9 | return Number(num) - Number(anotherNum) 10 | } 11 | } 12 | 13 | export function division(num){ 14 | return function (anotherNum){ 15 | return Number(num) / Number(anotherNum) 16 | } 17 | } 18 | 19 | export function multiply(num){ 20 | return function (anotherNum){ 21 | return Number(num) * Number(anotherNum) 22 | } 23 | } 24 | 25 | // ❌ 26 | export function deleteLastCharacter(screen){ 27 | let newValue = screen.value.split("") 28 | newValue.pop() 29 | return newValue.join("") 30 | } -------------------------------------------------------------------------------- /calculator/src/styles/styles.css: -------------------------------------------------------------------------------- 1 | :root{ 2 | --bg-color: #121f3d; 3 | --text-color: white; 4 | --border-color: #40587c; 5 | --button-text-color: rgb(131, 217, 136); 6 | } 7 | 8 | *{ 9 | padding: 0; 10 | margin: 0; 11 | box-sizing: border-box; 12 | } 13 | 14 | body{ 15 | font-size: 30px; 16 | background-color: var(--bg-color); 17 | color: var(--text-color); 18 | height: 90vh; 19 | } 20 | 21 | .calculator{ 22 | width: max-content; 23 | height: 100%; 24 | margin: 0 auto; 25 | margin-top: 10px; 26 | } 27 | 28 | .calculator--screen{ 29 | width: 100%; 30 | height: 70px; 31 | border: 1px solid var(--border-color); 32 | border-radius: 10px; 33 | } 34 | 35 | .calculator--input{ 36 | border: 0; 37 | height: 100%; 38 | width: 100%; 39 | color: var(--text-color); 40 | font-weight: 900; 41 | font-size: 14px; 42 | letter-spacing: 1px; 43 | text-align: right; 44 | } 45 | 46 | .calculator--input:disabled{ 47 | background: transparent; 48 | } 49 | 50 | .calculator--buttons{ 51 | display: grid; 52 | grid-template-columns: repeat(3, 70px); 53 | grid-template-rows: repeat(3, 70px); 54 | gap: 4px; 55 | margin: 10px 0; 56 | } 57 | 58 | .calculator--button, .calculator--operation{ 59 | border: 1px solid var(--border-color); 60 | border-radius: 10px; 61 | height: 100%; 62 | width: 100%; 63 | background: transparent; 64 | color: var(--text-color); 65 | } 66 | 67 | .calculator--operations{ 68 | display: flex; 69 | flex-wrap: wrap; 70 | justify-content: space-between; 71 | } 72 | 73 | .calculator--operation{ 74 | color: var(--button-text-color); 75 | width: 40px; 76 | } 77 | 78 | button{ 79 | transition: all .3s; 80 | } 81 | 82 | button:hover{ 83 | cursor: pointer; 84 | opacity: 0.7; 85 | } 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /githubProfile/README.md: -------------------------------------------------------------------------------- 1 | # Mi perfil de github 🐱 -------------------------------------------------------------------------------- /githubProfile/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | My github profile 10 | 11 | 12 | 13 | 18 | 21 |
22 |
23 |

24 | Search some users in the searchbar 25 |

26 |
27 |
28 |
29 |
30 |
31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /githubProfile/src/domElements.js: -------------------------------------------------------------------------------- 1 | const fullName = document.querySelector("#fullname") 2 | const username = document.querySelector("#username") 3 | const profileImg = document.querySelector("#img") 4 | const bio = document.querySelector("#bio") 5 | const searchForm = document.querySelector("#form-search") 6 | const searchInput = document.querySelector("#search-user") 7 | 8 | const profile = document.querySelector(".profile") 9 | const errorContainer = document.querySelector(".error") 10 | 11 | const firstFollower = document.querySelector("#first") 12 | const lastFollower = document.querySelector("#last") -------------------------------------------------------------------------------- /githubProfile/src/index.js: -------------------------------------------------------------------------------- 1 | const baseApi = "https://api.github.com/users" 2 | 3 | const fetchData = async (dataApi, callbackErr, callbackOk) => { 4 | // ❌ refactor 5 | let data = await fetch(dataApi) 6 | if(data.status !== 200){ 7 | let err = await data.json() 8 | callbackErr(err) 9 | }else{ 10 | data = await data.json() 11 | if(!errorContainer.classList.contains("invisible")){ 12 | errorContainer.classList.add("invisible") 13 | } 14 | if(profile.classList.contains("invisible")){ 15 | profile.classList.remove("invisible") 16 | } 17 | callbackOk(data) 18 | } 19 | 20 | return data 21 | } 22 | 23 | const getTheLastPage = (followersCount) => { 24 | return parseInt(followersCount / 30) + 1 25 | } 26 | 27 | const fetchFollowersInformation = async (url) => { 28 | let data = await fetch(url) 29 | data = await data.json() 30 | let first = data[0].url 31 | last = await fetch(`${url}?page=${getTheLastPage(128)}`) 32 | last = await last.json() 33 | last = last[last.length - 1] 34 | console.log(last) 35 | 36 | first = await fetch(first) 37 | first = await first.json() 38 | last = await fetch(last.url) 39 | last = await last.json() 40 | printUserInformation(first, firstFollower) 41 | printUserInformation(last, lastFollower) 42 | 43 | } 44 | 45 | const printUserInformation = (data, target = profile) => { 46 | target.innerHTML = ` 47 |
48 |

49 | ${data.name} 50 |

51 |

52 | @${data.login} 53 |

54 |
55 |
56 | ${data.name} 57 |
58 |

59 | ${data.bio ? data.bio : "This user has no information"} 60 |

61 | ` 62 | } 63 | 64 | const printError = (error) => { 65 | profile.classList.add("invisible") 66 | errorContainer.classList.remove("invisible") 67 | errorContainer.innerText = error.message 68 | } 69 | 70 | searchForm.addEventListener("submit", (event) => { 71 | event.preventDefault() 72 | const value = searchInput.value 73 | fetchData(`${baseApi}/${value}`, printError, printUserInformation) 74 | .then(data => { 75 | const followers = data.followers_url 76 | fetchFollowersInformation(followers) 77 | }) 78 | }) 79 | -------------------------------------------------------------------------------- /githubProfile/src/styles.css: -------------------------------------------------------------------------------- 1 | :root{ 2 | --bg-color: #121f3d; 3 | --text-color: white; 4 | --border-color: #40587c; 5 | --button-text-color: rgb(131, 217, 136); 6 | } 7 | 8 | *{ 9 | padding: 0; 10 | margin: 0; 11 | box-sizing: border-box; 12 | } 13 | 14 | body{ 15 | font-family: sans-serif; 16 | background-color: var(--bg-color); 17 | color: var(--text-color); 18 | display: flex; 19 | flex-direction: column; 20 | justify-content: center; 21 | align-items: center; 22 | } 23 | 24 | /* Profile section */ 25 | 26 | .invisible{ 27 | display: none; 28 | } 29 | 30 | .profile{ 31 | width: 90%; 32 | margin: 20px auto; 33 | max-width: 400px; 34 | } 35 | 36 | .profile, .error{ 37 | border: 1px solid var(--border-color); 38 | padding: 10px; 39 | border-radius: 15px; 40 | } 41 | 42 | .profile--user{ 43 | text-align: center; 44 | } 45 | 46 | .profile--fullname{ 47 | margin: 10px 0; 48 | } 49 | 50 | .profile--anchor{ 51 | color: var(--button-text-color); 52 | text-decoration: none; 53 | } 54 | 55 | /* Profile img */ 56 | 57 | .profile--container{ 58 | display: flex; 59 | justify-content: center; 60 | margin: 10px 0; 61 | } 62 | 63 | .profile--img{ 64 | border-radius: 50%; 65 | width: 120px; 66 | height: 120px; 67 | } 68 | 69 | .profile--bio{ 70 | text-align: center; 71 | } 72 | 73 | 74 | -------------------------------------------------------------------------------- /passwordGenerator/README.md: -------------------------------------------------------------------------------- 1 | # Generador de contraseñas 2 | 3 | Primer proyecto del Platzi challenge para crear tu portafolio como JS developer 4 | -------------------------------------------------------------------------------- /passwordGenerator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Password generator 10 | 11 | 12 | 13 | 14 |
15 |

Aquí aparecerá tu contraseña

16 | 17 | 18 |
19 |

55

20 | 21 |

Define el total de caracteres de tu contraseña

22 |
23 |
24 | 25 |

Incluir letras

26 |
27 |
28 | 29 |

Incluir numeros

30 |
31 |
32 | 33 |

Incluir simbolos

34 |
35 |
36 | 37 |

Incluir palabras

38 |
39 |
40 | 41 |
42 |
43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /passwordGenerator/src/index.js: -------------------------------------------------------------------------------- 1 | const paragraphPassword = document.querySelector("#password"); 2 | const form = document.querySelector("#form"); 3 | const buttonCopy = document.querySelector("#button-copy"); 4 | const inputLength = document.querySelector("#input-length"); 5 | const passwordLengthParagraph = document.querySelector("#password-length"); 6 | 7 | const API = "https://goquotes-api.herokuapp.com/api/v1/random?count=5"; 8 | 9 | const letters = [ 10 | "a", 11 | "b", 12 | "c", 13 | "d", 14 | "e", 15 | "f", 16 | "g", 17 | "h", 18 | "i", 19 | "j", 20 | "k", 21 | "l", 22 | "m", 23 | "n", 24 | "o", 25 | "p", 26 | "q", 27 | "r", 28 | "s", 29 | "t", 30 | "u", 31 | "v", 32 | "w", 33 | "x", 34 | "y", 35 | "z", 36 | ]; 37 | 38 | const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; 39 | const symbols = ["'", ":", "!", "@", "#", "$", "^", ")", "&", "*", "%", "-"]; 40 | let words = []; 41 | 42 | function generatePassword(passwordLength, botonsitos) { 43 | let arrayOfArrays = []; 44 | 45 | if (botonsitos.letters) { 46 | arrayOfArrays.push(letters); 47 | } 48 | 49 | if (botonsitos.numbers) { 50 | arrayOfArrays.push(numbers); 51 | } 52 | 53 | if (botonsitos.symbols) { 54 | arrayOfArrays.push(symbols); 55 | } 56 | 57 | if (botonsitos.words) { 58 | arrayOfArrays = []; 59 | arrayOfArrays.push(words); 60 | } 61 | 62 | let strongPassword = []; 63 | for (let i = 0; i < passwordLength; i++) { 64 | const myArr = arrayOfArrays[getRandomNumber(0, arrayOfArrays.length - 1)]; 65 | 66 | const randomCharacter = myArr[getRandomNumber(0, myArr.length - 1)]; 67 | 68 | strongPassword.push(randomCharacter); 69 | } 70 | 71 | if (botonsitos.words) { 72 | strongPassword = strongPassword.join("-"); 73 | } else { 74 | strongPassword = strongPassword.join(""); 75 | } 76 | paragraphPassword.value = strongPassword; 77 | } 78 | 79 | function fetchData(API) { 80 | fetch(API) 81 | .then((response) => response.json()) 82 | .then((data) => { 83 | words = data.quotes.map((quote) => quote.text); 84 | words = words.join("").split(" ").sort(); 85 | }); 86 | } 87 | 88 | fetchData(API); 89 | 90 | function getRandomNumber(min, max) { 91 | return Math.floor(Math.random() * (max - min + 1)); 92 | } 93 | 94 | function copyToClipboard(target) { 95 | const element = document.querySelector(target); 96 | const value = element.value; 97 | if (value.length === 0) { 98 | alert("Tienes que generar una contraseña"); 99 | } else { 100 | window.navigator.clipboard.writeText(value); 101 | alert("Copiaste la contraseña"); 102 | } 103 | } 104 | 105 | form.addEventListener("submit", (event) => { 106 | event.preventDefault(); 107 | const formElement = event.target; 108 | const passwordLength = formElement.length.value; 109 | const checks = { 110 | letters: formElement.letters.checked, 111 | words: formElement.words.checked, 112 | numbers: formElement.numbers.checked, 113 | symbols: formElement.symbols.checked, 114 | }; 115 | 116 | if (checks.words) { 117 | formElement.letters.checked = false; 118 | } 119 | 120 | generatePassword(passwordLength, checks); 121 | buttonCopy.disabled = false; 122 | }); 123 | 124 | buttonCopy.addEventListener("click", () => { 125 | copyToClipboard("#password"); 126 | }); 127 | 128 | inputLength.addEventListener("input", (e) => { 129 | passwordLengthParagraph.innerText = e.target.value; 130 | }); 131 | -------------------------------------------------------------------------------- /passwordGenerator/src/styles.css: -------------------------------------------------------------------------------- 1 | :root{ 2 | --bg-color: #121f3d; 3 | --text-color: white; 4 | --border-color: #40587c; 5 | --button-text-color: rgb(131, 217, 136); 6 | } 7 | 8 | *{ 9 | padding: 0; 10 | margin: 0; 11 | box-sizing: border-box; 12 | } 13 | 14 | body{ 15 | background-color: var(--bg-color); 16 | color: var(--text-color); 17 | font-family: sans-serif; 18 | font-size: 26px; 19 | display: flex; 20 | align-items: center; 21 | justify-content: center; 22 | height: 100vh; 23 | width: 100vw; 24 | } 25 | 26 | .form{ 27 | border: 1px solid var(--border-color); 28 | padding: 20px; 29 | border-radius: 15px; 30 | width: 80%; 31 | max-width: 700px; 32 | } 33 | 34 | .option{ 35 | display: flex; 36 | margin: 10px 0; 37 | } 38 | 39 | .option input{ 40 | margin-right: 10px; 41 | } 42 | 43 | #password:disabled{ 44 | cursor: not-allowed; 45 | border: 0; 46 | border-bottom: 1px solid var(--border-color); 47 | background: transparent; 48 | padding: 10px 0; 49 | font-weight: 900; 50 | font-size: 26px; 51 | width: 100%; 52 | color: var(--text-color); 53 | } 54 | 55 | #button-copy{ 56 | border: 1px solid var(--border-color); 57 | background: transparent; 58 | color: var(--text-color); 59 | font-size: 20px; 60 | padding: 10px 5px; 61 | border-radius: 10px; 62 | margin: 10px 0; 63 | 64 | } 65 | 66 | #button-copy:hover{ 67 | cursor: pointer; 68 | opacity: 0.6; 69 | 70 | } 71 | 72 | #button-copy:disabled{ 73 | cursor: not-allowed; 74 | } -------------------------------------------------------------------------------- /ticTacToe/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeoCode0/PlatziChallenge--js-dev/aac9d96053a73452fc1740a33d3f3f8bc41a3c55/ticTacToe/README.md -------------------------------------------------------------------------------- /ticTacToe/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Tic Tac Toe 10 | 11 | 12 | 13 |

Mi tic tac toe

14 | 15 |
16 |
17 | 18 |
19 |
20 |

Escoge tu arma

21 | 22 | 23 |
24 |
25 | 26 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /ticTacToe/src/index.js: -------------------------------------------------------------------------------- 1 | const board = document.querySelector("#board"); 2 | const modal = document.querySelector("#modal") 3 | const listOfSlots = []; 4 | const rows = [] 5 | let listOfMovements = []; 6 | let currentSlot; 7 | 8 | let buttons = document.querySelectorAll(".options--button"); 9 | 10 | let player1 = { 11 | active: false, 12 | value: "", 13 | }; 14 | let player2 = { 15 | active: false, 16 | value: "", 17 | }; 18 | 19 | buttons = Array.from(buttons); 20 | 21 | board.classList.add("disabled"); 22 | 23 | function printValue(event) { 24 | const target = event.target; 25 | currentSlot = target; 26 | 27 | if (player1.active) { 28 | target.innerText = player1.value; 29 | player1.active = false; 30 | player2.active = true; 31 | clearProcess() 32 | myEnemy() 33 | } else { 34 | target.innerText = player2.value; 35 | player2.active = false; 36 | player1.active = true; 37 | clearProcess() 38 | } 39 | } 40 | 41 | function clearProcess() { 42 | currentSlot.classList.add("disabled"); 43 | currentSlot.removeEventListener("click", printValue); 44 | listOfMovements = listOfMovements.filter(elem => elem.id !== currentSlot.id) 45 | checkWinner() 46 | } 47 | 48 | function createSlots() { 49 | let subArray = [] 50 | for (let index = 0; index < 9; index++) { 51 | const slot = document.createElement("div"); 52 | slot.id = `slot-${index}` 53 | slot.addEventListener("click", printValue); 54 | 55 | listOfSlots.push(slot); 56 | subArray.push(slot) 57 | if(subArray.length === 3){ 58 | rows.push(subArray) 59 | subArray = [] 60 | } 61 | } 62 | 63 | listOfMovements = [...listOfSlots] 64 | 65 | board.append(...listOfSlots); 66 | } 67 | 68 | function myEnemy() { 69 | if(listOfMovements.length >= 1){ 70 | const enemySlot = listOfMovements[randomNumber(0, listOfMovements.length - 1)] 71 | enemySlot.click() 72 | } 73 | } 74 | 75 | function randomNumber(min, max) { 76 | 77 | return Math.floor(Math.random() * (max - min + 1)) 78 | } 79 | 80 | function checkWinner(){ 81 | const combinations = rows.map(slots => { 82 | return slots.map(slot => slot.textContent).join("") 83 | }) 84 | 85 | if(listOfMovements.length === 0){ 86 | alert("Nadie gano, empate") 87 | }else{ 88 | if(combinations.some(combination => combination === player1.value.repeat(3))){ 89 | winnerProcess(player1, "Tu user") 90 | } 91 | 92 | if(combinations.some(combination => combination === player2.value.repeat(3))){ 93 | winnerProcess(player2,"Computadora") 94 | } 95 | } 96 | 97 | } 98 | 99 | function winnerProcess(player, playerName) { 100 | for (let i = 0; i < listOfMovements.length; i++) { 101 | const element = listOfMovements[i]; 102 | element.removeEventListener("click", printValue) 103 | } 104 | 105 | listOfMovements = [] 106 | modal.classList.remove("invisible") 107 | 108 | modal.innerHTML = ` 109 |

Ha ganado el ${playerName} ${player.value}

110 | ` 111 | } 112 | 113 | createSlots(); 114 | 115 | buttons.map((button) => { 116 | button.addEventListener("click", (event) => { 117 | const target = event.target; 118 | const value = target.textContent; 119 | 120 | board.classList.remove("disabled"); 121 | 122 | if (value === "⭕") { 123 | player1.value = value; 124 | player1.active = true; 125 | player2.value = "❌"; 126 | } else { 127 | player1.value = value; 128 | player1.active = true 129 | player2.value = "⭕"; 130 | } 131 | 132 | buttons.map((button) => { 133 | button.disabled = true; 134 | }); 135 | }); 136 | }); 137 | -------------------------------------------------------------------------------- /ticTacToe/src/styles.css: -------------------------------------------------------------------------------- 1 | :root{ 2 | --bg-color: #121f3d; 3 | --text-color: white; 4 | --border-color: #40587c; 5 | --button-text-color: rgb(131, 217, 136); 6 | } 7 | 8 | *{ 9 | padding: 0; 10 | margin: 0; 11 | box-sizing: border-box; 12 | } 13 | 14 | body{ 15 | background-color: var(--bg-color); 16 | color: var(--text-color); 17 | font-family: sans-serif; 18 | } 19 | 20 | h1{ 21 | text-align: center; 22 | } 23 | 24 | .board{ 25 | display: grid; 26 | margin: 0 auto; 27 | grid-template-columns: 1fr 1fr 1fr; 28 | grid-template-rows: 1fr 1fr 1fr; 29 | 30 | width: 400px; 31 | height: 400px; 32 | font-size: 40px; 33 | } 34 | 35 | .board div{ 36 | display: flex; 37 | align-items: center; 38 | justify-content: center; 39 | border: 1px solid var(--border-color); 40 | transition: all .5s; 41 | } 42 | 43 | .board div:hover{ 44 | background-color: var(--border-color); 45 | } 46 | 47 | .disabled{ 48 | opacity: 0.5; 49 | cursor: not-allowed; 50 | } 51 | 52 | .options{ 53 | text-align: center; 54 | margin: 20px 0; 55 | } 56 | 57 | .options p{ 58 | font-size: 24px; 59 | font-weight: 900; 60 | } 61 | 62 | .options--button{ 63 | font-size: 30px; 64 | border: 1px solid var(--border-color); 65 | background: transparent; 66 | border-radius: 15px; 67 | height: 80px; 68 | width: 100px; 69 | margin: 10px 20px; 70 | } 71 | 72 | .options--button:hover{ 73 | cursor: pointer; 74 | } 75 | 76 | .options--button:disabled{ 77 | cursor: not-allowed; 78 | 79 | } 80 | 81 | #modal{ 82 | position: absolute; 83 | top: 50%; 84 | left: 0; 85 | right: 0; 86 | width: 50%; 87 | height: 40vh; 88 | margin: 0 auto; 89 | font-size: 30px; 90 | color: white; 91 | font-weight: 900; 92 | background-color: #40587c; 93 | border-radius: 15px; 94 | } 95 | 96 | 97 | #modal.invisible{ 98 | display: none; 99 | } 100 | --------------------------------------------------------------------------------