El Reto del Burger – Edición 2021

El Reto del Burger es un reto de programación de monstrochan.org. Este año el reto consistía en programar un clón del clásico juego Frogger. En mi caso, decidí hacer una versión lo más apegada posible a la versión de Atari 2600 del juego y estoy bastante satisfecho con el resultado considerando que fue escrito en un lapso de 48 horas. El clón está programado usando Allegro 5 y de momento no puedo compartir el código pero espero poder publicarlo próximamente.

Puzzle Collection para Android

Puzzle Collection es un juego que he desarrollado usando Allegro 5 y lo he publicado en la tienda de Android. El desarrollo de este juego comenzó como una broma, después decidí retomarlo para el Reto de la Toja Azul en su edición 2020 – 2021 y fue así como seguí trabajando en el juego hasta darle una presentación aceptable. Programar el juego en Allegro, llevar a cabo todo el proceso de registro y seguir los lineamientos de publicación en la tienda de Android es una tarea difícil cuando trabajas en solitario. En particular, es muy probable que trabajar en Unity o Godot sea una manera mucho más fácil y eficiente de programar un juego pero quise hacerlo con Allegro como un reto personal.

Puzzle Collection se encuentra en fase beta abierta y apreciaría cualquier tipo de comentarios o sugerencias sobre el juego.

Compilar PhysicFS con MinGW

PhysicsFS es una librería que permite acceder al contenido de archivos .zip transparentemente como si fueran parte de nuestro sistema de archivos. Allegro 5 cuenta con un addon para usar PhysicsFS en tus programas de Allegro. En posts anteriores describí la forma de compilar la versión de Allegro para Windows usando MinGW, en esa ocasión no compilamos PhysicsFS para Windows para poder usarla con Allegro pero ahora tengo un proyecto de Allegro que requiere usar este addon y por lo tanto debemos compilar la librería usando MinGW.

Compilar PhysicsFS para Windows no debería ser difícil ya que usa CMake, por lo que el archivo de toolchain que usamos en posts anteriores debería sernos útil:

monstruosoft@debian:~/build/physfs-3.0.2$ mkdir build-mingw32
monstruosoft@debian:~/build/physfs-3.0.2$ cd build-mingw32/
monstruosoft@debian:~/build/physfs-3.0.2/build-mingw32$ export PKG_CONFIG_LIBDIR=/home/monstruosoft/mingw32-i686/lib/pkgconfig/
monstruosoft@debian:~/build/physfs-3.0.2/build-mingw32$ cmake -DCMAKE_TOOLCHAIN_FILE=~/toolchain-mingw32.cmake -DCMAKE_INSTALL_PREFIX=/home/monstruosoft/mingw32-i686/ ..
-- The C compiler identification is GNU 6.3.0
-- The CXX compiler identification is GNU 6.3.0
-- Check for working C compiler: /usr/bin/i686-w64-mingw32-gcc
-- Check for working C compiler: /usr/bin/i686-w64-mingw32-gcc -- works
-- PhysicsFS will build with the following options:
--   ZIP support: enabled
--   7zip support: enabled
--   GRP support: enabled
--   WAD support: enabled
--   HOG support: enabled
--   MVL support: enabled
--   QPAK support: enabled
--   SLB support: enabled
--   VDF support: enabled
--   ISO9660 support: enabled
--   Build static library: enabled
--   Build shared library: enabled
--   Build stdio test program: enabled
--     Use readline in test program: disabled
-- Configuring done
-- Generating done
-- Build files have been written to: /home/monstruosoft/build/physfs-3.0.2/build-mingw32
monstruosoft@debian:~/build/physfs-3.0.2/build-mingw32$ make -j 4
Scanning dependencies of target physfs-static
Scanning dependencies of target physfs
...
[ 95%] Built target physfs
[100%] Built target test_physfs
monstruosoft@debian:~/build/physfs-3.0.2/build-mingw32$ make install
[ 47%] Built target physfs-static
[ 95%] Built target physfs
[100%] Built target test_physfs
Install the project...

Ahora simplemente debemos recompilar Allegro con el MinGW para generar el addon de PhysicsFS:

monstruosoft@debian:~/build/physfs-3.0.2/build-mingw32$ cd ..
monstruosoft@debian:~/build/physfs-3.0.2$ cd ..
monstruosoft@debian:~/build$ cd allegro-5.2.5.0/
monstruosoft@debian:~/build/allegro-5.2.5.0$ cd build-mingw32/
monstruosoft@debian:~/build/allegro-5.2.5.0/build-mingw32$ cmake -DCMAKE_TOOLCHAIN_FILE=~/toolchain-mingw32.cmake -DCMAKE_INSTALL_PREFIX=/home/monstruosoft/mingw32-i686/ ..
...
-- Found PhysFS: /home/monstruosoft/mingw32-i686/bin/libphysfs.dll  
-- Found PHYSFS: /home/monstruosoft/mingw32-i686/bin/libphysfs.dll  
-- Could NOT find ZLIB (missing:  ZLIB_LIBRARY) (found version "1.2.8")
-- Performing Test PHYSFS_IMPLICIT_ZLIB
-- Performing Test PHYSFS_IMPLICIT_ZLIB - Success
...
monstruosoft@debian:~/build/allegro-5.2.5.0/build-mingw32$ make -j 4
monstruosoft@debian:~/build/allegro-5.2.5.0/build-mingw32$ make install

Ahora podemos usar PhysicsFS en nuestros programas de Allegro 5.

Compilar programas para Windows desde Linux – Parte 1

¿Eres un usuario de Linux y disfrutas haciendo tus programitas en C o C++ pero quieres compartirlos con el resto del mundo que seguramente usa Windows?. Compartirles el código es una posibilidad pero el usuario promedio no sabría qué hacer con él así que tus opciones se reducen a entregarles un ejecutable listo para usar en Windows. Por suerte, compilar tus programas para Windows es relativamente fácil en Linux, sobre todo si alguna vez usaste MinGW en Windows. MinGW es, por así decirlo, el gcc para Windows. MinGW ofrece compiladores para C y C++ (entre otros) que generan archivos ejecutables .exe para Windows así que, como te imaginarás, solamente tenemos que instalar MinGW para poder usarlo para generar ejecutables para Windows desde nuestra distro de Linux. Para instalar MinGW en Debian Stretch, basta con instalar el paquete mingw-w64 desde el gestor de paquetes.

Read More

Tarea de programación C – Números primos

En un post anterior escribí el código para encontrar números primos en Java. A diferencia del código en aquel post, aquí he usado una representación digital de la criba o coladera de Eratóstenes. Usar una representación en memoria tiene la ventaja de que nos permite ahorrarnos muchos cálculos innecesarios que el anterior código hacía para comprobar si un número era primo. Además, gracias a que cualquier compilador moderno nos permite declarar arreglos o reservar bloques de memoria de tamaño arbitrario, es posible usar este código para encontrar fácilmente los números primos por debajo de 1,000,000,000 (o cualquier número en el rango del tipo de datos int).

#include 
#include 
#include 
#include 

#define LIMITE 100000000

uint8_t *criba;
int contador = 0;

int main() {
    criba = calloc(LIMITE, 1);
    assert(criba);
    for (int i = 2; i < LIMITE; i++) {
        if (criba[i] == 0) {
            contador++;
            printf("%d\n", i);
            uint64_t mul = i * 2;
            while (mul < LIMITE) {
                criba[mul] = 1;
                mul += i;
            }
        }
    }
    printf("Se encontraron %d números primos menores a %d.\n", contador, LIMITE);
}

El código anterior define LIMITE con un valor de 100,000,000 y en mi PC toma aproximadamente 4 segundos al programa para encontrar los números primos por debajo de ese valor. Si incremento el valor de LIMITE a 1,000,000,000 toma aproximadamente 40 segundos en mi PC, que sigue siendo más rápido que el tiempo que tomaba la versión anterior del código para un valor de 100,000,000. Nota que puede llegar a millones la cantidad de números primos para estos valores de LIMITE, por lo que recomiendo enviar la salida del programa a un archivo y no directamente a la terminal. Es decir, ejecuta el programa de la siguiente manera:

monstruosoft@debian:~/code$ ./primos > /dev/shm/primos.txt

En el archivo primos.txt se guardará la salida del programa, por ejemplo:

2
3
5
7
11
13
17
19
23
29
31
37
...
999999667
999999677
999999733
999999739
999999751
999999757
999999761
999999797
999999883
999999893
999999929
999999937
Se encontraron 50847534 números primos menores a 1000000000.

Nota también que aunque este código puede fácilmente reservar un bloque de memoria de 1 GB en C, el mismo código en Java siempre me daba errores de OutOfMemoryException. Incluso después de aumentar el tamaño de la memoria usada por la máquina virtual de Java, usar System.out.println() era extremadamente lento para intentar escribir varios millones de líneas de texto a un archivo, y un ArrayList con varios millones de números primos también causaba problemas al intentar usar el método toString() así que si conoces la forma de usar este código en Java deja un comentario.

 

Tarea de programación Java – Números primos

Una clásica tarea de programación es encontrar todos los números primos menores por debajo de una cantidad introducida por el usuario. El algoritmo más básico consiste en usar una versión digital de la criba de Eratóstenes que es bastante simple de escribir pero puede volverse muy lenta conforme se incrementa el la cantidad que introduce el usuario. El código en este post es básicamente el algoritmo la criba de Eratóstenes con unas cuantas optimizaciones. En mi PC es posible encontrar todos los números primos menores a 10,000,000 relativamente rápido usando este código. Para los números primos menores a 100,000,000 el código se tarda casi un minuto en mi PC. Para números mayores el tiempo se multiplicaría considerablemente.

El código para encontrar los números primos es el siguiente (lo siento, no pude lograr que wordpress formateara el código correctamente):

public static ArrayList<Integer> numerosPrimos(int max) {
    ArrayList<Integer> primos = new ArrayList<Integer>();
    primos.add(2);

    for (int i = 3; i < max; i++) {
        boolean es_primo = true;
        double limite = Math.ceil(Math.sqrt(i));
        for (int j = 0; j < primos.size(); j++) {
             if (i % primos.get(j) == 0) {
                 es_primo = false;
                 break;
             }
             if (primos.get(j) > limite) break;
        }
        if (es_primo) primos.add(i);
    }

    return primos;
}

 

He agregado este método a la clase TareasProgramacion en github. Se puede usar este método como en el siguiente ejemplo:

import java.util.*;
import org.monstruosoft.utils.*;

public class Prueba {
    public static void main(String args[]) {
        Scanner s = new Scanner(System.in);
        System.err.print("Escribe el número máximo para la búsqueda de números primos: ");
        int max = s.nextInt();
        ArrayList primos = TareasProgramacion.numerosPrimos(max);
        for (Integer i: primos)
            System.out.println(i);
        System.out.println("Se encontraron " + primos.size() + " números primos menores a " + max + ".");
    }
}

Al ejecutar el programa obtenemos la siguiente salida:

monstruosoft@debian:~/code/monstruosoft/java-utils$ java Prueba 
Escribe el número máximo para la búsqueda de números primos: 100
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
59
61
67
71
73
79
83
89
97
Se encontraron 25 números primos menores a 100.

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.