Cookie Clicker con HTML+CSS+Javascript (parte 8): Comprando abuelas

En esta parte del proyecto, vamos a añadir al juego la posibilidad de comprar Abuelas que cocinen varias galletas para nosotros cada segundo. De esta forma podremos conseguir incrementar nuestra cantidad de galletas sin tener que hacer clic.

Imagen para activar la mejora

Al pulsar sobre esta imagen activarás la mejora de las abuelas. Puedes elegir cualquier imagen para tu juego, teniendo en cuenta simplemente que deberás colocarla dentro de la carpeta img. A continuación te proporcionamos una imagen de ejemplo:

Modificaciones del fichero «index.html»

Vamos a añadir un div adicional a nuestro código html para poder activar la mejora. De esta forma, el jugador podrá comprar abuelas pulsando sobre la imagen. Por cada abuela que adquiera el jugador, se generarán más galletas automáticamente por segundo:

...
<body>
    ...
    <div class="caja-mejora" onclick="comprarMejora('abuelas')">
        <img src="img/abuela.png" width="150">
        <div id="textoAbuelas"></div>
    </div>
    ...
</body>
</html>

Modificaciones del fichero «variables.js»

Añadimos la mejora de abuelas al objeto juego, con un precio inicial de 300 galletas. Cada abuela comprada generará 5 galletas por segundo:

// Objeto que almacena toda la información relativa al progreso y configuración del juego
let juego = {
    galletas: 0, // Cantidad total de galletas conseguidas
    mejoras: {
        ...
        abuelas: { cantidad: 0, precio: 300, descripcion: "Abuelas que cocinan 5 galletas por segundo" }, // Mejora de Abuelas
    }
}

Modificaciones del fichero «script.js»

Para conseguir que las abuelas cocinen y produzcan galletas sin que tengamos que hacer clic, deberemos incrementar la cantidad de galletas de forma automática cada segundo. Para ello ya disponemos de la función «producirAutomaticamente()», que creamos en la unidad anterior, dentro del fichero «script.js». Añadiendo una sola línea a esta función podremos conseguir que por cada abuela que hayamos comprado, consigamos 5 galletas más de forma automática cada segundo:

...

// Función para producir galletas automáticamente
// Se ejecuta cada segundo y suma galletas según las mejoras compradas
function producirAutomaticamente() {
    ...
    juego.galletas += juego.mejoras.abuelas.cantidad * 5;  // Suma las galletas por Abuelas
    guardarProgreso();  // Guarda el progreso automáticamente
}

Cookie Clicker con HTML+CSS+Javascript (parte 7): Auto clickers

En esta parte del proyecto, vamos a añadir una mejora conocida como Auto Clicker, que permite al jugador ganar galletas automáticamente cada segundo sin tener que hacer click.

Imagen para activar la mejora

Al pulsar sobre esta imagen activarás la mejora de clicks automáticos. Puedes elegir cualquier imagen para tu juego, teniendo en cuenta simplemente que deberás colocarla dentro de la carpeta img. A continuación te proporcionamos una imagen de ejemplo:

Modificaciones del fichero «index.html»

Vamos a añadir un div adicional a nuestro código html para poder activar la mejora. De esta forma, el jugador podrá comprar Auto Clickers pulsando sobre la imagen. Por cada Auto Clicker que adquiera el jugador, se generará una galleta automáticamente por segundo:

...
<body>
    ...
    <div class="caja-mejora" onclick="comprarMejora('autoClickers')">
        <img src="img/mano.png" width="100">
        <div id="textoAutoClickers"></div>
    </div> 
    ...
</body>
</html>

Modificaciones del fichero «variables.js»

Añadimos la mejora de Auto Clickers al objeto juego, con un precio inicial de 200 galletas. Cada Auto Clicker comprado generará una galleta por segundo:

// Objeto que almacena toda la información relativa al progreso y configuración del juego
let juego = {
    galletas: 0, // Cantidad total de galletas conseguidas
    mejoras: {
        ...
        autoClickers: { cantidad: 0, precio: 200, descripcion: "Auto click cada segundo" }, // Mejora de AutoClicker
    }
}

// Variable global para controlar el intervalo de la producción automática
let intervalo = null;

Modificaciones del fichero «script.js»

Deberemos crear una nueva función de JavaScript dentro del fichero «script.js», y la ejecutaremos automáticamente cada segundo:

  • Función reiniciarJuego(): Esta función se encargaba de reiniciar el progreso del juego. Ahora además deberá encargarse de cancelar el intervalo de producción de galletas automáticamente. Para ello deberemos añadir la siguiente línea:
    • clearInterval(intervalo); // Detiene la producción automática de galletas
  • Función producirAutomaticamente(): Esta función se ejecuta cada segundo y añade galletas según la cantidad de Auto Clickers comprados.
  • Inicialización: En la función inicializar(), deberemos configurar un intervalo que ejecute producirAutomaticamente() cada segundo. Para ello utilizaremos la función nativa de JavaScript setInterval.
...

// Función para reiniciar el juego: ¡Sólo debemos añadir la línea con la llamada a 'clearInterval()'!
function reiniciarJuego() {
    // Pregunta al usuario si realmente quiere reiniciar, y si dice que sí, borra el progreso y reinicia la página
    if (confirm("...")) {
        clearInterval(intervalo);  // Detiene la producción automática de galletas
        localStorage.removeItem('juego');  // Elimina el progreso guardado
        location.reload();  // Recarga la página
    }
}

...

// Función para producir galletas automáticamente
// Se ejecuta cada segundo y suma galletas según las mejoras compradas
function producirAutomaticamente() {
    juego.galletas += juego.mejoras.autoClickers.cantidad;  // Suma las galletas por AutoClicker
    guardarProgreso();  // Guarda el progreso automáticamente
}

// Función para inicializar el juego
function inicializar() {
    cargarProgreso();  // Carga el progreso guardado al iniciar el juego
    intervalo = setInterval(producirAutomaticamente, 1000);  // Cada segundo, produce galletas automáticamente
}

inicializar();

Cookie Clicker con HTML+CSS+Javascript (parte 6): Compilando la aplicación para móvil

En esta unidad aprenderemos cómo podemos obtener la aplicación en formato «apk» para instalarla en nuestros propios dispositivos móviles.

Utilizaremos la plataforma VoltBuilder, que facilita la creación de aplicaciones nativas para Android e iOS a partir de proyectos web hechos con HTML, CSS y JavaScript. Sólo tendremos que subir un archivo zip con nuestro proyecto, y la herramienta se encargará de convertirlo en una app nativa. Además, como está disponible en la nube, no tendremos que instalar ningún tipo de software en nuestros equipos.

A continuación se enumeran los pasos que deberemos seguir para compilar nuestra aplicación y obtener el fichero de instalación «.apk».

Icono de la aplicación

Podemos utilizar el mismo icono tanto para mostrarlo en la pestaña del navegador, como para compilar e instalar nuestra aplicación en dispositivos móviles. En nuestro caso, utilizaremos la misma imagen, pero podría ser otra diferente:

Pantalla de bienvenida

La imagen que utilicemos para la pantalla de bienvenida deberá tener cierto margen alrededor, ya que se mostrará en el centro de la pantalla, y la plataforma VoltBuilder la recortará para asegurar que se muestra de manera correcta en cualquier dispositivo y orientación.

Crear fichero de configuración «config.xml»

Primero deberemos crear el archivo «config.xml» en el directorio raiz de la aplicación. A continuación se muestra un ejemplo básico (puedes cambiar el nombre, la descripción, el icono y la pantalla de bienvenida a tu gusto):

<?xml version='1.0' encoding='utf-8'?>
<widget id="com.fernandoruizrico.clicker" xmlns="http://www.w3.org/ns/widgets" version="1.0.1">
  <name>Cookie Clicker</name>
  <description>Juego Cookie Clicker</description>

  <icon src="img/icono.png" />
  <splash src="img/pantalla-bienvenida.png" />

  <preference name="SplashScreenDelay" value="2000" />
  <preference name="AutoHideSplashScreen" value="true" />
  <preference name="AndroidWindowSplashScreenAnimatedIcon" value="img/pantalla-bienvenida.png" />
  <preference name="AndroidWindowSplashScreenBackground" value="#FFFFFF" />

  <plugin name="cordova-plugin-device" />
  <plugin name="cordova-plugin-splashscreen" />
  <plugin name="cordova-plugin-vibration" />

  <preference name="Orientation" value="portrait" /> 

  <access origin="*" />
</widget>

Comprimir la aplicación en formato «.zip»

La página web VoltBuilder necesita que le proporcionemos nuestra aplicación en formato ZIP. Por ello, deberemos comprimir toda nuestra aplicación en un solo fichero ZIP, que incluirá el fichero «config.xml», y todos los ficheros html, CSS y JavaScript. No importa el nombre que tenga el fichero comprimido.

Compilar la «.apk»

El último paso que deberemos seguir para obtener nuestra aplicación en formato «apk» será subir nuestro fichero «.zip» a la página web VoltBuilder. El proceso es muy sencillo:

  1. Acceder a la página de inicio de VoltBuilder: https://volt.build/
  2. Iniciar sesión (con tu cuenta de gmail por ejemplo) haciendo click en la opción «Login/Sign Up».
  3. Hacer click en la opción «Upload».
  4. Enviar nuestra aplicación arrastrando el fichero ZIP directamente al cuadro de «Android». También se puede hacer click sobre dicho cuadro y seleccionar nuestro fichero después.
  5. Una vez la aplicación se haya compilado, podremos descargar el fichero «.apk» generado a través de un código QR, y ya podremos instalarlo en nuestro propio móvil.

Cookie Clicker con HTML+CSS+Javascript (parte 5): Añadiendo icono y estilos

En esta parte vamos a añadir un icono al juego y aplicaremos los primeros estilos CSS. Esto nos permitirá personalizar el juego, haciéndolo visualmente más atractivo e interactivo, lo que ofrece una mejor experiencia para el jugador.

Icono de la aplicación

Puedes elegir cualquier imagen para el icono de tu juego, teniendo en cuenta simplemente que deberás colocarla dentro de la carpeta img. A continuación te proporcionamos una imagen de ejemplo:

Modificar el archivo «index.html»

Realizaremos las siguientes modificaciones sobre el fichero «index.html»:

  • Añadir un icono: El icono aparecerá en la pestaña del navegador, y posteriormente también lo utilizaremos como icono de aplicación cuando instalemos el fichero «apk» en nuestros dispositivos móviles. Utilizaremos la etiqueta <link> para enlazar el archivo de imagen.
  • Fichero con estilos CSS: Usamos un archivo externo con código CSS («style.css») para aplicar estilos a la página.
  • Clases CSS: Añadiremos las correspondientes clases para aplicar estilos específicos a cada caja. Utilizaremos el atributo class sobre cada bloque div:
    • caja-galleta: Caja que contiene la galleta sobre la que hacemos click para incrementar el contador.
    • caja-mejora: Cajas que activan cada una de las mejoras del juego.
    • caja-opciones: Caja que contendrá opciones como reiniciar el juego, o más adelante, exportar e importar el progreso.
<!DOCTYPE html>
<html lang="es">
<head>
    ...
    <link rel="icon" type="image/png" href="img/icono.png">
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>Cookie Clicker</h1>

    <div class="caja-galleta" onclick="incrementarContador()">
        ...
    </div>

    <div class="caja-mejora" onclick="comprarMejora('galletasExtra')">
        ...
    </div>
 
    <div class="caja-opciones">
        <button onclick="reiniciarJuego()">Reiniciar el juego</button>
    </div>
 
    ...
</body>

Archivo «style.css»

Crearemos el fichero «style.css» que contendrá todos los estilos que utilizaremos para personalizar nuestro juego:

  • Estilos básicos: Centramos el contenido, aplicamos una fuente legible y hacemos que el contador de galletas destaque.
  • Estilos avanzados: Añadiremos bordes, sombras, y efectos de clic para que las cajas sean más interactivas y agradables visualmente.
body {
  text-align: center; /* Centra el contenido horizontalmente */
  font-family: Arial, sans-serif; /* Fuente básica para mantener legibilidad */
}

#contador {
  color: #991919; /* Rojo oscuro para destacar el contador, simboliza "galletas acumuladas" */
  font-weight: bold; /* El texto en negrita ayuda a que sea más legible y destaque */
  font-size: 250%; /* Tamaño grande para captar la atención del usuario */
}

