Friday, October 05, 2012

Comunicación entre aplicaciones usando Delphi


Llevo ya tiempo queriendo comunicar una aplicación mía en Delphi con un motor de ajedrez, un engine como Rybka o Shredder, por ejemplo, que son programas de ajedrez que se instalan en una interfaz gráfica común, como la de Fritz o Chessbase, y desde esa interfaz, mandan información de lo que está analizando el software de ajedrez.

La comunicación entre procesos (o programas) en una PC se puede hacer de varias maneras: una es a través de OLE (Object Linking & Embedded), que es el sistema en el que, por ejemplo, mi programa de corrección ortográfica, Lapsus, se comunica con MsWord. Otra opción es DDE (Data Dynamic Exchange), la cual -hasta donde la usé- resultó muy mal documentada y llena de problemas y errores. Me parece que ya Microsoft no la actualizó más y se enfocó a OLE, desechando para siempre DDE. Finalmente hay una tercera posibilidad, el comunicarse vía el stdinput y stdoutput, que son los canales naturales para recibir y mandar información. La salida estándar (stdoutput) es la pantalla y la entrada estándar (stdinput) es el teclado.

Sin embargo, en Delphi el poder manejar las entradas y salidas estándar no puede hacerse a través de la interfaz gráfica (GUI), por lo que hay que buscar una solución, y esto son los pipes (tuberías), en el mejor estilo Unix. Un 'pipeline' es un conjunto de procesos encadenados por sus salidas y entradas estándar, de manera que la salida de cada proceso (el resultado que arroja, pues), se alimenta como la entrada estándar del siguiente. Cada conexión se implementa como un pipe anónimo. El concepto fue inventado por Douglas McIlroy, para los shells de Unix, y se le nombró así por la analogía con una tubería física.

El problema a resolver tiene que ver con el utilizar este tercer mecanismo de entradas y salidas estándar. Esto hay que hacerlo así porque los motores de ajedrez son en general programas que funcionan en la consola (es decir, sin interfaz gráfica), y se comunican con las interfaces gráficas, como Arena o Fritz, para poder jugar al ajedrez. En las mencionadas interfaces el programador resuelve la parte gráfica: cómo mostrar el tablero, cómo hacer las jugadas, quizás usando el ratón o el teclado, etc. y entonces el creador de un programa de ajedrez solamente tiene que comunicarse con la interfaz. Esto se hace a través de un protocolo. Los más populares son: Winboard y UCI (Universal Chess Interface).

Actualmente los programadores de ajedrez han enfatizado su uso a través de UCI, protocolo inventado por los creadores de Shredder, Rudolf Huber y Stefan Meyer-Kahlen, en noviembre del 2000. Es gratuito para ser usado libremente y puede verse como el rival del protocolo de XBoard/Winboard, el cual ha definido una serie de comandos para comunicarse entre la interfaz gráfica y el motor de ajedrez. Recuérdese que el motor de ajedrez es un programa típico de consola, sin interfaz gráfica, que se comunica con la interfaz a través de los puertos stdinput y stdoutput. Así entonces, la interfaz gráfica (GUI) le manda un comando al programa de ajedrez a través de la implementación de un pipeline. El programa recibe el comando y toma acción.  Normalmente regresa una cadena a través del pipeline, la cual puede ser manipulada por la GUI. Es de esta manera que ambos programas se comunican.

Cuando empecé con este problema, revisé muchos sitios en donde mostraban -supuestamente- cómo lograr esta comunicación vía stdinput y stdoutput. Vi mucho código, buenos y malos ejemplos, pero de una u otra manera no podía resolver el problema de comunicación entre ambos procesos. Por ejemplo, en una prueba de un simple programa de consola que pide una cadena y regresa la misma cadena (pero en mayúsculas), el código del autor de esa página mostraba la funcionalidad, pero si se trataba de hacer un programa de consola que estuviese en un ciclo esperando una entrada para dar una salida (terminando, por ejemplo, con la palabra 'fin'), el código ejemplo no funcionaba.

En otra página web hallé programadores con exactamente la misma dificultad, e incluso, un cubano mostraba parte de la solución. La implementación -cuando quise hacerla- tomando su código, no me resultó. Finalmente se me ocurrió buscar en un sitio donde hay todo para Delphi: Torry.net. Hallé en http://torry.net/pages.php?id=224 el componente Threaded TPipe. Viene el código fuente y una demostración, también con código en Delphi. Funciona muy bien.

¿Cuál es la idea de todo esto? Poder comunicarse con un proceso de consola, en este caso un motor de ajedrez, me permite poder "platicar" con el engine de manera que pueda poner, por ejemplo, a jugar a engines entgre sí, o al mismo engine contra sí mismo. Otra opción es poder crear una interfaz gráfica que pueda servir para que se juegue con los engines que soporten el protocolo UCI, el cual es realmente bastante sencillo, es decir, tiene muy pocos comandos.

Seguiremos informando al respecto en los siguientes artículos del blog.

2 comments:

Javier Par said...

Aquí tienes un ejemplo de un codigo fuente en Delphi para jugar al ajedrez, no sé si te podrá dar alguna idea para conectar un motor como dices en el post, pero bueno por algo se empieza...
http://delphimagic.blogspot.com.es/2008/11/ajedrez-con-delphi-chess-with-delphi.html

Morsa said...

Muchas gracias, Javier, ya pude hacer la conexión entre un engine y mi programa. Hice una aplicación, que he puesto en modo experimental, en donde funciona correctamente el asunto. Puedes verlo aquí:

http://la-morsa.blogspot.mx/2012/10/juegue-la-ciega-contra-los-engines-de.html

saludos
Manuel