Ajedrez Toledo

Hace unos días, movido por la curiosidad, decidí buscar información sobre el desarrollo de juegos de ajedrez. Pues bien, el alma se te cae literalmente a los pies cuando te encuentras con una joya de la programación como esta que reproduzco al final de este artículo.

En solo 64 líneas de código (3473 caracteres), dispuestas caprichosamente en forma de composición ASCII Art, el genio mexicano de la programación Óscar Toledo Gutiérrez dejó escrito para la historia, en lenguaje C, lo que solo se puede catalogar como una obra de arte. Cualquiera puede imprimir el código, enmarcarlo y ponerlo sobre el cabecero de la cama, de hecho deberían estar haciéndolo ahora mismo, yo ya lo tengo, y es que esta joya, amigos, cabe impresa en la cara de una sola hoja de papel.

Se trata de un juego de ajedrez completamente funcional capaz de ganar a cualquier jugador aficionado que quiera aceptar el reto. Según palabras de su propio creador, se estima que puede desarrollar una fuerza de juego que puede variar entre los 1400 y los 1600 puntos Elo según el nivel de dificultad.

Todo acabaría ahí, si no fuese porque, además, el maldito programa del demonio fue diseñado para funcionar a través de una interfaz gráfica (corre tanto sobre sistemas X Window como sobre Microsoft Windows), y que requiere únicamente de ratón para poderse jugar.

Toledo Chess

El secreto de las cifras tan minúsculas de código resultante radica en que está escrito en C ofuscado de manera que el código fuente resultante utilice el menor número de caracteres posible. Como el aficionado a la programación en C podrá comprobar tras la lectura del código, no hay manera humana de entender qué demonios hace a primera vista.

El programa fue digno ganador del 19th International Obfuscated C Code Contest (IOCCC) celebrado el año 2006, donde obtuvo el galardón de Most Portable Chess Set.

Sin más, aquí está el código fuente:

#include <X11/Xlib.h>
char *l="dbcefcbddabcddcba~WAB+  +BAW~              +-84HLSU?A6J57IKJT576,",
*F="          ,>>,>  xl~w/?  ,>>>,s  m_>>__  mm2>>>  >uk>>> ",*f;y,u;
#define v for (i=b=0;b>511?b=0,i+=64:0,i<512;b+=64) A=i/64*10+b/64+21,XCopy
Plane(d, r[I[A]+7+14*(i+b>>6&1)+28*(A==z)],w,C,0,0,64,64,b,i,1); XFlush(d);
#define _(a) *f++=a&*F?-(z%14<7):"U252UDDDD"[z/14*2|u&1],
#define G(p) p##Pixel(d,DefaultScreen(d))
#define R(a) |(a==O|p==a)*
#define P return y=~y,
#define a X(0,0,0,
#define H while(
#define D ;if(

I[304],b,i,z;main(x,W)                   char**W; { Display *d =
 XOpenDisplay(""); Window               w = XCreateSimpleWindow
  (d, DefaultRootWindow(d),            64,64,512,512,2,G(Black)
   ,G(Black)); XGCValues g;           XButtonEvent e; int A,r
    [56],Z,* m = I , C ,Y;           XSelectInput(d,w,32772);
     XMapWindow( d, w); g.          foreground = G(White);
      C=XCreateGC(d,w,4,&g);       F+=48; H f=I,i=0,z<56){
       H u=0,i++<8){ H _(0)_(     64)_(16)_(8)_(4)_(2)_(1
       )_(0)++u<8); F++; } F-=   z%7-6?z%14<6?16:0:8; r[
        z++]=XCreateBitmapFromData(d,w,I,64,64); } srand(
       time(z=u=0)); H I[z]=-(   z>98|z<21|(z+1)%10<2),
      ++z<120); H ++m<9+I) 80     [m]=-2,90[m]=~(20[m]=7&*
     l++),30[m]=1; D 1<x) Z=       *W[1]-45; D 2<x){ a u,1
   ,Z); a u,0,1); z=0; } H          1){ XNextEvent(d,&e); D
  e.type==12){ v } D e.              type==4){ b=e.y/64*10+e
.x/64+21; D(b[I]^y)<-1)               { z=b; v} else{ i=(b<29
|b>90)&((z[I]^y)==-2)?-                6^y:z[I]; Y=y; a u,0,1);
z=0; v D 1<x&&Y-y){ a u                ,1,Z); a u,0,1); } z=0;
        v } } } }


                             X(w,c,h,e,S,s)