/* Agrupación de estilos compartidos para cajas de galleta, mejora y opciones */
.caja-galleta, .caja-mejora, .caja-opciones {
  border-radius: 10px; /* Consistencia visual en los bordes redondeados */
  box-shadow: 2px 2px 10px; /* Sombra para simular profundidad */
  padding: 10px; /* Espacio interno suficiente para evitar que el contenido quede pegado al borde */
  margin: 20px 10px; /* Espaciado que separa visualmente las cajas, sin sobrecargar el espacio */
}

.caja-galleta {
  border: 2px solid #7837d4; /* Borde morado, un color alegre que representa el clic principal del juego */
  cursor: pointer; /* Indica que es interactivo */
}

.caja-galleta:active {
  transform: scale(0.98); /* Efecto visual de que la caja está siendo presionada */
  box-shadow: 2px 2px 5px; /* Sombra más pequeña para dar sensación de "presión" */
}

.caja-mejora {
  border: 2px solid #d4af37; /* Borde dorado para simbolizar un "logro" o mejora premium */
  cursor: pointer; /* Interactivo */
}

.caja-mejora:active {
  transform: scale(0.98); /* Efecto de clic */
  box-shadow: 2px 2px 5px; /* Sombra reducida al hacer clic */
}

.caja-opciones {
  border: 2px solid #d45e37; /* Borde naranja, un color cálido para representar acciones de configuración */
  padding: 20px; /* Mayor espacio interno para acomodar los botones y opciones de manera cómoda */
}

Cookie Clicker con HTML+CSS+Javascript (parte 4): Reiniciando el progreso del juego

En esta parte aprenderemos a reiniciar el progreso del juego. Esto permitirá que el jugador borre todo su progreso y comience desde cero si lo desea. Este enfoque enseña cómo manipular el almacenamiento local del navegador y cómo interactuar con el usuario mediante ventanas de confirmación.

Modificar el archivo «index.html»

Vamos a agregar un botón en la página que le dará al jugador la opción de reiniciar su juego. Cuando el jugador haga clic en el botón, se ejecutará la función reiniciarJuego():

...
</body>
    ...

    <div>
        <button onclick="reiniciarJuego()">Reiniciar el juego</button>
    </div>

    ...
</body>
</html>

Modificar el archivo «script.js»

Añadiremos una nueva función JavaScript que agrupará el código que se debe ejecutar cuando el usuario desee reiniciar el juego:

  • Función reiniciarJuego(): Esta función se ejecuta cuando el jugador hace clic en el botón de reiniciar. Primero, se le pregunta al jugador si realmente quiere reiniciar (usando confirm()). Si el jugador acepta, la función borra el progreso guardado en localStorage y recarga la página:
...

// Función para reiniciar el juego
function reiniciarJuego() {
    // Pregunta al usuario si realmente quiere reiniciar, y si dice que sí, borra el progreso y reinicia la página
    if (confirm("¿Estás seguro de borrar todo el progreso? (Esto no se puede deshacer)")) {
        localStorage.removeItem('juego');  // Elimina el progreso guardado
        location.reload();  // Recarga la página
    }
}

Cookie Clicker con HTML+CSS+Javascript (parte 3): Guardando el progreso

En esta parte aprenderemos a guardar el progreso del juego usando la memoria del navegador, para que el jugador no pierda su avance cuando cierre la página. Veremos cómo utilizar el almacenamiento local para almacenar cualquier tipo de información, de forma que podamos recuperarla después de actualizar o cerrar nuestra página, o incluso después de haber cerrado el navegador.

¿Qué es localStorage?

localStorage es una pequeña «libreta virtual» donde podemos guardar información directamente en el navegador del usuario. A diferencia de la memoria temporal (que se pierde al cerrar la página), los datos guardados en localStorage persisten incluso después de cerrar el navegador o apagar el ordenador.

¿Por qué usar localStorage?

En un juego como Cookie Clicker, queremos que el progreso del usuario (por ejemplo, cuántas galletas ha acumulado o cuántos ayudantes tiene) se mantenga aunque cierre el navegador. localStorage nos permite almacenar este tipo de información de manera sencilla.

¿Cómo funciona localStorage?

localStorage solo guarda datos en formato de texto. Para almacenar datos complejos (como un objeto con el estado del juego), usamos el formato JSON, convirtiendo nuestros datos a texto y viceversa con dos sencillas funciones:

  • JSON.stringify(): Convierte datos complejos a texto.
  • JSON.parse(): Convierte ese texto de vuelta a datos.

¿Qué es es JSON?

JSON es un formato utilizado para almacenar y enviar datos. Su estructura es muy sencilla y está basada en pares clave-valor:

  • Claves: Son los nombres de los datos (por ejemplo, «galletas»).
  • Valores: Son los datos asociados a cada clave (por ejemplo, 200).

Usamos JSON para convertir objetos complejos en cadenas de texto que pueden ser almacenadas en localStorage. Luego, podemos convertirlos de vuelta a objetos que podamos utilizar dentro del código.

Modificar el archivo «script.js»

Utilizando las siguientes funciones podremos guardar y recuperar la cantidad de galletas acumuladas y las mejoras o logros conseguidos durante el juego:

  • Función guardarProgreso(): Esta función guarda los datos del juego en el almacenamiento local del navegador usando localStorage. Los datos se convierten en formato JSON para que el navegador pueda almacenarlos como texto.
  • Función cargarProgreso(): Al iniciar el juego, esta función carga los datos guardados desde localStorage, conviertiendo la cadena JSON en un objeto y verificando que los datos sean correctos antes de usarlos.
...

// Función para comprar una mejora
// "mejora" es el objeto que representa la mejora que el jugador intenta comprar
function comprarMejora(nombreMejora) {
    const mejora = juego.mejoras[nombreMejora];
    if (juego.galletas >= mejora.precio) {  // Verifica si el jugador tiene suficientes galletas
        ...
        guardarProgreso();  // Guarda el progreso después de la compra
    } 
    ...
}

// Función para incrementar el contador de galletas cuando el jugador hace clic
function incrementarContador() {
    ...
    guardarProgreso();  // Guarda el progreso en la memoria del navegador para no perder las galletas conseguidas
}

// Función para guardar el progreso del juego
function guardarProgreso() {
    // Genera una cadena de texto cont todos los datos del juego y lo guarda en el navegador.
    localStorage.setItem('juego', JSON.stringify(juego));

    // Después de guardar, actualiza el progreso visualmente
    mostrarProgreso();  
}

