La función strfry: Anagramas y un poco de diversión

Leyendo información sobre la glibc me ha llamado la atención la función strfry. Esta función cambia aleatoriamente el orden de los caracteres en un string formando un anagrama y es una función específica de GNU que no existe en otros sistemas. Al verla se me han ocurrido un par de curiosidades que hacer con ella.

Anagramas

La primera es hacer un pequeño programa en C que muestre un anagrama de la palabra o frase que le pasemos como parámetro. Gracias a esta función, que nos hace todo el trabajo, el programa es trivial.

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
	if(argc < 2)
	{
		fprintf(stderr, "Uso: anag palabra\n");
		return 1;
	}

	strfry(argv[1]);
	printf("%s\n", argv[1]);
	return 0;
}

Sólo tenemos que guardarlo en un fichero anag.c y compilarlo con este comando:

gcc -Wall anag.c -o anag

Ahora podemos probarlo unas cuantas veces pasándole una palabra como parámetro y veremos que cada vez nos devuelve un anagrama diferente. También podemos ponerle una frase o algo más largo, pero entonces tenemos que meterlo entre comillas.

[~/work/strfry]$ ./anag linux
unilx
[~/work/strfry]$ ./anag linux
luixn
[~/work/strfry]$ ./anag linux
ilnux

Si probamos varias veces nos daremos cuenta de que los anagramas se repiten bastante. La función no es que esté pensada para que tenga una buena distribución aleatoria, pero está bien como curiosidad.

Como soy así, no puedo resistir la tentación de hacer una burrada. Vamos a ver lo que pasa si le pasamos como parámetro su propio código fuente.

[~/work/strfry]$ ./anag "$(cat anag.c)"
ibu"nr
nUaf itcrrn
#ai    \N Eivv{r avf1sntan 2Ur
1sd>l c    ;npl*ri
Sngi,  _
 Rs#e    rUh
(.f[ga,f aigh})re
1r    forCr 
gd;(f    e(
ar"igs%)a)ayh_
tnngc}r
Gcr nt(e tun#a    pirs
0t gtr*n"e;r]oit

¡Mola! 🙂

Ahora voy a explicar brevemente como funciona. La verdad es que es trivial.

La linea 1 es importante. La función strfry está declarada en string.h, pero sólo si se define el símbolo _GNU_SOURCE, así que hay que definir este símbolo al principio del programa. Es importante que esté delante de cualquier include.

Las lineas 2 y 3 son los includes que necesito para las funciones que usa el programa. La librería stdio es para las funciones printf y fprintf y la librería string es donde está strfry.

La linea 5 declara la función main de forma que reciba los parámetros del programa y pueda devolver un código de error en caso de que detecte algún problema.

Las lineas 7 a 11 son una simple comprobación para asegurarme de que los parámetros son correctos. Simplemente compruebo que se le haya pasado al menos un parámetro al ejecutable. Si no es así, muestro un error por la salida de errores y termino el programa con un código de error.

La linea 13 es la que realmente hace el trabajo. Simplemente llama a la función strfry con el parámetro del ejecutable, que estará en argv[1].

En linea 14 mostramos el valor del parámetro argv[1] después de que strfry lo haya convertido en un anagrama.

Por último, la linea 15 termina el programa devolviendo un código 0 que indica que todo ha ido bien.

Barajando las cartas

Otra cosa que se me ha ocurrido hacer con esta función es usarla para barajar las cartas de un hipotético juego de naipes. Si lo pensamos bien, no es muy distinto de hacer un anagrama. Sólo hay que tener en cuenta que la función strfry trabaja sobre caracteres que ocupan 1 byte e identifica el final de la cadena por el carácter nulo, así que el truco es buscar alguna forma de codificar las cartas de la baraja de manera que cada una ocupe un byte y el cero no esté incluido en la codificación para que podamos usarlo como carácter de final.

Lo que he decidido hacer es representar las cartas como números de forma que los dos últimos bits de cada número represente el palo de la carta (0 = oros, 1 = copas, 2 = espadas y 3 = bastos) y los bits 2 a 5 representen el número de forma que el as vale 1 y la sota, caballo y rey, valen 8, 9 y 10 respectivamente.

Por ejemplo, supongamos que queremos representar el 3 de espadas. Si las espadas tienen el valor 2, que es 10 en binario, y el número de la carta es el 3, que es 11 en binario, el tres de espadas sería el valor 001110 binario, que es 14 en decimal. Con un esquema se ve mejor:

Codificar el 3 de espadasCon esta codificación, la carta que tiene el código más bajo es el as de oros, que sería 000100, o sea 4 en decimal, y la carta con el mayor código sería el rey de bastos, que se representaría como 101011, que es 43 en decimal. Como vemos, el cero no aparece por ningún sitio, lo que nos permite usarlo como carácter de fin. Veamos el programa:

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>

char *ps[] = { "oros", "copas", "espadas", "bastos" };

char *ns[] =
{
	"as", "2", "3", "4", "5", "6", "7",
	"sota", "caballo", "rey"
};

int main(int argc, char **argv)
{
	char brj[41];
	int i;

	for(i = 0; i < 40; i++)
		brj[i] = i + 4;
	brj[i] = 0;

	strfry(brj);

	for(i = 0; brj[i] != 0; i++)
	{
		int p = brj[i] & 0x03;
		int n = brj[i] >> 2;
		printf("%s de %s\n", ns[n - 1], ps[p]);
	}
	return 0;
}

Podemos llamar a este programa, por ejemplo, baraja.c. Ahora lo compilamos:

gcc -Wall baraja.c -o baraja

Y podemos ejecutarlo para que nos genere un mazo de cartas barajado:

[~/work/strfry]$ ./baraja
caballo de oros
as de bastos
3 de espadas
rey de copas
5 de oros
7 de oros
6 de espadas
3 de copas
as de espadas
3 de oros
sota de copas
2 de espadas
4 de bastos
7 de bastos
4 de espadas
caballo de bastos
4 de oros
caballo de espadas
2 de bastos
7 de espadas
sota de espadas
as de oros
sota de oros
caballo de copas
5 de bastos
5 de espadas
sota de bastos
rey de bastos
6 de bastos
6 de oros
4 de copas
2 de copas
2 de oros
as de copas
3 de bastos
rey de oros
6 de copas
7 de copas
rey de espadas
5 de copas

Como véis, en C también se pueden hacer cosas chulas con pocas lineas.

Aparte de los includes y las declaraciones, el programa está dividido en tres partes claramente diferenciadas. La primera crea la baraja con las cartas ordenadas, la segunda mezcla las cartas y la tercera muestra en la terminal las cartas en el orden en el que han quedado tras barajar. Voy a explicarlo con más detalle:

Las lineas 1 a 3 son como las del programa anterior y poco más hay que decir de ellas. Las funciones que uso son también las mismas que en el programa anterior.

La linea 5 declara un array llamado ps que contiene los nombres de los palos de la baraja. La idea de este array es poder convertir fácilmente número de palo en su nombre. Por ejemplo, si tenemos en la variable p el valor 1 representando el palo de las copas, ps[p] será «copas», lo que nos permite obtener el nombre para montrárselo al usuario.

Las lineas 7 a 11 declaran el array ns, que es como ps pero para los números. La razón de que haya utilizado un array también para eso es poder poner los nombres de las cartas que tienen nombre en vez de número, como el as, la sota, el caballo y el rey.

A partir de la linea 13 empieza la función main.

La linea 15 declara un array llamado brj que es el que va a contener las 40 cartas de la baraja codificadas en un byte cada una. Esta declarado con tamaño 41 para que pueda contener también el carácter nulo del final.

La linea 16 declara una variable de tipo entero que voy a usar como índice para recorrer el array de la baraja.

Las lineas 18, 19 y 20 forman la primera parte a la que me refería antes. Lo que hacen es recorrer la baraja introduciendo las cartas, que van a ser números consecutivos empezando por el 4. Al final, añaden el nulo que indica el final de la baraja.

La linea 22 constituye por sí sola la segunda parte del programa y es la que mezcla las cartas llamando a la función strfry.

A partir de la linea 24 empieza la tercera parte del programa. En esta linea empieza un bucle que recorre las cartas tal y como quedaron con objeto de mostrárselas al usuario.

La linea 26 obtiene el palo de la carta actual, aplicando una operación lógica AND entre el byte que representa la carta y la máscara 0011 para filtrar todos los bits excepto los dos últimos.

La linea 27 obtiene el número desplazando dos bits a la derecha el byte que representa la carta. De esta manera, descartamos los bits del palo y quedan los del número en la posición correcta.

La linea 28 muestra la carta al usuario, teniendo en cuenta que los números de las cartas empiezan en 1, pero el primer índice del array ns es 0. De ahí lo de n – 1.

Por último, la linea 30 devuelve un 0 como valor de retorno de la función main, indicando que todo ha ido bien.

EOF

2 comentarios en “La función strfry: Anagramas y un poco de diversión

    1. hexborg Autor

      Muchas gracias. Sí. A veces me entra la inspiración y me apetece hacer cosas de estas, que no son nada del otro mundo, pero son curiosas y están entretenidas.
      Por cierto, he visto que wordpress se había vuelto ha cargar los ficheros de los includes. Ya los he vuelto a poner. No sé por qué pasa esto de vez en cuando…
      Saludos y gracias por comentar.

Los comentarios están cerrados.