Fundamentos de Programación

1

Bienvenida a Platzi: ¿qué necesitas para tomar el curso?

2

Programación en Navegadores: Primeros Pasos

3

Crea tu primer sitio web

4

Sitios web con HTML

5

Estructura de árbol en HTML

6

Instalando tu primer editor de código

7

Cómo declarar variables y usar prompt

8

Algoritmo de piedra, papel o tijera

9

Algoritmo avanzado de piedra, papel o tijera

10

Aleatoriedad

11

Refactor del código usando funciones

12

Ciclos

13

Gana 3 veces

14

Archivos de HTML y JavaScript

15

¿Qué es el DOM?

Quiz: Fundamentos de Programación

Desarrollando un juego con HTML y JavaScript

16

Maquetación con HTML

17

Sección de elegir mascota

18

¿Dónde ubicar la etiqueta script? Conectando HTML con JavaScript

19

Escuchando eventos con JavaScript

20

addEventListener

21

Manipulación del DOM

22

Enemigos aleatorios

23

Ataques en JavaScript

24

Ataques aleatorios del enemigo

25

Imprimiendo ataques del enemigo

26

¿Ganaste, perdiste o empataste?

27

Tablas de verdad

28

Creando el contador de vidas

29

¿Quién ganó el juego?

30

Reiniciando el juego

31

Ocultando elementos HTML con JS para mejorar la UX del juego

Quiz: Desarrollando un juego con HTML y JavaScript

Estilos con CSS

32

Anatomía de CSS

33

Tipos de display

34

Flexbox

35

Modelo de caja

36

Imágenes para los Mokepones

37

Estilos del botón

38

Adaptando HTML al diseño del juego

39

Layout: título y ataques

40

Adaptando JavaScript al diseño del juego

41

CSS Grid

42

Responsive Design

43

Detalles finales

Quiz: Estilos con CSS

Optimización de código

44

Revisión de código

45

Don't repeat yourself (DRY)

46

Clases y objetos

47

Clases y objetos de Mokepon

48

Arrays o arreglos

49

Objetos vs. arreglos

50

Ciclos: manipulando el DOM con iteradores

51

Declaración lenta de variables

52

Una sola fuente de la verdad

53

Mascotas aleatorias con arreglos

54

Ataques dinámicos por cada mascota: extraer

55

Renderizado dinámico en HTML

56

Eventos de click dinámicos

57

Secuencia de ataques del enemigo

58

Iniciando el combate

59

Resolviendo el reto de condicionales

60

Optimizando el frontend del juego

Quiz: Optimización de código

Mapa con canvas

61

Introducción a canvas: dibujando con JavaScript

62

Moviendo a Capipepo hacia la derecha

63

Movimiento hacia todas las direcciones

64

Movimientos con el teclado

65

Imágenes y personajes de fondo

66

Métodos en las clases

67

Obstáculos y colisiones

68

Combate entre mokepones colisionados

69

Mapa responsive

70

Botones bonitos y viewport

Quiz: Mapa con canvas

Backend: videojuego multijugador

71

¿Qué es backend?

72

Instalación de Node.js y NPM

73

Terminal de comandos y Node.js

74

Servidor web con Express.js

75

HTTP, localhost, servidores y puertos

76

Express.js y fetch: API REST con JavaScript

77

JSON y POST: mokepon online

78

Transmisión de coordenadas

79

Mokepones dinámicos en el mapa

80

Optimizando el mapa del juego

81

Batalla entre jugadores

82

Consumiendo la API de ataques del enemigo

Quiz: Backend: videojuego multijugador

Próximos pasos

83

Probando el juego en varios dispositivos

84

¿Y ahora qué curso tomar?

No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

No se trata de lo que quieres comprar, sino de quién quieres ser. Aprovecha el precio especial.

Antes: $249

Currency
$209

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Suscríbete

Termina en:

14 Días
7 Hrs
7 Min
11 Seg

Obstáculos y colisiones

67/84
Recursos

Aportes 56

Preguntas 27

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Con este curso he aprendido Matemáticas, Lógica de razonamiento, Programación CSS JS, THML Y DE VIDEOJUEGOS 😎😎😎

Una maravilla de curso. Disfrutándolo como un niño pequeño 🙏🏼❤️

Aquí les dejo una idea o propuesta. Cuando haya una colisión pueden probar a hacer sus propios alerts(), así tendrán sus propios estilos y no los que muestra el navegador. También pueden añadirle todo los tipos de datos que venimos utilizando. Como el nombre del enemigo, incluso hacer un tipo de mensaje personalizado si el enemigo es uno u otro. También pueden añadirle un intervalo de tiempo para que el Modal desaparezca automáticamente con el método setTimeOut().

Si la colisión sucede mucho antes de lo planeado por las imágenes que están usando se puede ajustar de la siguiente manera

let arribaNerd = namePjPlayerObjeto.y +25
let abajoNerd = namePjPlayerObjeto.y + namePjPlayerObjeto.alto -25
let derechaNerd = namePjPlayerObjeto.x + namePjPlayerObjeto.ancho -25
let izquierdaNerd = namePjPlayerObjeto.x +25

Por cierto yo usé let y no const porque la posición cambia conforme muevo el personaje, es decir no es constante según yo

En esta parte del curso habría estado genial que nos enseñaran a separar por modulos el codigo js, y exportarlo e importarlo porque ya es muy extenso, pero imagino que para fines didácticos esta bien. Sigo re emocionada aprendiendo :3

Desde que empecé a armar el juego a mi modo quise que muchas cosas fueran al AZAR (manteniendo el espíritu de piedra, papel o tijera) así que esta vez me pareció un poco aburrido que lo personajes enemigos no se movieran… les mostrare mi codigo que hace que se muevan:
-Uso mi funcion que me arma el array con los enemigos para darle un valor inicial de movimiento a los enemigos(valores diferentes para que no tomen en la misma dirección)

function IndiceEnemigos (){
    enemigos = invocaciones.filter(function(pj){
        return pj.nombre != invocaciones[indiceInvocacion_p1].nombre
    })
    enemigos[0].velY = -2
    enemigos[0].velX = -3
    enemigos[1].velX = 2
    enemigos[1].velY = 3
}

-Necesito así como para mover nuestro personaje(actualizar coordenadas en pantalla), una función que los mueva a ellos, esta función la activa otro intervalo

function movEnemigos (){
    enemigos[0].x += enemigos[0].velX
    enemigos[0].y += enemigos[0].velY

    enemigos[1].x += enemigos[1].velX
    enemigos[1].y += enemigos[1].velY

    controlMovEnem(0)
    controlMovEnem(1)  
}

-Luego necesito ponerles un límite o se saldrán de la pantalla, para ello usó las coordenadas del borde de mi canva, al “chocar” contra el borde cambian de dirección con una velocidad aleatoria y desvían ligeramente en el otro eje para que sea menos predecible el movimiento

function controlMovEnem (index){
    if(enemigos[index].x < 0 ){
        enemigos[index].velX = aleatorio(7,25)
        enemigos[index].velY += aleatorio(-5,5)
    }
    if (enemigos[index].x > 480) {
        enemigos[index].velX = aleatorio(-7,-25)
        enemigos[index].velY += aleatorio(-5,5)
    }
    if (enemigos[index].y < 0) {
        enemigos[index].velY = aleatorio(7,25)
        enemigos[index].velX += aleatorio(-5,5)
    }
    if (enemigos[index].y > 315) {
        enemigos[index].velY = aleatorio(-7,-25)
        enemigos[index].velX += aleatorio(-5,5)
    }
}

Con esto ya se mueven los enemigos, toca hacer unos ajustes adicionales para que se detengan luego de chocar pero no es nada que no se pueda hacer con lo que hemos visto… de hecho todo lo que se me ocurre hacer lo hago con lo visto porque básicamente no se mucho XD, cuando no se algo pero “pienso” se que se puede hacer le pregunto a google :p para ver cómo se hace.

Me parece que la Profesora explica demasiado rápido y lo hace ver demasiado simple y solo se limita a escribir en el código. Me parece que debería pausar un poco, dando un poco más de profundidad en la explicación de lo que se está haciendo en el código.

😄 Tan feliz que siente uno cuando nota todo lo aprendido no? 😃 😃

no entiendo casi

rayos no entendí muy bien lo de os cuadro, ósea la explicación de la grilla

Que buen curso!

Que manera de conectar y abordar cada tema.

No se si mi cerebro aguantara hasta el final del curso 😂😂

Como aprendimos en clases anteriores, si en un futuro queremos agregar más mokepones enemigos, entonces sería mejor optimizar la parte del código donde generamos los mokepones enemigos. Yo lo hice de esta manera:

  • Primero creé un array donde guardaremos los objetos de los mokepones enemigos:
let enemyMokepons = []; // Save enemy's mokepons
  • Ahora le aplicamos un ForEach para recorrer el array y que con cada mokepon que esté ahí dentro haga algo:
enemyMokepons.forEach((enemyMokepon) => {
    enemyMokepon.paintMokepon(); // con solo esta línea de código estamos pintando los mokepones enemigos que queramos
  });
  • De paso podemos también comprobar si nuestro mokepon choca o colisiona con uno de esos mokepones enemigos, tal como lo vimos en esta clase. Quedaría de la siguiente manera:
enemyMokepons.forEach((enemyMokepon) => {
    enemyMokepon.paintMokepon();

    if (playerPetSelected.speedX || playerPetSelected.speedY) {
      checkCollision(enemyMokepon); // con solo esta línea de código estamos comprobando si nuestro mokepon colisiona con cualquiera de los mokepones enemigos que tengamos
    }
  });
  • Y listo! ya nuestro código no solo quedaría optimizado si no abierto a que le agreguemos más mokepones, solo tendríamos que crear nuevos objetos con sus propiedas y métodos y esta parte no tendríamos que tocarla.

Esto no es nuevo, ya lo habíamos hecho en anteriores clases. Espero les sirva para mejorar su código. Saludos!

esta clase me costo mucho 😁 el tema de las funciones me viene pesando a lo largo del curso

Me sirvió mucho para entender lo de las colisiones primero determinar que x es Horizontal y sobre el van los movimientos de arriba y abajo (la primera y segunda condición) y el eje Y es vertical y en el van los movimientos de izquierda y derecha (la 3era y la 4ta condición).

luego sobre la grilla inicial fui marcando mis resultados con azul los de x y con rojo los de y.

Es mi impresión o faltó una explicación más detallada de la cuadricula? por ejemplo, cómo así que la parte de ArribaCuadroPequeño es 2 si en la pantalla se ve como si estuviera en el punto (3,3)

Les recomiendo hacer notas en su código para que no olviden lo que están aprendiendo, mucho éxito a todos los que están llegando a estas zonas del curso de programación, vamos, ¡SI PUEDES!

Yo en vez de crear cada una de las variables en la función, lo que hice fue crearlas en el constructor de las clase mokepon

class mokepon{ // we set a prototype (class in other languages) with class
	constructor(name, image, live, x = 10, y = 10){ 
		...
		
		this.x = x // x-axis
		this.y = y // y-axis
		this.w = 80 // width
		this.h = 80 // height
		...

		this.right = this.x + this.w
		this.left = this.x
		this.top = this.y
		this.bottom = this.y + this.h
	}

Después le añadí un método para que cada vez que, si cambiaban los valores de x oy, se actualizaran los lados:

setSides(){
		this.right = this.x + this.w
		this.left = this.x
		this.top = this.y
		this.bottom = this.y + this.h
	}

Y al final así quedó mi función:

function checkColision(enemy){
	playersMokeponObject.setSides()
	if(
		playersMokeponObject.top > enemy.bottom ||
		playersMokeponObject.bottom < enemy.top ||
		playersMokeponObject.left > enemy.right ||
		playersMokeponObject.right < enemy.left
	){
		return
	}
	console.log("Hay colisión con " + enemy.name)
}

Código completo

Estuve analizando la grilla y lo comprendí de esta manera:

  • Cuando dice “abajo” o “arriba” toma los valores de manera horizontal.
  • Cuando dice “izquierda” o “derecha” toma los valores de manera vertical
    De este modo es posible comparar las coordenadas entre cuadros para realizar las validaciones de mayor que o menor que. Obviamente, infiero que el cuadro morado es la cara del mokepon enemigo y la del naranja, la del mokepon del jugador. Les agradezco hacerme saber si estoy bien o mal.

Esto les ahorrará unas cuantas líneas.



mokeponesEnemigos.forEach((mokeponEnemigo) => {
mokeponEnemigo.pintarMokepon()
if (mascotaJugadorObjeto.velocidadX !== 0 || mascotaJugadorObjeto.velocidadY !== 0) {
            revisarColision(mokeponEnemigo)
        }
})

La formula para saber si hay colisión es si NO se cumplen estas reglas. Si solo alguna esta cumpliéndose, no hay colisión.

myImagenAbajo < imagenEnemigoArriba
myImagenArriba > imagenEnemigoAbajo
myImagenDerecha < imagenEnemigoIzquierda
myImagenIzquierda > imagenEnemigoDerecha

Cuál era el comando para poder escribir en varios renglones a la misma vez???
¿Hay un curso para aprender a manejar la frustración? Es que a este punto voy a necesitarlo. 😂😂

Después de ver la clase y leer el código, todavia no termino de comprender la lógica de la cuadrícula inicial, me está costando un poco, aunque felizmente trabajando en el código, no tuve ningún problema al desarrollarlo

Interesante lo de presionar Ctrl + click en la function o variable para ser redireccionado en donde se esta usando.

la Profe Estefany Salas explica de una muy buena manera… 😃

la explicacion de esta clase no fue la mejor , no entendi nada, como consejo a la profe estefany es que en un ffuturo de las lecciones un poco mas despacio y con su debida explicacion ya que da por hecho que todos entienden

Este segmento de verdad que no está tan complicado que el segmento anterior. Está mucho más llevadero.

Confirmo, esta clase estuvo complicada, pero bueno 3 veces la ví hasta entenderla. GRacias. #Seguimos

Utilice un forEach para pintar a los personajes (mokepones), evitando pintar el que elegimos para que no se vea repetido y con el mismo forEach revisar si hay colisiones.

personajesEnemigo.forEach((personajeEnemigo) => {
        if (personajeJugadorObjeto.id === personajeEnemigo.id) {
            personajeJugadorObjeto.pintarPersonaje()
        } else {
            personajeEnemigo.pintarPersonaje()
            if ((personajeJugadorObjeto.velocidadX !== 0) || (personajeJugadorObjeto.velocidadY !== 0)) {
                revisarColision(personajeEnemigo)
            }
        }
    })

Uffaaa que clase! de mucha logica ! de seguro para verla unas 2 veces mas !!! de basico nada!! jaja … un abrazo compañeros y fuerza y aguante para los que aun seguimos aqui!!

Agradecido con Platzi por esta oportunidad de aprender.

Es interesante la forma en que se usan las colisiones, aunque este sea un tema de videojuegos. No esta nada mal ver algo de videjuegos usando programacion basica como HTML CSS JS.

Mi codigo

function revisioColosion(enemigo){
  const arribaEnemigo = enemigo.y
  const abajoEnemigo = enemigo.y + enemigo.alto
  const izquierdaEnemigo = enemigo.x
  const derechaEnemigo = enemigo.x + enemigo.ancho

  const arribaMascota = moke.y
  const abajoMascota =  moke.y + moke.alto
  const izquierdaMascota = moke.x
  const derechaMascota = moke.x + moke.ancho
  if(
    abajoMascota < arribaEnemigo ||
    arribaMascota > abajoEnemigo ||
    derechaMascota < izquierdaEnemigo ||
    izquierdaMascota > derechaEnemigo 
  ){
    return
  }
  mokeEnemigo =  enemigo
  seleccionarMascotaEnemigo()
  console.log ('mi perre'+ mokeEnemigo.nombre)
}

Bueno, menos mal sabe que la clase se va poniendo un poco complicada!!

Interesante clase, como todas!!!

con este curso una prende mejor

Super!

Que buena clase, disfrute mucho creando las coliciones.

Tremenda clase, la he repetido como 3 veces y por fin la estoy entendiendo, hay que seguir practicando no hay que rendirse... para que de esa forma sea casi mecánico, aunque la lógica requiere también de mucho entrenamiento. Gracias profe...!!!
Queridos colegas, no entiendo muy bien el porqué el ***if*** de la función ***revisarColision()*** no tiene un ***else***, pero su código ejecuta las líneas de abajo como si lo fueran. Pues solo funcionan si hay una colisión, es como si las líneas de abajo estuviesen condicionadas pero no están dentro del ***if***, o si lo están ¿porqué no existe un ***else***? y cuando coloco el ***alert*** y ***detenerMovimiento()*** como ***else*** de ese ***if***, me tira un error en la consola. Si se supone que ese ***if*** ejecuta su bloque de código cuando no haya una colisión.
hola, no me carga ni el mapa ni los mokepones, que podria hacer?
Para los que lo olvidaron, el comando que la profe usa para editar varias lineas al mismo tiempo es Ctrl+D
esta profesora me encanta, es muy buena
necesito ayuda por favor, la imagen de mi ratigueya se queda en la parte superior pero las coordenadas de la colisión o las coordenadas donde se supone que debe estar si está no aparece en la posición, alguien sabe que puede ser? ![](https://static.platzi.com/media/user_upload/image-1771c25d-3edc-4650-9338-d1ae4b15761a.jpg)
Buenas ya que he visto que se ha complicado un poco el entender la grilla y que muchos compañeros ya la han explicado, voy a dar la explicación de como yo la logre entender: 1.Arriba y abajo corresponden al eje x 2.Derecha e izquierda corresponden al eje y 3.Hay que tener en cuenta el enunciado para saber de donde buscar la posición del objeto. ![]()![](https://static.platzi.com/media/user_upload/image-bfe3adac-fdad-4fb9-96d4-5d00ce261f52.jpg)
Buenas!, les comparto un problema que tuve. Apenas iniciaba el mapa me daba el "alert" de colisión. Tenía el código idéntico al de la profe. Me sonaba raro que en la function revisarColision() utilizara en las variables la palabra "alto", "ancho". Lo cambié al inglés: const abajoEnemigo = enemigo.y + enemigo.height const derechaEnemigo = enemigo.x + enemigo.width Anduvo perfectamente. Saludos!

Quiero ayudarles a comprender la clase, sobre todo la lógica en guardar la posición en x del borde de abajo de la imagen, y de guardar la posición en y del borde de la derecha de la imagen.

Esta parte precisamente…

    const arribaEnemigo = enemigo.y
    const abajoEnemigo = enemigo.y + enemigo.alto
    const derechaEnemigo = enemigo.x + enemigo.ancho
    const izquierdaEnemigo = enemigo.x

Me costo mi tiempo comprender el porque al sumar la posición del enemigo en “y” con el alto de la imagen da como resultado la posición en “y” del borde de abajo de la imagen.

Todo radica en la función:

lienzo.drawImage(img, x, y, width, height); 

Antes creía que en el punto exacto donde se cruza la posición en “x” con la posición en “y” era donde se dibujaba la imagen, de manera que el punto de cruze es realmente el centro de la imagen dibujada. Pues no es así.
.
.
La posición en “x” realmente es donde inicia la parte superior de la imagen en la coordenada x, y la posición en “y” es donde se inicia la parte del borde izquierdo de la imagen, por lo que en los parametros de width y height se nos pregunta hasta donde queremos que se extienda la imagen a partir del borde superior y el borde de la izquierda de la imagen.
.
.
Por eso mismo al sumar enemigo.y + enemigo.alto nos da la posición en “y” del borde inferior de la imagen. De igual manera al sumar enemigo.x + enemigo.ancho nos da la posición en “x” del borde de la derecha de la imagen.
.
.
Espero haberles ayudado, si no me logré explicar en algún punto de mi aporte porfavor haganmelo saber.

Muestro mi manera que por ahí es más eficiente en código.
(en mi método uno elige un mokepon de 6 y tiene que luchar con los otros 5)

primero, cuando toco el botón de Elegir Mascota "guarde” el índice del mokepon elegido = “MascotaJugador” y luego hay colisión si se choca con algún otro

En elegir mascota:

function seleccionarMascotaJugador() { // se ejecuta en -- iniciarJuego 
    let auxtemp = 0  // mantiene el orden de la funciones

    // obtiene nombre mascota
    if (inputHipodoge.checked) {
        mascotaJugador = inputHipodoge.id
        auxtemp = 1
    } else if (inputCapipepo.checked) {
        mascotaJugador = inputCapipepo.id
        auxtemp = 1
    } else if (inputRatigueya.checked) {
        mascotaJugador = inputRatigueya.id
        auxtemp = 1
    } else if (inputPydos.checked) {
        mascotaJugador = inputPydos.id
        auxtemp = 1
    } else if (inputTucapalma.checked) {
        mascotaJugador = inputTucapalma.id
        auxtemp = 1
    } else if (inputLangostelvis.checked) {
        mascotaJugador = inputLangostelvis.id
        auxtemp = 1
    } else {
        alert('Selecciona una mascota')
    }

    // esto solo se activa si el jugador eligio mascota
    if (auxtemp === 1) {

        // con el nombre obtengo el ID y lo guardo
        for (let i = 0; i < mokepones.length; i++) {
            if(mascotaJugador == mokepones[i].nombre){
                mascotaJugador = i
            }
        }

        // con el ID muestro los datos
        spanMascotaJugador.innerHTML = mokepones[mascotaJugador].nombre
        imgMascotaJugador.setAttribute('src', mokepones[mascotaJugador].foto)

        iniciarMapa();
    }

Ahora cuando dibujo los mokepones:

function pintarCanvas() {
    mokepones[mascotaJugador].x = mokepones[mascotaJugador].x + mokepones[mascotaJugador].velocidadX;
    mokepones[mascotaJugador].y = mokepones[mascotaJugador].y + mokepones[mascotaJugador].velocidadY;
    lienzo.clearRect(0, 0, mapa.width, mapa.height);
    lienzo.drawImage(
        mapaBackground,
        0, 0, mapa.width, mapa.height
    )

    for (let i = 0; i < mokepones.length; i++) {
        mokepones[i].pintarlo()
    }

    if (mokepones[mascotaJugador].velocidadX !== 0 ||
        mokepones[mascotaJugador].velocidadY !== 0 ) {
            for (let i = 0; i < mokepones.length; i++) {
                if (i !== mascotaJugador){
                    revisarColision(mokepones[i])
                }
            }
        }
}

genial,super excelente estoy como una esponja jajajaja aprendiendo…

const izquierdaEnemigo = enemigo.x
const derechaEnemigo = enemigo.x + enemigo.ancho

const arribaEnemigo = enemigo.y
const abajoEnemigo = enemigo.y + enemigo.alto
  1. Creamos una funcion llamada revisarColisiones() en donde comprobaremos los valores de posicion y verimos si estan colisionando con un return y un alert
  2. Creamos una constante llamada arribaEnemigo = enemigo.y
  3. abajoenemigo = enemigo.y + enemigo.alto
  4. y hacemo lo mismo con el resto de puntos tanto en el enemigo como en la mascota aliada
  5. y por ultimo hacemos la comparacion de ser mayor que con todas estas variables
  6. ponemos un if para que solo revise la colision si este se esta moviendo (” ahora entiendo por que se pedia que se utilizara el sistemas de fisicas de unity”)
  7. Cuando detente la colicion del enemigo, simplemente detenen su movimiento

<aside>
💡 el pivot es donde empieza a dibujar, a diferencia de unity que el pivot esta en el centro, el pivot en un canvas esta en la esquina superior derecha

</aside>

Para que los enemigos se puedan mover apenas cargue la página de manera aleatoria hice esta función:

function movEnemigoAleatorio(enemigo) {
    if(enemigo.x <= mapa.width-60 && enemigo.y <= mapa.height-60){//compara si están dentro del mapa
        if(aleatorio(1,2)==1){
            if(aleatorio(1,2)==1){
                enemigo.x = enemigo.x+5
                enemigo.y = enemigo.y+0
            }
            else{
                enemigo.x = enemigo.x-5
                enemigo.y = enemigo.y+0
            }
        }
        else{
            if(aleatorio(1,2)==1){
                enemigo.x = enemigo.x+0
                enemigo.y = enemigo.y+5
            }
            else{
                enemigo.x = enemigo.x+0
                enemigo.y = enemigo.y-5
            }
        }
    }
    else{
        if(aleatorio(1,2)==1){
            enemigo.x = enemigo.x-5
        }
        else{
            enemigo.y = enemigo.y-5
        }

    }
} 

Además si desean que haya colisión sin necesidad de mover con las flechas cuando el enemigo se mueva solo…comentar el if que encierra a las colisiones de los enemigos.

Uff, estuvo intensa esta clase, es extraño que no me salieron errores, que alivio 🤩

el alert entonces sirve para dar respuesta al programar

asu que chevere es esto 😉 😃 😃 😃