// Función para cargar el progreso guardado
function cargarProgreso() {
    // Recupera los datos del juego guardados previamente
    let juegoGuardado = localStorage.getItem('juego');

    // Si el jugador ha guardado su progreso previamente, lo recuperamos y lo cargamos
    if (juegoGuardado) {
        juegoGuardado = JSON.parse(juegoGuardado);  // Convierte la cadena JSON a un objeto

        // Comprueba si los datos guardados previamente son correctos
        if (datosCorrectos(juegoGuardado)) {
            juego = juegoGuardado;  // Recupera los datos guardados y actualiza el estado del juego
        } else {
            guardarProgreso();  // Si los datos son incorrectos, guarda los datos actuales
        }
        mostrarProgreso();  // Actualiza visualmente el progreso
    }
}

// Función para inicializar el juego
function inicializar() {
    cargarProgreso();  // Carga el progreso guardado al iniciar el juego
}

inicializar();  // Llama a la función para iniciar el juego

Añadir la función de verificación en «utils.js»

A medida que vayamos actualizando la funcionalidad del juego, podrían haber algunas inconsistencias entre los datos guardados y los que tenga la nueva versión de nuestro juego. Por ello necesitaremos una función que compruebe si podemos recuperar la información guardada para utilizarla directamente:

  • Función datosCorrectos(datos): Esta función verifica que los datos guardados tienen las mismas propiedades que el objeto juego. De esta forma, si hay diferencias o los datos están corruptos, podremos evitar cargar datos incorrectos.
...

// Función para comprobar si los datos recibidos como parámetro tienen las mismas propiedades que el objeto 'juego'
function datosCorrectos(datos) {
    // Si no es un objeto o es null, devuelve false.
    if (typeof datos !== 'object' || datos === null) return false;

    // Obtener las claves (propiedades) de 'datos' y 'juego'.
    const keys1 = Object.keys(datos);
    const keys2 = Object.keys(juego);

    // Si el número de propiedades es distinto, devuelve false.
    if (keys1.length !== keys2.length) return false;

    // Comprobar que todas las claves de 'datos' están también en 'juego'.
    return keys1.every(key => keys2.includes(key));
}

Cookie Clicker con HTML+CSS+Javascript (parte 2): Galletas extra por cada click

En esta unidad vamos a explicar cómo agregar una mejora al Cookie Clicker que permita ganar galletas extra con cada clic.

Imagen para activar la mejora

Al hacer click sobre esta imagen activarás la mejora de galletas extra. Puedes elegir cualquier imagen para tu juego, teniendo en cuenta simplemente que deberás colocarla dentro de la carpeta img. A continuación te proporcionamos una imagen de ejemplo:

Modificaciones del fichero «index.html»

Primero añadiremos una nueva imagen que represente una mejora que podremos comprar dentro de nuestro juego. Cuando el usuario haga clic en esta imagen, podrá comprar una mejora que le dará galletas extra por cada clic:

  • Para poder representar la mejora, utilizaremos una imagen adicional que meteremos dentro de un bloque div.
  • Al hacer clic sobre el bloque div, el jugador podrá adquirir la mejora si tiene suficientes galletas. Este proceso se llevará a cabo en la función comprarMejora('galletasExtra'), que crearemos a continuación en un fichero JavaScript.
...
<body>
    ...
    <div onclick="comprarMejora('galletasExtra')">
      <img src="img/muchas-galletas.png" width="125">
      <div id="textoGalletasExtra"></div>
    </div>

    <script src="variables.js"></script>
    <script src="utils.js"></script>
    <script src="script.js"></script>
</body>
</html>

Modificaciones del fichero «variables.js»

En este archivo, añadiremos una propiedad nueva dentro del objeto juego para almacenar las mejoras del juego, incluyendo la cantidad de galletas extra que el jugador ganará con cada clic y el precio de esta mejora:

  • La mejora tiene un precio (5 galletas) y un contador que muestra cuántas veces ha sido comprada.
  • Dentro de juego, ahora tenemos una sección llamada mejoras, que incluye la mejora de galletas extra.
// Objeto que almacena toda la información relativa al progreso y configuración del juego
let juego = {
    galletas: 0, // Cantidad total de galletas conseguidas
    mejoras: {
        galletasExtra: { cantidad: 0, precio: 100, descripcion: "Galletas extra por cada click" }, // Mejora de galletas extras por click
    }
}

Fichero «utils.js»

Este archivo contendrá funciones auxiliares, como una que muestra mensajes al jugador. Por ejemplo, cuando compra una mejora o cuando no tiene suficientes galletas:

  • mostrarMensaje(mensaje) muestra una ventana emergente con un mensaje. Es útil para informar al jugador si ha comprado una mejora o si no tiene suficientes galletas.
// Función para mostrar mensajes por pantalla, para avisar por ejemplo de las mejoras adquiridas
function mostrarMensaje(mensaje) {
    alert(mensaje);
}

Modificaciones del fichero «script.js»

Aquí vamos a modificar las funciones que actualizan el contador de galletas y que permiten comprar mejoras:

  • Función incrementarContador(): Ahora esta función no solo aumentará el contador de galletas en 1, sino que también sumará las galletas extra si el jugador ha comprado la mejora.
  • Función comprarMejora(nombreMejora): Esta función permite al jugador comprar mejoras. Verifica si tiene suficientes galletas y, si es así, descuenta el precio, aumenta la cantidad de la mejora y muestra un mensaje de confirmación.
...

// Función para incrementar el contador de galletas cuando el jugador hace clic en la galleta
function incrementarContador() {
    juego.galletas++;  // Aumenta la cantidad de galletas
    juego.galletas += juego.mejoras.galletasExtra.cantidad;  // Suma las galletas extra si se ha comprado la mejora    
    mostrarProgreso();  // Muestra la cantidad de galletas
}

// Función para comprar una mejora
// "mejora" es el objeto que representa la mejora que el jugador intenta comprar
function comprarMejora(nombreMejora) {
    const mejora = juego.mejoras[nombreMejora];
    if (juego.galletas >= mejora.precio) {  // Verifica si el jugador tiene suficientes galletas
        juego.galletas -= mejora.precio;  // Resta el precio de las galletas
        mejora.cantidad++;  // Aumenta la cantidad de la mejora
        mostrarMensaje(`¡Tienes ${mejora.cantidad} x ${mejora.descripcion}!`);  // Muestra un mensaje de confirmación
        mostrarProgreso();  // Muestra la cantidad de galletas después de pagar la mejora
    } else {
        mostrarMensaje("¡No tienes suficientes galletas!", 'warning');  // Si no tiene galletas suficientes, muestra una advertencia
    }
}

Cookie Clicker con HTML+CSS+Javascript (parte 1): Contando galletas

En esta parte implementaremos la funcionalidad básica del juego, es decir, un contador que acumule galletas a media que vayamos haciendo clicks.

La galleta

Puedes elegir cualquier imagen para tu juego. A continuación te proporcionamos una de ejemplo:

Como iremos utilizando más imágenes a medida que vayamos añadiendo funcionalidad a nuestro juego, lo ideal es crear una carpeta img que contendrá todas las imágenes, y así las mantendremos agrupadas y separadas del resto de ficheros.

Estructura del archivo «index.html»

El archivo HTML define cómo se verá el juego en el navegador. Aquí vamos a crear una página con un título, una imagen de una galleta, y un contador que muestra cuántas veces se ha hecho clic en la galleta:

  • Primero incluiremos las etiquetas básicas de HTML. Por ejemplo, utilizaremos <!DOCTYPE html> para especificar que estamos usando HTML5.
  • En la sección <head>:
    • Seleccionaremos el idioma de la página.
    • El juego tendrá un título (que aparecerá en la pestaña del navegador).
    • Especificaremos que nuestra aplicación será adaptable a diferentes tamaños de pantalla.
  • En la sección <body>:
    • Colocaremos el título visible «Cookie Clicker».
    • Añadiremos la imagen de la galleta sobre la que el usuario puede hacer clic.
    • Por último definiremos un div donde aparecerá el contador de clics.
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cookie Clicker</title>
</head>
<body>
    <h1>Cookie Clicker</h1>

    <div onclick="incrementarContador()">
        <img src="img/galleta.png" alt="Galleta" width="200">
        <div id="contador">0</div>
    </div>

    <script src="variables.js"></script> 
    <script src="script.js"></script>
</body>
</html>

Código JavaScript «variables.js»

Este archivo contiene una variable llamada juego, que almacena la información relacionada con el estado del juego, como el número de galletas que ha conseguido el jugador.

Definimos un objeto llamado juego, que contiene las propiedades del juego. Aquí, solo vamos a almacenar el número de galletas que el jugador ha ganado.

// Objeto que almacena toda la información relativa al progreso y configuración del juego
let juego = {
    galletas: 0, // Cantidad total de galletas conseguidas
}

Código JavaScript «script.js»

Este archivo maneja la lógica del juego. Cada vez que el jugador hace clic en la galleta, el número de galletas aumenta y se actualiza el contador en la pantalla:

  • Función mostrarProgreso(): Esta función actualiza el título del navegador para mostrar cuántas galletas se han conseguido. Además, cambia el texto en el contador de la página para reflejar la cantidad actualizada.
  • Función incrementarContador(): Esta función es llamada cada vez que se hace clic en la galleta. Incrementa el número de galletas en el objeto juego y llama a mostrarProgreso() para actualizar el resultado en la ventana del navegador.
// Función para mostrar al usuario los progresos actualizados a medida que van cambiando los datos
function mostrarProgreso() {
    // Cambia el título de la pestaña del navegador para mostrar el número de galletas
    document.title = `Cookie Clicker - ${juego.galletas} galletas`;

    // Actualiza el contador de galletas en pantalla
    document.getElementById('contador').textContent = juego.galletas.toLocaleString();
}

// Función para incrementar el contador de galletas cuando el jugador hace clic en la galleta
function incrementarContador() {
    juego.galletas++;  // Aumenta la cantidad de galletas
    mostrarProgreso();  // Muestra la cantidad de galletas
}

Cookie Clicker con HTML+CSS+Javascript

En las siguientes unidades aprenderemos a desarrollar nuestra propia versión del popular juego Cookie Clicker, utilizando simplemente tecnologías web. Además, podremos compilar nuestra aplicación en formato «apk» para instalarla en cualquier dispositivo móvil.

Contando galletas

Galletas extra por cada click

Guardando el progreso

Reiniciando el progreso del juego

Añadiendo icono y estilos

Compilando la aplicación para móvil

Auto clickers

Comprando abuelas

Comprando granjas

Comprando fábricas

El resultado

Puedes probar el resultado en este enlace.

Manual básico de Matplotlib

Introducción a Matplotlib

¿Qué es Matplotlib?

Matplotlib es una librería de Python utilizada para crear visualizaciones de datos en forma de gráficos 2D. Es altamente versátil y permite generar una amplia gama de gráficos, desde simples gráficos de líneas y barras hasta complejos gráficos de dispersión, histogramas, gráficos de pastel, entre otros. Matplotlib es particularmente popular en campos como ciencia de datos, estadística e ingeniería, ya que ofrece una manera fácil y flexible de representar visualmente datos de manera clara y comprensible. La librería se integra bien con otras herramientas de análisis de datos en Python, como NumPy y Pandas, lo que facilita el análisis y la visualización de grandes conjuntos de datos. Con Matplotlib, los usuarios pueden personalizar cada aspecto de un gráfico, desde los colores y estilos de línea hasta la inclusión de leyendas, títulos y anotaciones, lo que la convierte en una excelente herramienta para crear visualizaciones que representen la información de manera eficaz.

Instalación

Para comenzar a utilizar Matplotlib, necesitas instalar la librería en tu entorno de Python. Esto se hace fácilmente utilizando pip, que es el gestor de paquetes de Python. Este comando descargará e instalará la última versión de Matplotlib y sus dependencias:

pip install matplotlib

Importar Matplotlib

En Python, las librerías se importan para acceder a sus funcionalidades. Para Matplotlib, la parte que se utiliza con mayor frecuencia es pyplot, un módulo que proporciona una interfaz de alto nivel para crear gráficos. Se suele importar con el alias plt para simplificar su uso. De hecho, este alias plt es un estándar de facto y verás que la mayoría de los ejemplos y tutoriales utilizan esta convención:

import matplotlib.pyplot as plt

En entornos donde no se tiene una pantalla (como en servidores web o scripts que generan gráficos sin mostrarlos), se recomienda utilizar matplotlib.use('Agg') para configurar Matplotlib en un backend que no requiere una interfaz gráfica:

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