{ int p,O=          *l,t,d,o,C          ,*g,E,n,*m          =I,N=-1e8,
          A,L,r,x =           10,q; y=~y          ; H--O>20)
{ o=I[p=O]          D q=o^y,q>          0){ q+=(q<          2)*y,C=q
         ["51#/+++"],       A=q["95+3/33"];       do { m=0,
r=I[p+=C[l          ]-64] D !w          |p==w&&q>1          |C+2<A|!r)
          { d=abs(O-          p) D g=q<2          &e>>6==p+(
y?x:-x)?I+          (e>>6):0,           !r&(q>1|d%          x<1||g)|(r
          ^y)<-1){ n          =o,t=q<2&(          89<p|30>p)
?n+=y|1,6^          y:o+(y|1)           D (r^y)<-6          ) P 1e7-811
          *h; H n-t)          { O[I]=0,p          [I]=n,m?*g
=*m,*m=0:g          ?*g=0:0;            E=e&63 R(           91)16 R(28)
          4 R(21)2 R          (98)32; L=          (q>1?6-q?l
[p/x-1]-l[          O/x-1]-q+2          :(E|=y?8:1          ,!!m)*9:(E
          |=20-d?0:           64*p,n-o?(          l[15+n]-' '
)*9:d/8+!!          g*99))+(l[          r+15]-' ')          *9+l[p%x]-
          h-l[O%x];           L-=s>h||s==         h&L>49&1<s
?X(s>h?0:p          ,L,h+1,E,N          ,s):0 D !(          z-O|i-n|h|
          p-b|S|L<-           1e6))return         u=E; O[I]=
o,p[I]=r,m          ?*m=*g,*g=          0:g?*g=-2^          y:0 D S|h&&
          (L>N||!h&N          ==L&&rand(          )&4)){ N=L
D !h&&s) i          =n,z=O,b=p          D h&&c-L<S          ) P L; } q
          >5&d<2&C+6          <A&&(g=I+p          ,r=I[p+=p-
O],m=p<O?g          -3:g+2,!(e          &(p<O?3:5)          <<3*-y|*g|
          r|m[p<O?1:          -1])&&L>-           1e6&&1e6>a
63,1,0))?d          ++:(n+=y|1          );} } } C           +=q<2&C+3>
          A&((y?O<80          :39<O)||r)          ; } H!r&q>
2&q<6||(p=          O,++C<A))           ; } } P N+          1e8?N:0; }

Para compilar el código basta con copiar el texto y guardarlo en un archivo de texto con extensión .c o bien descargarlo desde el siguiente enlace:

Toledo Chess

El desarrollador recomienda utilizar utilizar el máximo nivel de optimización posible en su compilación. Esto ayudará en su ejecución ya que el programa intentará consumir todos los recursos que pueda para funcionar correctamente. En GCC bastaría con:

gcc -O3 -fexpensive-optimizations source.c -o chess -lX11

Existe una versión portada y compilada para Microsoft Windows que puede descargarse aquí:

Toledo Chess (Microsoft Windows)

Para ejecutar el programa deberemos indicar por parámetros el tipo de partida que queremos jugar. Para ello abrimos un terminal y ejecutamos cualquiera de las siguientes instrucciones:

prog        Modo de dos jugadores (útil si no hay un tablero a mano)
prog 1      Humano con blancas - Máquina con negras, nivel 1 (rápido)
prog 2      Humano con blancas - Máquina con negras, nivel 2 (medio)
prog 3      Humano con blancas - Máquina con negras, nivel 3 (muy lento)
prog 1 b    Humano con negras - Máquina con blancas, nivel 1 (rápido)
prog 2 b    Humano con negras - Máquina con blancas, nivel 2 (medio)
prog 3 b    Humano con negras - Máquina con blancas, nivel 3 (muy lento)

Existe mucha más información acerca del funcionamiento del programa en la web de Óscar Toledo Gutiérrez. Allí, el curioso podrá encontrar también parches y actualizaciones que permiten añadir mas funcionalidades al programa.

Revisiones y evoluciones posteriores del programa analizado hoy aquí culminaron el año 2009 dando como resultado Nanochess, el programa de ajedrez más pequeño jamás escrito en C.

¡Bravo Oscar! Desde aquí mi reconocimiento.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *