El blog de Juan Palómez

29 marzo 2015

Recopilación de frikadas

Filed under: Uncategorized — Etiquetas: , , , , — thisisoneball @ 13:49

 
 

AAlib: reproducir vídeo o jugar a videojuegos en ASCII-art:

Abre las imágenes en una pestaña nueva para verlas a tamaño original:

 

 
 

 
 

 
 

Windows 95 en un smartwatch:

 
 

 
 

Jugar al Quake en un osciloscopio:

 
 

 
 

psDooM: matar procesos del sistema jugando al DooM:

 
 

 
 
Tempest: reproducir música en una radio utilizando las frecuencias que emite un monitor de tubo:

 
 

 
 

Algoritmo que dibuja bitmaps jugando al Tetris:

 
 

 
 

Reproducir música con unidades de floppy disk:

 
 

 
 

Tatuaje de pantalla azul de Windows:

 
 

Anuncios

18 octubre 2009

La ranita feliz

Filed under: Uncategorized — Etiquetas: , , , , — thisisoneball @ 15:00

Este es un programa que resuelve una de las pruebas del juego del MAME Puzzle & Action: Tant-R, en concreto la de la rana que tiene que ir pisando todas las casillas del tablero sin dejar ninguna sin pisar. Estas son algunas de las pantallas que te pueden salir:

tant0000 tantr tant0001

El programa muestra un tablero como el del juego pero vacío. Si pinchas en una casilla sitúas la rana, y las siguientes que pinches serán las piedras. Cuando hayas terminado de poner piedras pulsas Solución. Si hay más de una solución te seguirán saliendo cada vez que pulses el botón hasta que ya no queden más:

captura

Pincha aqui para bajar el ejecutable y el código

Puedes usar los casos que salen en el juego, como en las tres capturas de arriba, o probar otros.

Si vas a probar los tuyos propios muchas veces pasará que no haya ninguna solución, o que haya tantas que se te quede el programa prácticamente colgado pensando. Poniendo 5 o 6 piedras normalmente saca las soluciones rápido.

Realmente son dos programas separados:

  • Un programa en Lisp que es el que calcula las soluciones (rana.lsp). Recibe por la entrada estándar las posiciones de las piedras y de la rana: cada par de números son la coordenada X y la Y de una piedra, empezando de 0,0 que es la esquina superior izquierda. Luego una linea que ponga FIN, y luego las coordenadas de la rana.

    Al ejecutarlo imprime las soluciones con un dibujo en ascii, donde < > ^ v significa izquierda, derecha, arriba, abajo.

    Se puede ver todo esto en los ficheros entrada.txt, salida.txt y ejecutar.bat

  • Un frontend para el programa anterior, hecho en C + API de Windows
    Puedes usar ranita.exe para ejecutarlo o puedes abrir los ficheros de Visual Studio para editarlo. en ambos casos hace falta el clisp, que he incluido en una carpeta en el ZIP.

    Genera el fichero entrada.txt a partir de lo que dibuje el usuario, llama al programa Lisp para que calcule las soluciones, lee el fichero salida.txt y lo dibuja en la ventana.

  • 26 enero 2009

    Dependencias entre ficheros fuente

    Filed under: Uncategorized — Etiquetas: , , , — thisisoneball @ 17:25

    Otra de las múltiples carencias de MySQL es que no tiene un sistema de dependencias entre objetos. Por ejemplo, un procedimiento almacenado que isnerta en una tabla, o lee de una vista, o una vista que lee de otras tablas o vistas, etc…

    Te permite borrar o cambiar de nombre un objeto aun cuando está siendo referenciado desde otro sitio. Esto hay quien lo ve como una ventaja (es más cómodo siempre que sepas lo que estás haciendo). Pero para un proyecto medianamente grande y en el que trabajen varias personas se hace inmantenible.

    Siempre queda la opción de hacer un programa que lea los ficheros fuente, y sepa encontrar ahí dónde se referencian otros objetos o ficheros fuente. He estado buscando en Internet algo hecho y solo he visto alguna cosa para ficheros .c y .h. Asi que he preparado este script como solución rápida. De momento solo busca en ficheros fuente de Perl y de procedimientos almacenados de MySQL.

    Va dibujando un arbol de las llamadas entre unos y otros. Por ejemplo:

    perl1.pl
    	perl2.pl
    	mysql1.sql
    	mysql2.sql
    		mysql4.sql
    	mysql5.sql

    en este caso el fichero perl1.pl llama a un programa Perl (perl2) y a 3 procedimientos almacenados (mysql1, mysql2 y mysql5), y mysql2 a su vez llama a mysql4

    Los parámetros del programa son el fichero fuente a partir del cual se empieza a buscar, y la lista de directorios en los que buscar, y se ejecuta desde el directorio que contiene a éstos. Ej:

    --raiz/
        |--perl/
            |-- perl1.pl
    	|-- perl2.pl
        |--mysql/
            |-- mysql1.pl

    En este caso se llamaría desde ‘raiz’, por ejemplo con:

    dependencias.pl perl1.pl {perl,mysql}

    Requiere el módulo File::Grep, que sirve para lo mismo que el comando grep. Es bastante incómodo de utilizar y probablemente habría sido mejor utilizar simplemente grep en un script de shell o de Perl.

    El programa es fácilmente adaptable a otros lenguajes. Solo hay que especificar cómo se hacen las llamadas a otros scripts (do, call, system, …)

    Se agradecen ampliaciones y correcciones.

    #!/usr/bin/perl
    
    use File::Grep qw( fgrep );
    
    die "Uso: dependencias.pl <archivo fuente inicial> {directorio1,directorio2}...\n\n" unless @ARGV == 2;
    
    my $directorios = $ARGV[1];
    
    busca($ARGV[0], "");
    
    sub busca {
    
    	my $ocurrencia;
    	my %matches;
    	my $match_key;
    	foreach $ocurrencia (fgrep { /(call|do).*\(/i } glob "$directorios/$_[0]") {
    		%matches = %{${$ocurrencia}{'matches'}};			# matches es un hash que contiene las lineas encontradas
    		foreach (sort keys %matches) {
    			if ($matches{$_} =~ m/call\s+(.+)\(/i) {		# llamadas a procedimientos almacenados MySQL
    				print "$_[1]$1.sql\n";
    				busca("$1.sql", $_[1]."    ");
    			} elsif ($matches{$_} =~ m/do.*\'(.+)\'/i) {		# llamadas a scripts Perl
    				print "$_[1]$1\n";
    				busca("$1", $_[1]."    ");
    			}
    		}
    	}
    
    }

    30 noviembre 2008

    Extraer y reemplazar texto con expresiones regulares en MySQL

    Filed under: Uncategorized — Etiquetas: , , , — thisisoneball @ 17:46

    Extracting and replacing text with MySQL regular expressions

    Entre las múltiples carencias de MySQL se encuentran (en parte) las expresiones regulares. Tiene el operador REGEXP que permite saber si una cadena casa con una expresión regular concreta, pero no permite extraer fragmentos de esa cadena ni reemplazar la cadena encontrada con otro texto.

    Esta es una función para MySQL que hace precisamente eso valiéndose del propio operador REGEXP.

    Es una chapuza rápida, probablemente no sea conveniente utilizarlo en determinados casos ya que debería ser bastante lenta (hace una llamada a REGEXP por cada subcadena contenida dentro de la cadena a buscar , por ejemplo, para buscar en ‘abc’, comprobaría la expresión regular con:

    a

    ab

    abc

    b

    bc

    c

    Si llamas a esta función dentro del select o el where de una consulta con muchos registros puede ser realmente ineficiente. He añadido DETERMINISTIC ya que la función es determinista, y en algunos casos de esta manera se puede ahorrar bastante tiempo de proceso.

    Hay otras opciones, como instalar UDFs de expresiones regulares. Las UDFs son código compilado y son mucho más rápidas. La ventaja de esta función es que es más fácil de instalar y es más portable.

    Uso:

    Devuelve la cadena más grande dentro de ‘cadena’ que coincida con ‘re’, por ejemplo

    SELECT substring_regexp(‘jket5657twr’, ‘[0-9]+’, NULL) –> 5657

    En este caso hay varias cadenas que coinciden con [0-9]+, por ejemplo 565, 5 , 657, … He decidido hacerlo de forma que devuelva la mayor de ellas.

    Para sustituir, se pasa como parámetro la cadena de sustitución, en vez de NULL:

    SELECT substring_regexp(‘jket5657twr’, ‘[0-9]+’, ‘HOLA’) –> jketHOLAtwr

    Código:

    DELIMITER $$
    
    DROP FUNCTION IF EXISTS `substring_regexp` $$
    CREATE FUNCTION `substring_regexp`(cadena text, re text, sustitucion text) RETURNS text CHARSET latin1 DETERMINISTIC
    BEGIN
    
    declare a, b, tam int;
    declare subcadena, retorno text;
    
    set tam = 0;
    set retorno = '';
    set a = 1;
    inicio: WHILE a <= length(cadena) DO
    	set b = a;
    	final: WHILE b <= length(cadena) DO
    		set subcadena = substring(cadena, a, b-a+1);
    		if subcadena regexp concat('^', re, '$') and length(subcadena) > tam then
    			set tam = length(subcadena);
    			if sustitucion is null then
    				set retorno = subcadena;
    			else
    				set retorno = replace(cadena, subcadena, sustitucion);
    			end if;
    		end if;
    		set b = b+1;
    	END WHILE final;
    	set a = a+1;
    END WHILE inicio;
    
    return retorno;
    
    END $$
    
    DELIMITER ;

    22 octubre 2008

    Timoconcurso “cerillas”

    Filed under: Uncategorized — Etiquetas: , , — thisisoneball @ 23:32

    Este es un programa en C que resuelve uno de los problemas de los “timoconcursos” de los canales de la TDT. En concreto el que aparece un número de 4 cifras hecho con cerillas y hay que conseguir el número más alto posible moviendo dos cerillas.

    Realmente este programa es algo más genérico: se puede especificar el número de cerillas que habría que mover, se pueden hacer con números de cualquier número de cifras, y devuelve todas las soluciones, no sólo el más alto.

    Esto es una curiosidad, no es para llamar al programa y tener la solución, porque el timo por lo visto está en que te tienen media hora al teléfono esperando para dar la solución, y eso en el caso de que cojan la llamada.

    NO LLAMES AL TIMOCONCURSO.

    #define	TRUE	1
    #define	FALSE	0
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <math.h>
    
    /*	tipo de datos "segmentos":
    		vector de 7 shorts, almacena un dígito en forma de segmentos, de la siguiente forma:
    			|-0-|
    			1   2
    			|-3-|
    			4   5
    			|-6-|
    		por ejemplo el 0 se almacenaría como:
    			segmentos numero0 = {1,1,1,0,1,1,1};
    
    	conjuntos de segmentos:
    		permiten almacenar un número completo compuesto de varios dígitos, por ejemplo para almacenar números de 4 dígitos:
    			segmentos numero[4];
    		para hacer lo mismo en tiempo de ejecución:
    			segmentos *numero;
    			numero = (segmentos *) calloc(4, sizeof(segmentos));
    */
    typedef short segmentos[7];
    
    //	especificación de los dígitos del 0 al 9 en forma de segmentos (vector de 10x7 shorts)
    segmentos digitos[10] = {
    	{1,1,1,0,1,1,1},
    	{0,0,1,0,0,1,0},
    	{1,0,1,1,1,0,1},
    	{1,0,1,1,0,1,1},
    	{0,1,1,1,0,1,0},
    	{1,1,0,1,0,1,1},
    	{1,1,0,1,1,1,1},
    	{1,0,1,0,0,1,0},
    	{1,1,1,1,1,1,1},
    	{1,1,1,1,0,1,1}
    };
    
    //	copia los 7 segmentos de un dígito
    void CopiarSegmentos(segmentos destino, segmentos origen)
    {
    	memcpy(destino, origen, 7 * sizeof(destino[0]));
    }
    
    //	transforma un entero "origen" a un vector de segmentos "destino" (no un solo dígito sino un número completo compuesto de "tam" dígitos)
    void IntSegmento(segmentos *destino, int origen, int tam)
    {
    	while (tam >= 1) {
    		tam--;
    		CopiarSegmentos(destino[tam], digitos[origen % 10]);
    		origen = origen / 10;
    	}
    }
    
    /*	compara dos números de "tam" dígitos
    	devuelve TRUE si entre los dos se han movido "ndiferencias" segmentos
    */
    int CompararSegmentos(segmentos *origen, segmentos *destino, int tam, int ndiferencias)
    {
    	int i;
    	int diferencias = 0, total = 0;
    
    	for (i=0; i < tam*7; i++) {
    		total += (*origen)[i] - (*destino)[i];
    		if ((*origen)[i] != (*destino)[i])
    			diferencias++;
    	}
    
    	// total es la diferencia en el total de segmentos, y debe ser 0 (mismo nº de cerillas en los dos casos)
    	// diferencias es el total de segmentos cambiados (es el doble del nº de cerillas movidas)
    	if (diferencias == ndiferencias*2 && total == 0)
    		return TRUE;
    	else
    		return FALSE;
    }
    
    /*	parámetros del programa: el número inicial y el número de cerillas que hay que mover
    
    	retorno:
    		0	sin errores
    		1	error en el número de parámetros pasados
    		2	error en el formato de los parámetros
    */
    int main(int argc, char *argv[])
    {
    	int inicial, ndiferencias, tam; segmentos *s_inicial;
    	int candidato; segmentos *s_candidato;
    
    	// gestión de parámetros de entrada
    	if (argc != 3) {
    		fprintf(stderr, "Uso: %s\nEjemplo: %s 5912 2\n", argv[0], argv[0]);
    		return 1;
    	}
    
    	inicial = atoi(argv[1]);		// ej: 5912
    	ndiferencias = atoi(argv[2]);		// ej: 2
    	if (inicial == 0 || ndiferencias == 0) {
    		fprintf(stderr, "Los parámetros tienen que ser numéricos y distintos de 0\nEjemplo: %s 5912 2\n", argv[0]);
    		return 2;
    	}
    
    	tam = strlen(argv[1]);			// ej: 4
    	candidato = (int) pow(10,tam) - 1;	// ej: 9999
    
    	s_inicial = (segmentos *) calloc(tam, sizeof(segmentos));
    	s_candidato = (segmentos *) calloc(tam, sizeof(segmentos));
    
    	// pasar el nº inicial a segmentos y comparar con cada nº candidato decrecientemente
    	IntSegmento(s_inicial, inicial, tam);
    	while (candidato >= 0) {
    		IntSegmento(s_candidato, candidato, tam);
    		if (CompararSegmentos(s_inicial, s_candidato, tam, ndiferencias))
    			printf("%d\n", candidato);
    		candidato--;
    	}
    
    	return 0;
    }

    Blog de WordPress.com.