Creación de gráficos básicos

Gráfico de líneas

Los gráficos de líneas son uno de los tipos de gráficos más comunes y se utilizan para mostrar la relación entre dos variables continuas. Son especialmente útiles para mostrar cómo cambia una variable en función del tiempo u otra métrica continua.

Ejemplo: Ventas a lo largo del año

import matplotlib.pyplot as plt

# Datos
meses = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio']
ventas = [150, 200, 250, 300, 350, 400]

# Crear el gráfico
plt.plot(meses, ventas)

# Añadir títulos y etiquetas
plt.title("Ventas Mensuales")
plt.xlabel("Meses")
plt.ylabel("Ventas en $")

# Mostrar el gráfico
plt.show()

Explicación:

  • plt.plot(meses, ventas) genera una línea que conecta los puntos definidos por las listas meses y ventas.
  • plt.title, plt.xlabel, y plt.ylabel se usan para añadir un título y etiquetas a los ejes.
  • plt.show() muestra el gráfico en la pantalla.

Gráfico de barras

Los gráficos de barras son ideales para comparar diferentes categorías. Cada barra representa una categoría, y su altura o longitud representa la magnitud de la variable asociada a esa categoría.

Ejemplo: Comparación de ventas por producto

productos = ['Producto A', 'Producto B', 'Producto C']
ventas = [300, 450, 150]

plt.bar(productos, ventas)

plt.title("Ventas por Producto")
plt.xlabel("Productos")
plt.ylabel("Ventas en $")

plt.show()

Explicación:

  • plt.bar(productos, ventas) crea un gráfico de barras donde cada barra corresponde a un producto y su altura muestra el volumen de ventas.
  • Este tipo de gráfico es útil para visualizar comparaciones directas entre diferentes elementos.

Gráfico de dispersión

Un gráfico de dispersión es útil cuando deseas visualizar la relación entre dos variables numéricas. Cada punto en el gráfico representa un par de valores.

Ejemplo: Relación entre publicidad y ventas

publicidad = [100, 200, 300, 400, 500]
ventas = [10, 20, 30, 40, 50]

plt.scatter(publicidad, ventas)

plt.title("Relación entre Publicidad y Ventas")
plt.xlabel("Publicidad en $")
plt.ylabel("Ventas en $")

plt.show()

Explicación:

  • plt.scatter(publicidad, ventas) dibuja un punto para cada par de valores en las listas publicidad y ventas.
  • Este gráfico es excelente para identificar patrones, tendencias o posibles correlaciones entre las variables.

Histograma

Los histogramas se utilizan para mostrar la distribución de un conjunto de datos. Dividen el rango de datos en intervalos (o «bins») y cuentan cuántos valores caen en cada intervalo.

Ejemplo: Distribución de edades en un grupo

edades = [23, 25, 26, 29, 30, 32, 35, 36, 40, 41, 42, 45, 50, 55, 60]

plt.hist(edades, bins=5)  # 'bins' define el número de barras en el histograma

plt.title("Distribución de Edades")
plt.xlabel("Edades")
plt.ylabel("Número de Personas")

plt.show()

Explicación:

  • plt.hist(edades, bins=5) crea un histograma que divide los datos en 5 intervalos y cuenta cuántos valores caen en cada intervalo.
  • Los histogramas son útiles para comprender la distribución de los datos, por ejemplo, si los datos están sesgados hacia un extremo o si tienen una distribución normal.

Personalización de gráficos

Colores y estilos de líneas

Matplotlib permite personalizar el color y el estilo de las líneas en los gráficos, lo que es útil para mejorar la claridad y la estética de la visualización.

Ejemplo: Personalización de un gráfico de líneas

dias = ['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes']
temperaturas = [22, 24, 20, 21, 19]

plt.plot(dias, temperaturas, color='green', linestyle='--', marker='o')

plt.title("Temperaturas Diarias")
plt.xlabel("Días")
plt.ylabel("Temperatura (°C)")

plt.show()

Explicación:

  • color='green' cambia el color de la línea a verde.
  • linestyle='--' convierte la línea en discontinua.
  • marker='o' añade marcadores de círculos en cada punto de datos.

Añadir leyendas

Las leyendas identifican las distintas series de datos en un gráfico, lo que es esencial cuando se comparan varias series.

Ejemplo: Comparación de ventas de dos productos

meses = ['Enero', 'Febrero', 'Marzo', 'Abril']
ventas_producto_a = [200, 220, 250, 270]
ventas_producto_b = [180, 190, 200, 210]

plt.plot(meses, ventas_producto_a, label='Producto A')
plt.plot(meses, ventas_producto_b, label='Producto B')

plt.title("Comparación de Ventas")
plt.xlabel("Meses")
plt.ylabel("Ventas en $")
plt.legend()  # Añadir leyenda

plt.show()

Explicación:

  • label='Producto A' y label='Producto B' especifican el nombre de cada línea en la leyenda.
  • plt.legend() coloca la leyenda en el gráfico.

Gráficos con subtramas

Los subgráficos permiten colocar múltiples gráficos en una sola figura, organizándolos en una cuadrícula.

Ejemplo: Gráficos de ventas y beneficios

meses = ['Enero', 'Febrero', 'Marzo', 'Abril']
ventas = [250, 300, 350, 400]
beneficios = [50, 60, 70, 80]

fig, (ax1, ax2) = plt.subplots(1, 2)  # Una fila y dos columnas

ax1.plot(meses, ventas)
ax1.set_title("Ventas")

ax2.plot(meses, beneficios, color='red')
ax2.set_title("Beneficios")

plt.show()

Explicación:

  • plt.subplots(1, 2) crea una figura con una fila y dos columnas de subgráficos.
  • ax1.plot(...) y ax2.plot(...) permiten dibujar gráficos en los diferentes ejes.

Gráficos avanzados

Gráfico de series temporales

Los gráficos de series temporales son fundamentales para mostrar cómo cambian los datos a lo largo del tiempo.

Ejemplo: Evolución de precios de acciones

import pandas as pd
import matplotlib.pyplot as plt

# Datos de ejemplo
fechas = pd.date_range(start='2023-01-01', periods=6, freq='M')
precios = [100, 105, 102, 108, 110, 115]

plt.plot(fechas, precios, marker='o')

plt.title("Evolución de Precios de Acciones")
plt.xlabel("Fecha")
plt.ylabel("Precio ($)")
plt.xticks(rotation=45)

plt.show()

Explicación:

  • pd.date_range(start='2023-01-01', periods=6, freq='M') genera un rango de fechas mensuales a partir de enero de 2023.
  • Este gráfico es ideal para mostrar datos financieros, meteorológicos, u otras métricas que varían con el tiempo.

Gráfico de pastel

Los gráficos de pastel se utilizan para mostrar la proporción de diferentes categorías en un conjunto de datos.

Ejemplo: Distribución de ventas por región

regiones = ['Norte', 'Sur', 'Este', 'Oeste']
ventas = [400, 300, 150, 150]

plt.pie(ventas, labels=regiones, autopct='%1.1f%%')

plt.title("Distribución de Ventas por Región")

plt.show()

Explicación:

  • plt.pie(ventas, labels=regiones, autopct='%1.1f%%') crea un gráfico de pastel y muestra los porcentajes de cada categoría en el gráfico.
  • Este tipo de gráfico es útil para mostrar cómo se distribuyen los datos en diferentes categorías.

Gráfico de barras apiladas

Los gráficos de barras apiladas son útiles para mostrar cómo diferentes subcomponentes contribuyen a un total.

Ejemplo: Ventas por categoría en diferentes meses

import numpy as np

meses = ['Enero', 'Febrero', 'Marzo', 'Abril']
categoria_a = [20, 35, 30, 35]
categoria_b = [25, 32, 34, 20]
categoria_c = [30, 30, 30, 30]

ind = np.arange(len(meses))  # La posición de los grupos
ancho = 0.35  # El ancho de las barras

p1 = plt.bar(ind, categoria_a, ancho)
p2 = plt.bar(ind, categoria_b, ancho, bottom=categoria_a)
p3 = plt.bar(ind, categoria_c, ancho, bottom=np.array(categoria_a)+np.array(categoria_b))

plt.ylabel('Ventas')
plt.title('Ventas por Categoría y Mes')
plt.xticks(ind, meses)
plt.legend(('Categoría A', 'Categoría B', 'Categoría C'))

plt.show()

Explicación:

  • bottom=categoria_a apila las barras de categoria_b sobre las de categoria_a.
  • Este gráfico es útil para ver la contribución relativa de diferentes categorías a lo largo del tiempo.

Guardar y compartir gráficos

Guardar gráficos en un archivo

Matplotlib permite guardar gráficos en diferentes formatos de archivo como PNG, PDF, SVG, entre otros. Esto es especialmente útil si deseas incluir gráficos en informes o presentaciones.

Ejemplo: Guardar un gráfico como PNG

x = [1, 2, 3, 4, 5]
y = [10, 20, 25, 30, 40]

plt.plot(x, y)

plt.title("Gráfico para Guardar")
plt.xlabel("Eje X")
plt.ylabel("Eje Y")

plt.savefig('grafico_guardado.png')  # Guarda el gráfico como PNG

Explicación:

  • plt.savefig('grafico_guardado.png') guarda el gráfico en el archivo grafico_guardado.png en el formato especificado.
  • Es posible cambiar el formato del archivo guardándolo con una extensión diferente, como .pdf o .svg.

Uso de BytesIO para generar gráficos en memoria

En algunos casos, no es necesario guardar los gráficos en el disco como archivos para compartirlos o visualizarlos en otras aplicaciones. Por ejemplo, en aplicaciones web se pueden enviar los gráficos dinámicamente al navegador sin crear archivos temporales. Aquí es donde entra en juego BytesIO, una clase de la librería estándar io en Python, que actúa como un contenedor de datos en memoria que se comporta como un archivo. Puedes escribir en él, leer de él, y tratarlo como si fuera un archivo regular, pero todo ocurre en la memoria RAM en lugar de en el disco duro.

Además, BytesIO no necesita ser instalado por separado porque forma parte de la biblioteca estándar de Python, específicamente dentro del módulo io. Esto significa que BytesIO está disponible automáticamente en cualquier instalación de Python moderna (Python 3 y superior) y no requiere ninguna instalación adicional:

from io import BytesIO

Ejemplo: Generación de un gráfico y almacenamiento en BytesIO

A continuación se muestra cómo se puede crear un gráfico con Matplotlib, almacenarlo en un objeto BytesIO en lugar de guardarlo en un archivo físico, y luego usar plt.close() para liberar recursos de manera eficiente:

import matplotlib.pyplot as plt
from io import BytesIO

# Datos de ejemplo
x = [1, 2, 3, 4, 5]
y = [10, 20, 25, 30, 35]

# Crear un gráfico
plt.plot(x, y)
plt.title("Gráfico en Memoria")
plt.xlabel("Eje X")
plt.ylabel("Eje Y")

# Crear un objeto BytesIO para almacenar el gráfico
img = BytesIO()

# Guardar el gráfico en el objeto BytesIO en formato PNG
plt.savefig(img, format='png')

# Colocar el puntero del archivo en memoria al inicio
# Ahora `img` contiene el gráfico en formato PNG, listo para ser enviado o procesado
img.seek(0)

# Cerrar la figura para liberar memoria
plt.close()

Explicación:

  • BytesIO() crea un objeto en memoria que se comporta como un archivo.
  • plt.savefig(img, format='png') guarda el gráfico en el objeto BytesIO en formato PNG.
  • img.seek(0) reposiciona el puntero al inicio del archivo en memoria para poder leerlo desde el principio.
  • plt.close() cierra la figura actual y libera los recursos de memoria asociados al gráfico. Esto es especialmente importante en aplicaciones que generan muchos gráficos, ya que evita el consumo excesivo de memoria.

Uso de BytesIO en Aplicaciones web con Flask

Una de las aplicaciones más comunes de BytesIO es en el desarrollo de aplicaciones web con frameworks como Flask. En estas aplicaciones, los gráficos se generan en respuesta a las solicitudes HTTP y se envían directamente al usuario para que los pueda visualizar en el navegador:

from flask import Flask, send_file
import matplotlib.pyplot as plt
from io import BytesIO

app = Flask(__name__)

