Tarea de programación C: El juego de la vida de Conway

El juego de la vida de Conway es una tarea clásica de cualquier curso de programación. El juego consiste en un conjunto de reglas sencillas que definen si las células en un tablero viven o mueren. Es interesante observar cómo estas sencillas reglas pueden formar patrones complejos.

conway

La versión del juego de la vida en este post fue escrita en C con Allegro 5 como un reto de programación de 24 horas y decidí usar un tablero de tamaño relativamente grande, una ventana de 800×600 pixeles donde cada pixel es una célula, ya que algunos patrones complejos son difíciles de observar si se elige un tablero muy pequeño.

Hay que tener en cuenta que, debido al límite de tiempo, sólo estaba enfocado en terminar el reto aunque el código no fuera muy eficiente, por lo que esta versión puede fácilmente llevarse el 100% del CPU. Tengo algunas ideas para optimizar el código, editaré este post cuando aplique los cambios.

Para compilar en Linux simplemente descarga el código desde github y compila usando cmake y make:

monstruosoft@debian:~/life$ mkdir build
monstruosoft@debian:~/life$ cd build
monstruosoft@debian:~/life/build$ cmake ..
monstruosoft@debian:~/life/build$ make

El programa también debe compilarse correctamente en Windows si tienes Allegro 5 instalado.

[EDIT:] He actualizado el código en github. Las optimizaciones que hice no ayudaron mucho a reducir el uso del CPU durante la simulación pero al menos se corrigió el uso del 100% del CPU cuando la simulación estaba inactiva.

Tarea de programación Java: convertir a números romanos

El código Java para esta tarea es prácticamente idéntico al código C del post anterior, sólo tenemos que ponerlo dentro de un método de una clase. En este caso podemos usar la clase TareasProgramación que ya hemos usado en posts anteriores.

public class TareasProgramacion {
    private static final String ROMANOS_UNIDADES[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
    private static final String ROMANOS_DECENAS[]  = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
    private static final String ROMANOS_CENTENAS[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
    private static final String ROMANOS_MILES[]    = {"", "M", "MM", "MMM"};

    public static String cantidadNumerosRomanos(String s) {
        StringBuilder result = new StringBuilder();
        BigDecimal totalBigDecimal = new BigDecimal(s).setScale(2, BigDecimal.ROUND_DOWN);
        long parteEntera = totalBigDecimal.toBigInteger().longValue();

        if (parteEntera  3999)
            throw new IllegalArgumentException("El número a convertir debe estar entre 1 y 3999.");

        int m = (int)parteEntera / 1000, c = (int)(parteEntera % 1000) / 100, d = (int)(parteEntera % 100) / 10, un = (int)parteEntera % 10;
        result.append(ROMANOS_MILES[m]);
        result.append(ROMANOS_CENTENAS[c]);
        result.append(ROMANOS_DECENAS[d]);
        result.append(ROMANOS_UNIDADES[un]);

        return result.toString();
    }
}

Una ejecución de un programa de prueba para el código anterior luce como la siguiente:

Escribe un número entre 1 y 3999: 
12
XII
Escribe un número entre 1 y 3999: 
1999
MCMXCIX
Escribe un número entre 1 y 3999: 
2018
MMXVIII
Escribe un número entre 1 y 3999: 
3888
MMMDCCCLXXXVIII
Escribe un número entre 1 y 3999: 
3999
MMMCMXCIX

Para obtener el código completo visita el repositorio de github de monstruosoft.

Tarea de programación C: Cantidad a números romanos

Una clásica tarea de programación, convertir una cantidad a números romanos. Normalmente verás esta tarea soportar valores en el rango de 1 a 3999 por obvias razones. Esta es la versión en lenguaje C, la versión Java en el siguiente post.

// Tarea de programación - Números romanos
#include <stdio.h>
#include <stdlib.h>

const char *miles[]    = {"", "M", "MM", "MMM"};
const char *cientos[]  = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
const char *decenas[]  = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
const char *unidades[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};

int main() {
    int a;

    printf("Escribe un numero entre 1 y 3999: ");
    scanf("%d", &a);

    if (a < 1 || a > 3999) {
        printf("Debes escribir un número entre 1 y 3999.\n");
        exit(0);
    }

    int m = a / 1000, c = (a % 1000) / 100, d = (a % 100) / 10, un = a % 10;
    printf("El número %d se escribe así en números romanos: %s%s%s%s\n",
            a, miles[m], cientos[c], decenas[d], unidades[un]);
}

 

Tarea de programación Java: convertir números a texto

En este post escribiremos una clase de Java que nos permita convertir un número a su representación escrita en forma de texto. Existen algunas alternativas en español pero las que he revisado tienen límites muy pequeños como convertir sólo números entre 0 y 100, o tienen límites arbitrarios como 999,999,9991 sin ninguna razón aparente. Además, ya sabemos que me gusta hacer las cosas yo mismo 😛 . El código de este post puede convertir cualquier entero positivo de tipo int en el rango de 0 a 2,147,483,647 y, en teoría, cualquier número entero menor a 1,000,000,000,000 que debería ser suficiente en la mayoría de los casos 😛 :

Read More

En corto No.4 – Tips de CMake

CMake incluye opciones predefinidas para habilitar/deshabilitar la información de depuración, de forma que no tienes que escribir estas reglas manualmente. Simplemente tienes que pasar al comando cmake la opción -DCMAKE_BUILD_TYPE con el valor de tu elección. Los posibles valores son Release, Debug, RelWithDebInfo, MinSizeRel. Por ejemplo:

monstruosoft@monstruosoft-PC:~$ cmake .. -DCMAKE_BUILD_TYPE=Debug

 

En corto No. 3 – Tips de NetBeans

Hay varias opciones si quieres agregar un identificador de versión automáticamente a tus programas de NetBeans pero la más sencilla de implementar es abrir el archivo build.xml en el directorio base de tu proyecto y editarlo de la siguiente siguiente manera:

...
  <tstamp>
    <format property="NOW" pattern="yyyy-MM-dd HH:mm:ss z" />
  </tstamp>
  <manifest file="manifest.mf">
    <attribute name="Implementation-Version" value="${NOW}"/>
  </manifest>

</project>

Esto pondrá un valor con la fecha de compilación en el archivo manifest.mf usado para crear el .jar de tu aplicación. Dentro de tu código puedes obtener ese valor con la siguiente instrucción:

String version = MiClase.class.getPackage().getImplementationVersion();
Fuentes:
http://www.javaxt.com/Tutorials/Netbeans/How_to_Add_Version_Information_to_a_Jar_File_with_Netbeans
https://stackoverflow.com/questions/5204297/put-version-to-my-java-application-netbeans

Usar los métodos del paquete java.util.logging puede ser útil para imprimir información de depuración que no quieres imprimir en la versión final de tu aplicación (definitivamente es una mejor opción que usar System.out.println()). Al igual que con el número de versión, hay varias formas de configurar el nivel de información de depuración que quieres imprimir pero una de las más sencillas es copiar el archivo de ejemplo logging.properties de ruta-de-instalación-del-JDK/jre/lib/logging.properties al directorio de tu proyecto y modificarlo para definir el nivel de información a imprimir por el logger que uses en tu código:

...
# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
...
# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
# OFF - Deshabilita todos los mensajes
# ALL - Imprime todos los mensajes
# Nombre de nivel - limita los mensajes a ese nivel y superiores
nombre.logger = OFF

Una vez creado/modificado este archivo, sólo tienes que pasarlo como argumento al iniciar tu aplicación:

monstruosoft@monstruosoft-PC:~/ java -Djava.util.logging.config.file=ruta-del-archivo-logging.properties MiClase
Fuente:
https://stackoverflow.com/questions/6307648/change-global-setting-for-logger-instances/6307666#6307666

En corto No. 2 – Tips de cmake

Si programas algo más complejo que un Hello World, es recomendable que tengas algún sistema para asistir en la compilación de tus programas; puede ser un makefile escrito a mano o, por ejemplo, un script de cmake.

En uno de mis proyectos recientes tengo algunos archivos de código fuente que son la base para un par de ejemplos. Mi script de cmake tenía las siguientes líneas:

ADD_EXECUTABLE (ejemplo-1 ${SOURCE_DIR}/ejemplo-1.c ${SOURCE_DIR}/base.c ${SOURCE_DIR}/extras.c)
ADD_EXECUTABLE (ejemplo-2 ${SOURCE_DIR}/ejemplo-2.c ${SOURCE_DIR}/base.c ${SOURCE_DIR}/extras.c)

Esto funciona pero noté que los archivos base.c y extras.c estaban siendo compilados dos veces cada que compilaba los programas, una vez para el ejecutable ejemplo-1 y otra vez para ejemplo-2. Normalmente esperaría que cada archivo sólo fuera compilado una vez pero al parecer eso no funciona así en cmake. En su lugar, para evitar compilar los archivos innecesariamente, se puede definir una librería temporal o de mentiras 😛 para que cada archivo sólo sea compilado una vez usando la instrucción ADD_LIBRARY con el modificador OBJECT. Por ejemplo:

ADD_LIBRARY(MI_LIBRERIA_DE_MENTIRAS OBJECT ${SOURCE_DIR}/base.c ${SOURCE_DIR}/extras.c)
ADD_EXECUTABLE (ejemplo-1 ${SOURCE_DIR}/ejemplo-1 $<TARGET_OBJECTS:MI_LIBRERIA_DE_MENTIRAS>)
ADD_EXECUTABLE (ejemplo-2 ${SOURCE_DIR}/ejemplo-2 $<TARGET_OBJECTS:MI_LIBRERIA_DE_MENTIRAS>)

 

 

En corto No. 1 – Imprimir el valor de un uint64_t con printf

En corto es una nueva serie de posts rápidos para cosas que no merecen escribir un post completo y detallado pero que pueden ser útiles. Los temas serán variados, desde programación, Linux, software y cualquier cosa que tenga en mente 😛 .

Comenzamos con cómo imprimir el valor de una variable uint64_t en C usando printf(). En primer lugar es necesario agregar el archivo de cabecera inttypes.h, además de stdint.h que se debe agregar para usar uint64_t:

#include <stdint.h>
#include <inttypes.h>

Ahora podemos imprimir el valor de una variable uint64_t con printf() de la siguiente manera:

uint64_t variable;
printf("%" PRIu64 "\n", variable);
printf("%" PRIx64 "\n", variable); // para imprimir en hexadecimal

Referencia completa aquí.

Tarea de programación: cola dinámica

Un visitante del sitio me pidió ayuda con su tarea de programación sobre una cola dinámica en lenguaje C. El programa debía tener la opción de desplegar el contenido de la cola además de permitir agregar y quitar elementos de la cola.

Este es el código con las correcciones necesarias para que funcione y corra en Linux; también debería correr en Windows para usuarios que no estén usando el Turbo C de los años  90s, aunque debería ser sencillo hacerlo funcionar con unas cuantas modificaciones mínimas.

Espero que este post sea de ayuda para los estudiantes que siempre buscan ayuda para este tipo de tareas de programación. Seguramente seguiré haciendo posts de este tipo de acuerdo a las solicitudes de tareas mas recurrentes :P.

// Tarea de programación - Cola dinámica
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

typedef struct nodo {
    int valor;
    struct nodo *siguiente;
} nodo;

nodo *cola = NULL, *ultimo = NULL;

/*
 * Esta función agrega un nuevo elemento a la cola, recibe como
 * parámetro el valor que se agregará a la cola; los nuevos elementos
 * siempre se agregan al final.
 */
int agregar(int valor) {
    nodo *n = (nodo *)malloc(sizeof(nodo));
    n->valor = valor;
    n->siguiente = NULL;

    printf("Agregando el elemento %d a la cola...\n", valor);
    if (cola == NULL && ultimo == NULL) {
        cola = n;
        ultimo = n;
    }
    else {
        ultimo->siguiente = n;
        ultimo = n;
    }

    return 0;
}

/*
 * Esta función quita un elemento de la cola, no recibe ningún
 * parámetro porque en una cola siempre se quita el elemento que
 * está al inicio.
 */
int quitar() {
    if (cola == NULL && ultimo == NULL) {
        printf("La cola está vacía, no se puede quitar ningún elemento.\n");
        return 0;
    }
    else if (cola == ultimo) {
        int valor = cola->valor;
        free(cola);
        cola = NULL;
        ultimo = NULL;
        printf("Se ha quitado el elemento %d de la cola.\n", valor);
        printf("La cola ha quedado vacía.\n");
    }
    else {
        nodo *n = cola;
        cola = n->siguiente;
        printf("Se ha quitado el elemento %d de la cola.\n", n->valor);
        free(n);
    }
}

/*
 * Muestra la cola.
 */
void mostrar() {
    nodo *n = cola;
    if (n == NULL) {
        printf("La cola está vacía.\n");
        return;
    }

    printf("Mostrando la cola...\n");
    while (n != NULL) {
        printf("\t%d\n", n->valor);
        n = n->siguiente;
    }
}

int main() {
    printf("Este programa muestra el funcionamiento de una cola dinámica.\n");
    printf("- Presiona A para insertar un nuevo elemento a la cola.\n");
    printf("- Presiona Z para quitar un elemento de la cola.\n");
    printf("- Presiona P para mostrar la cola.\n");
    printf("- Presiona Q para salir.\n");

    char key;
    while ((key = toupper(getchar())) != 'Q') {
        switch (key) {
        case 'A':
            agregar(rand());
            break;
        case 'Z':
            quitar();
            break;
        case 'P':
            mostrar();
            break;
        }
    }
    return 0;
}