@app.route('/grafico')
def generar_grafico():
    # Datos de ejemplo
    x = [1, 2, 3, 4, 5]
    y = [10, 20, 25, 30, 35]

    # Crear un gráfico
    plt.plot(x, y)
    plt.title("Gráfico Generado en Flask")
    plt.xlabel("Eje X")
    plt.ylabel("Eje Y")

    # Crear un objeto BytesIO
    img = BytesIO()

    # Guardar el gráfico en el objeto BytesIO en formato PNG
    plt.savefig(img, format='png')
    img.seek(0)  # Reposicionar el puntero al inicio del archivo en memoria

    # Cerrar la figura para liberar memoria
    plt.close()

    # Enviar el gráfico como respuesta HTTP
    return send_file(img, mimetype='image/png')

if __name__ == '__main__':
    app.run(debug=True)

Explicación:

  • send_file(img, mimetype='image/png') envía el contenido del BytesIO al cliente web como una imagen PNG.
  • plt.close() cierra la figura una vez que el gráfico ha sido guardado en memoria, asegurando que no queden gráficos abiertos que puedan consumir memoria innecesariamente.
  • Esta configuración permite que cada vez que un usuario acceda a la ruta /grafico, se genere un gráfico dinámicamente en memoria y se envíe al navegador.

Ventajas del uso de BytesIO y plt.close()

  • Eficiencia: No se necesita escribir archivos temporales en disco, lo que reduce el I/O del disco y mejora el rendimiento. Además, plt.close() asegura que la memoria se libere inmediatamente después de generar el gráfico.
  • Seguridad: Al no crear archivos temporales, se minimiza el riesgo de dejar datos sensibles en el disco.
  • Flexibilidad: Este enfoque se puede utilizar con aplicaciones que requieren gráficos dinámicos, como servicios web o scripts automatizados, sin preocuparse por el manejo de archivos en el sistema.

Ejemplos reales

Análisis de datos meteorológicos

Este ejemplo muestra cómo crear un gráfico con la evolución de la temperatura a lo largo de un día. Es común en aplicaciones de monitoreo del clima o en la presentación de datos meteorológicos:

import matplotlib.pyplot as plt

horas = ['6 AM', '9 AM', '12 PM', '3 PM', '6 PM', '9 PM']
temperaturas = [15, 18, 22, 26, 23, 20]

plt.plot(horas, temperaturas, marker='o', linestyle='-', color='blue')

plt.title("Evolución de la Temperatura")
plt.xlabel("Hora del Día")
plt.ylabel("Temperatura (°C)")
plt.grid(True)

plt.show()

Explicación:

  • marker='o', linestyle='-', color='b' personaliza la línea del gráfico.
  • plt.grid(True) añade una cuadrícula al gráfico para mejorar la legibilidad.

Análisis de datos financieros

Con el siguiente código creamos un gráfico que muestra la evolución del precio de cierre de las acciones de una empresa durante una semana. Este tipo de gráfico es fundamental en finanzas para analizar el rendimiento de acciones, bonos, etc.:

import matplotlib.pyplot as plt

dias = ['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes']
precios = [150, 152, 148, 155, 160]

plt.plot(dias, precios, marker='o', linestyle='-', color='green')

plt.title("Evolución del precio de las acciones")
plt.xlabel("Días de la Semana")
plt.ylabel("Precio de la Acción ($)")
plt.grid(True)

plt.show()

Explicación:

  • plt.grid(True) añade una cuadrícula al gráfico para facilitar la comparación entre los días.

Análisis de resultados de exámenes

Este ejemplo muestra cómo comparar los resultados de dos clases en un examen. Puede resultar últil para analizar el rendimiento de los estudiantes de grupos diferentes:

import matplotlib.pyplot as plt

clases = ['Clase A', 'Clase B']
promedio_matematicas = [78, 85]
promedio_lenguaje = [82, 88]

x = range(len(clases))

plt.bar(x, promedio_matematicas, width=0.4, label='Matemáticas', align='center')
plt.bar(x, promedio_lenguaje, width=0.4, label='Lenguaje', align='edge')

plt.xlabel('Clases')
plt.ylabel('Promedio de Examen')
plt.title('Comparación de Resultados de Exámenes por Clase')
plt.xticks(x, clases)
plt.legend()

plt.show()

Explicación:

  • plt.bar(x, promedio_matematicas, width=0.4, label='Matemáticas', align='center') dibuja barras para Matemáticas.
  • plt.bar(x, promedio_lenguaje, width=0.4, label='Lenguaje', align='edge') dibuja barras para Lenguaje justo al lado de las de Matemáticas.
  • plt.legend() añade una leyenda que identifica qué barras corresponden a cada materia.

Recursos adicionales para aprender Matplotlib

Aprender a utilizar Matplotlib a fondo requiere tiempo y práctica, pero afortunadamente, la documentación oficial está muy bien redactada y organizada, y se actualiza con frecuencia. Además, dispone de una extensa galería de ejemplos donde puedes consultar muchos fragmentos de código, y observar el resultado generado.

Documentación oficial

La documentación oficial de Matplotlib es el recurso más completo y fiable. Este es el lugar al que debes recurrir cuando necesites información específica sobre cómo hacer algo con Matplotlib. Aquí encontrarás información detallada sobre todas las funciones y características. La documentación incluye:

  • Tutoriales: Introducciones paso a paso para principiantes que cubren desde los gráficos más básicos hasta técnicas avanzadas.
  • Guías de Uso: Explicaciones detalladas sobre cómo usar diversas partes de la API de Matplotlib.
  • Referencia de API: Una lista exhaustiva de todas las funciones, métodos y clases disponibles en Matplotlib, junto con ejemplos de cómo usarlos.
  • Ejemplos de Código: Pequeños fragmentos de código que ilustran cómo lograr resultados específicos con Matplotlib.

Galería de ejemplos

La galería de ejemplos de Matplotlib te proporciona un excelente recurso visual que muestra una gran cantidad de gráficos que puedes crear con Matplotlib. Cada ejemplo en la galería está acompañado del código fuente, lo que te permite ver exactamente cómo se generó el gráfico y experimentar por ti mismo. Esta sección de la documentación oficial incluye:

  • Cientos de ejemplos: La galería es ideal para obtener ideas sobre cómo visualizar tus propios datos de manera efectiva.
  • Código listo para usar: Puedes copiar y pegar el código directamente en tu propio proyecto y ajustarlo según tus necesidades.
  • Tipos de gráficos muy diversos: Desde gráficos de líneas y barras hasta gráficos más complejos como gráficos de violín, mapas de calor, y gráficos 3D.