Current world's smallest chess program

Discussion of chess software programming and technical issues.

Moderators: bob, hgm, Harvey Williamson

Forum rules
This textbox is used to restore diagrams posted with the [d] tag before the upgrade.
User avatar
hgm
Posts: 23631
Joined: Fri Mar 10, 2006 9:06 am
Location: Amsterdam
Full name: H G Muller
Contact:

Re: Current world's smallest chess program

Post by hgm » Sun Feb 22, 2009 7:16 am

Unfortunaly the adapter still had problems, as on Toledo the indication of a promotion piece is mandatory (while uMax assumes Q if it is not given). I fixed the adapter so that it now correctly handles promotions both ways (Toleo can underpromote!). The new source is given below. (Not thoroughly tested, as promotions are rare, and Toledo plays slowly.)

Do you have any idea what causes the 'memory leak' in Toledo? Is it that 'main' reserves space for buffers, each time it is called? (And because 'main' is used as Search() in Toledo it is called really often!) Not having a separate routine for Search() might save some charcters, but this effect is quickly undone if you need all the buffer stuff to prevent it from crashing! (Which probaby forces you to include stdio.h as well...)

Can you also compile a blitz version of Toledo? You can make this by changing the '5' on the last line (3rd character) into '4'.

Note that Toledo tends to hang after a game, using full CPU. The adapter tries to kill it, but apparently this does not work.

The new Max2WB.c:

Code: Select all

/**************************************************/
/*        Max2WB adapter by H.G. Muller           */
/* Allows micro-Max stand-alone versions, as well */
/* as Toledo_nanochess to play under WinBoard     */
/* Use "Max2WB <egine> as WinBoard chess-program  */
/* name, where <engine> is the name of the engine */
/* executable file.                               */
/**************************************************/

#include <stdio.h>
#include <signal.h>

char before&#91;80&#93;, after&#91;80&#93;;
FILE *f;

void ReadBoard&#40;int inp, char *board&#41;
&#123;       // read 64 non-blank characters
        int i;
        char c, *oldBoard = board;

        for&#40;i=0; i<64; i++) &#123;
            do&#123; read&#40;inp, &c, 1&#41;;
            &#125; while&#40;c == '\n' || c == ' ' || c == '\t' || c == '\r');
            *board++ = c;
        &#125;
        fprintf&#40;f, "E< '%s'\n", oldBoard&#41;; fflush&#40;f&#41;;
&#125;

void ProduceMove&#40;int from, int to&#41;
&#123;
        int ifrom=-1, ito=-1, i;
        write&#40;to, "\n", 1&#41;;      // send thinking command &#40;empty line&#41;
        ReadBoard&#40;from, after&#41;;  // should respond with board;
        for&#40;i=0; i<64; i++) &#123;
            if&#40;before&#91;i&#93; == after&#91;i&#93;) continue;
            if&#40;after&#91;i&#93; == '.') &#123;
                // squre became empty&#58; from-square
                if&#40;ifrom <= 0 || ifrom == 7 || ifrom == 070 || ifrom == 077&#41;
                     ifrom = i;  // first one, or overwrite corner Rook
                else if&#40;ifrom != 4 && ifrom != 074&#41;
                     ifrom = 65; // two fromSqr, and not K or R&#58; must be e.p.
            &#125; else &#123;
                // if not empty after move, must be to-square
                if&#40;ito < 0 || ito != 2 && ito != 072&#41;
                     ito = i; // first one, or write King over Rook
            &#125;
            if&#40;ifrom == 65&#41; ifrom = ito ^ 010; // calculate e.p. fromSqr
        &#125;
        if&#40;before&#91;ifrom&#93; != after&#91;ito&#93;) // piece changed&#58; promotion
            printf&#40;"move %c%c%c%c%c\n", 'a'+&#40;ifrom&7&#41;, '8'-&#40;ifrom>>3&#41;,
                                        'a'+&#40;ito&7&#41;,   '8'-&#40;ito>>3&#41;,
                                        after&#91;ito&#93;|32&#41;;
        else
            printf&#40;"move %c%c%c%c\n", 'a'+&#40;ifrom&7&#41;, '8'-&#40;ifrom>>3&#41;,
                                      'a'+&#40;ito&7&#41;,   '8'-&#40;ito>>3&#41;);
        fflush&#40;stdout&#41;;
&#125;

main&#40;int argc, char **argv&#41;
&#123;
        int toPipe&#91;2&#93;, fromPipe&#91;2&#93;, from_prog, to_prog, pid, pid2, i, j;
        char c, buf&#91;256&#93;, command&#91;256&#93;, *name;

        if&#40;argc < 2&#41;
            exit&#40;0&#41;;   // first arg must be engine

        name = argv&#91;1&#93;; while&#40;*name&#41; name++;
        while&#40;name-argv&#91;1&#93; > 0 && name&#91;-1&#93; != '\\' && name&#91;-1&#93; != '/') name--;

        /* OK, so we now send through to_prog, receive through from_prog */
        
        f = fopen&#40;"log", "w");

        &#123;   /* cmain loop ommunicates to WinBoard */
            int forceMode = 0;

            setbuf&#40;stdin, NULL&#41;;
            i = 0;
            while&#40;&#40;c=getchar&#40;)) != EOF&#41; &#123;
                if&#40;c == '\r') continue;
                if&#40;c != '\n') &#123; buf&#91;i++&#93; = c; continue; &#125;
                buf&#91;i&#93; = 0;
                fprintf&#40;f, "WB> %s\n", buf&#41;; fflush&#40;f&#41;;

                sscanf&#40;buf, "%s", command&#41;;
                if&#40;!strcmp&#40;command, "quit"))       // pass on & kill
                    close&#40;to_prog&#41;,
                    close&#40;from_prog&#41;,
                    kill&#40;pid,  SIGKILL&#41;,
                    fclose&#40;f&#41;,
                    exit&#40;0&#41;;
                else if&#40;!strcmp&#40;command, "result")) // pass on & kill
                    close&#40;to_prog&#41;,
                    close&#40;from_prog&#41;,
                    kill&#40;pid,  SIGKILL&#41;;
                else if&#40;!strcmp&#40;command, "protover"))
                    printf&#40;"feature myname=\"%s\" done=1\n", name&#41;,
                    fflush&#40;stdout&#41;;
                else if&#40;!strcmp&#40;command, "go"))    // end force mode & think
                    forceMode=0,
                    ProduceMove&#40;from_prog, to_prog&#41;;
                else if&#40;!strcmp&#40;command, "force")) // remember
                    forceMode = 1;
                else if&#40;!strcmp&#40;command, "new")) &#123; // start new engine proc
                    /* close old pipes */
                    if&#40;from_prog&#41;close&#40;from_prog&#41;;
                    if&#40;to_prog&#41;close&#40;to_prog&#41;;
                    /* set up pipes */
                    pipe&#40;toPipe&#41;;
                    pipe&#40;fromPipe&#41;;
                    /* create engine */
                    if&#40;&#40;pid = fork&#40;)) == 0&#41;
                    &#123;   /* child */
                        fclose&#40;stderr&#41;;
                        close&#40;toPipe&#91;1&#93;);
                        close&#40;fromPipe&#91;0&#93;);
                        dup2&#40;toPipe&#91;0&#93;, 0&#41;;
                        dup2&#40;fromPipe&#91;1&#93;, 1&#41;;
                        if&#40;toPipe&#91;0&#93; >= 2&#41; close&#40;toPipe&#91;0&#93;);
                        close&#40;fromPipe&#91;1&#93;);
                        execv&#40;argv&#91;1&#93;, argv+1&#41;;
                        perror&#40;argv&#91;1&#93;);
                        exit&#40;1&#41;;
                    &#125;
                    /* parent */
                    close&#40;toPipe&#91;0&#93;);
                    close&#40;fromPipe&#91;1&#93;);
                    from_prog = fromPipe&#91;0&#93;;
                    to_prog   = toPipe&#91;1&#93;;
                    forceMode = 0;
                    ReadBoard&#40;from_prog, before&#41;;
                &#125; else if&#40;&#40;buf&#91;4&#93; == 0 || buf&#91;5&#93; == 0&#41; &&
                        command&#91;0&#93; >= 'a' && command&#91;0&#93; <= 'h' &&
                        command&#91;2&#93; >= 'a' && command&#91;2&#93; <= 'h' &&
                        command&#91;1&#93; >= '1' && command&#91;1&#93; <= '8' &&
                        command&#91;3&#93; >= '1' && command&#91;3&#93; <= '8'   ) // pass on
                &#123; /* move */ char c; int k;
                    if&#40;c = buf&#91;4&#93;) &#123;
                        k = buf&#91;0&#93;-'a'+('8'-buf&#91;1&#93;)*8;
                        if&#40;after&#91;k&#93;=='+' || after&#91;k&#93; == '*') // uMax
                             k = c=='q'?0&#58;c=='r'?1&#58;c=='b'?2&#58;c=='n'?3&#58;4;
                        else k = c=='q'?6&#58;c=='r'?5&#58;c=='b'?4&#58;c=='n'?3&#58;2;
                        sprintf&#40;command, "%c%c%c%c%d\n",
                                buf&#91;0&#93;, buf&#91;1&#93;, buf&#91;2&#93;, buf&#91;3&#93;, k&#41;;
                        write&#40;to_prog, command, 6&#41;;
                    &#125; else &#123; sprintf&#40;command, "%c%c%c%c\n",
                                     buf&#91;0&#93;, buf&#91;1&#93;, buf&#91;2&#93;, buf&#91;3&#93;);
                        write&#40;to_prog, command, 5&#41;;
                    &#125;
                    ReadBoard&#40;from_prog, before&#41;; // read board after forced move
                    if&#40;!forceMode&#41;     // go thinking if not in force mode
                        ProduceMove&#40;from_prog, to_prog&#41;;
                &#125;
                i = 0;
            &#125;
        &#125;
&#125;

User avatar
hgm
Posts: 23631
Joined: Fri Mar 10, 2006 9:06 am
Location: Amsterdam
Full name: H G Muller
Contact:

Re: Current world's smallest chess program

Post by hgm » Sun Feb 22, 2009 10:50 am

I guess the hypothesis that some compilers cannot handle excessive recursive calling of main() because they hide code in there to allocate buffer memory is correct. I created a version of Toledo_nanochess where I split main(f,w,c,h,e,S,s) into a D(f,w,c,h,e,S,s) and a main() (the two code parts that were originally sistinguished by the 'if(*I)' statement), replacing all calls to main() by calls to D().

After shuttling some variables between local and global declarations, to make sure that the main() part has enough variables to perform its intended actions, this finally results in a source that:
1) stays within its initil memory allocation
2) uses unbuffered output through write() in stead of putchar()
3) is two characters shorter!

The problem that Toledo hangs (using CPU) after a game remains, though. Does anyone have an idea why the killing by the adapter does not work as I implemented it? Are there more reliable way under Windows to forcefully terminate a daughter process?

User avatar
hgm
Posts: 23631
Joined: Fri Mar 10, 2006 9:06 am
Location: Amsterdam
Full name: H G Muller
Contact:

Re: Current world's smallest chess program

Post by hgm » Mon Feb 23, 2009 9:54 am

I tried a Nunn match between micro-Max 1.6 and Toledo_nanochess. (Yes, the protocol of this new Toledo does support force mode, so you can play it with an external book!) To make for approximately fair CPU time usage I set uMax to 500,000 nodes (minimum) and Toledo-nC to 4 ply. (This requires altering of the source and re-compilation; the standard version of Toledo-nC is set for 5 ply.) This sets the programs to blitz speed.

It seems the programs are well matched at this speed (5+, 11=, 4- in the advantage of uMax). As both programs cannot recognize repeats, there are a lot of unnecessary draws. The worst cases of this were uMax not being able to win in KRBPPPPPK, and Toledo not being able to win KQKP (with a 4th-rank Pawn...). It is difficult to design compltely air conditions, as Toledo-nC uses fixed-depth, and uMax plays by node-count. So Toledo-nC tends to think longer in the middle-game, but then completely catches up in the end-game. In the end one of the two is likely to have used much more time than the other, (typically twice as much), but who that is depends on when the Queens were traded, and if there was a quick mate or a tedious end-game.

Nevertheless, they play purposefully, and both are able to win games. 8-)

User avatar
nanochess
Posts: 64
Joined: Thu Feb 19, 2009 4:34 pm
Location: Mexico, Mexico
Contact:

Re: Current world's smallest chess program

Post by nanochess » Mon Feb 23, 2009 3:54 pm

I did the following measurements:
  • 1. byte-size, using *NIX end-of-line \n (not \r\n)
    2. Non-blank: Characters !isspace(c)
    3. IOCCC: Characters !isspace(c) minus { } ; followed by isspace
In case of doubt use byte-size.
For Bill: To see the Javascript source, download the compressed file and open it with Wordpad (not Notepad). C and BASIC are very similar in expression treatment, the most difficult thing can be the different symbols for operators.
I have heard of the Byte APL Chess, although I have some very old numbers of Byte, I don't have the APL special, would be nice if someone scans it.
I tested several platforms, except Cygwin, as chess programming is a hobby mine and I do work in my pretty scarce free-time, could take me some time to do a new version of Toledo Nanochess well tested on Cygwin.

User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 6:43 pm

APL/J chess

Post by sje » Mon Feb 23, 2009 8:26 pm

Thirty five years ago, APL was fairly common and was offered back then along with Basic as a language option in IBM's first very personal computer. (Yes, IBM had a PC before its 1981 Intel 8088 based machine.)

I always liked APL with its funky character glyphs along with its ability to express pages of FORTRAN computation in a line or two. But its days have come and gone. The modern replacement for APL is the J language. J is nearly isomorphic to APL, but instead uses standard ASCII characters. Program sources are a bit longer because of use of multi-character operators; however, I'd guess that a sub 1 KB J chess program is certainly possible.

IanO
Posts: 476
Joined: Wed Mar 08, 2006 8:45 pm
Location: Portland, OR
Contact:

Re: Current world's smallest chess program

Post by IanO » Mon Feb 23, 2009 8:46 pm

sje wrote:Back in its youthful and productive years (late 1970s), Byte magazine had an APL themed issue. One of the sample programs was a chessplayer written in APL and I believe it had under a thousand characters of source.
I believe it. I'm honestly surprised no one has come up with an APL or J chess program. These languages are extremely efficient at modeling problems which are naturally represented in arrays and matrices.

Ian

Dann Corbit
Posts: 10000
Joined: Wed Mar 08, 2006 7:57 pm
Location: Redmond, WA USA
Contact:

Re: Current world's smallest chess program

Post by Dann Corbit » Mon Feb 23, 2009 8:58 pm

hgm wrote:I guess the hypothesis that some compilers cannot handle excessive recursive calling of main() because they hide code in there to allocate buffer memory is correct. I created a version of Toledo_nanochess where I split main(f,w,c,h,e,S,s) into a D(f,w,c,h,e,S,s) and a main() (the two code parts that were originally sistinguished by the 'if(*I)' statement), replacing all calls to main() by calls to D().

After shuttling some variables between local and global declarations, to make sure that the main() part has enough variables to perform its intended actions, this finally results in a source that:
1) stays within its initil memory allocation
2) uses unbuffered output through write() in stead of putchar()
3) is two characters shorter!

The problem that Toledo hangs (using CPU) after a game remains, though. Does anyone have an idea why the killing by the adapter does not work as I implemented it? Are there more reliable way under Windows to forcefully terminate a daughter process?
In C (but NOT in C++!) the ANSI/ISO standards say that you must be able to call main recursively.

However, the signature of his program is not one of the two signatures recognized by the standards document:
int main(void);
int main(int argc, char **argv);

Alternative signatures are allowed, but they must be documented by the compiler, and they are only valid for a compiler that implements them.
Hence, if his program works, it works by accident unless you have a compiler that specifically allows the signature:
int main(int, int, int, int, int, int, int);

User avatar
hgm
Posts: 23631
Joined: Fri Mar 10, 2006 9:06 am
Location: Amsterdam
Full name: H G Muller
Contact:

Re: Current world's smallest chess program

Post by hgm » Mon Feb 23, 2009 9:42 pm

Oscar Toledo gave me permision to publish my patch to Toledo_nanochess to fix the problems under gcc + Cygwin + Windows, which I do here:

Code: Select all

/**************************************************************************\
| Toledo Nanochess &#40;c&#41; Copyright 2009 Oscar Toledo G. All rights reserved  |
| 1274 non-blank characters. Evolution from my winning IOCCC 2005 entry.   |
| o Use D2D4 algebraic style for movements.  biyubi@gmail.com  Feb/13/2009 |
| o On promotion add a number for final piece &#40;3=N, 4=B, 5=R, 6=Q&#41;         |
| o Press Enter alone for computer to play.                                |
| o Full legal chess moves.                                                |
| o Remove these comments to get 1341 bytes source code (*NIX end-of-line&#41; |
| o This version patched to not crash under Cygwin                         |
\**************************************************************************/
char*l="ustvrtsuqqqqqqqqyyyyyyyy&#125;&#123;|~z|&#123;&#125;"
" + BAW~abcddcba .pknbrq  PKNBRQ\n?A6J57IKJT576,+-48HLSU";
#define F getchar&#40;)%16
#define v D&#40;21,0,0,0,
#define Z while&#40;
#define _ ;if&#40;
#define P return y^=8,
int level;
B,i,y,u,b,I&#91;120&#93;,G,x=10,z=15,q=100;D&#40;f,w,c,h,e,S,s&#41;&#123;int t,o,L,E,d,O=f,N=-1e9,p,
*g,n,*m=I,H,A,q,r,C,a=y?-x&#58;x;y^=8;d=!s||1e5>v 0,1,0&#41;;H=G;do&#123;_ o=I&#91;p=O&#93;)&#123;q
=o&z^y _ q<7&#41;&#123;A=q--&2?8&#58;4;C=o-9&z?q&#91;"& .$  "&#93;&#58;42;do&#123;r=I&#91;p+=C&#91;l&#93;-64&#93;_!w|p==w&&q|
A>2|!r&#41;&#123;g=q|p+a-e?0&#58;I+e _!r&&#40;q|A<3||g&#41;|&#40;r+1&z^y&#41;>9&#41;&#123;m=0 _!&#40;r-2&7&#41;&#41;P G=O,1e6-1e3
*h;n=o&z;t=q|p>28&p<91?n+1&#58;&#40;n+=2,7^y&#41;;Z n-t&#41;&#123;p&#91;I&#93;=n,E=O&#91;I&#93;=m?*g=*m,*m=0&#58;g?*g=0&#58;
0;L=&#40;1-q?l&#91;p/x+5&#93;-l&#91;O/x+5&#93;+l&#91;p%x+6&#93;-l&#91;O%x+6&#93;+o/16*8&#58;!!m*9&#41;+(!q?l&#91;p%x+6&#93;-98+!&#40;I&#91;
p-1&#93;^n&#41;+!&#40;I&#91;p+1&#93;^n&#41;+l&#91;n&7&#93;*9-387+!!g*99+&#40;1==A&&&#40;E=p&#41;)&#58;0&#41;+&#40;r?l&#91;r&7&#93;*9-288-h-q&#58;0&#41;
+!&#40;I&#91;p-a&#93;&z^y^9&#41;;L-=s>h||s==h&&#40;L>z&1<s|!d&#41;?D&#40;H,s>h|!d?0&#58;p,L,h+1,E,N,s&#41;&#58;0 _!(
B-O|i-n|h|p-b|S|L<-1e5&#41;&#41;return u=E;O&#91;I&#93;=o;p&#91;I&#93;=r;m?*m=*g,*g=0&#58;g?*g=9^y&#58;0 _ L>N&#41;
&#123;N=L;G=O _!h&S&&s&#41;i=n,B=O,b=p _ h&&c-L<S&#41;P L;&#125;q==1&A>6&!m&&&#40;g=I+p,m=p<O?g-3&#58;g+2
,o-y+*m>32&!r&!I&#91;p+=p-O&#93;&!m&#91;p<O?1&#58;-1&#93;&!!s&d&L>-1e5&#41;?0&#58;n++;&#125;&#125;&#125;&#125;Z!r&q>2||&#40;p=O,q|A
>2|&#40;o&16&&!r&#41;&&++C&&--A&#41;);&#125;&#125;&#125;Z++O>98?O=20&#58;f-O&#41;;P N+1e9?N<-998100+1e3*h&d?0&#58;N&#58;0;
&#125;main&#40;)&#123;Z B<120&#41;I&#91;B++&#93;=B%x?B/x%x<2|B%x<2?z&#58;B/x&4?0&#58;*l++&31&#58;7;Z i=19&#41;&#123;Z++i<q&#41;write&#40;1,l+(
i&#91;I&#93;|16&#41;,1&#41;_ x-&#40;B=F&#41;)&#123;i=I&#91;B+=q-F*x&#93;&z;b=F;b+=q-F*x;Z x-&#40;G=F&#41;&#41;i=G^8^y;&#125;else G=v u,
1,5&#41;;v u,0,1&#41;;&#125;&#125;
To make Toledo_nanochess play blitz (more exciting, and I also have the feeling it is better at that), change the '5' on the last line to a '4'.

In de-obfuscated form, the idea of the patch is to change the recursive calling of main() as it was done in the original program:

Code: Select all

int board&#91;&#93;;

main&#40;arguments&#41;
&#123;
    if&#40;*board&#41; // if bord is initialized
    &#123;
        // do stuff that belongs in Search&#40;)
        ...
        main&#40;....); // recursive calling
        ...
        return score;
    &#125;
    // do stuff that belongs in main&#40;)&#58;
    // Initialize board
    // I/O loop
    &#123;
        main&#40;...); // go searching
    &#125;
&#125;
to code with separate routine for main() and Serach();

Code: Select all

int board&#91;&#93;;

D&#40;arguments&#41;
    &#123;
        // do stuff that belongs in Search&#40;)
        ...
        D&#40;....); // recursive calling
        ...
        return score;
    &#125;

main&#40;)
&#123;
    // do stuff that belongs in main&#40;)&#58;
    // Initialize board
    // I/O loop
    &#123;
        D&#40;...); // go searching
    &#125;
&#125;
Because it takes fewer characters to spell 'D' than to spell 'main', and the call to Search() occurs in several places, and ithe if(*board) is no longer needed, this does not increase the character count.

I also have the final version of the Max2WB adapter now, after ironing out a bug in the hanling of e.p. captures:

Code: Select all

/***************************************************/
/*        Max2WB adapter by H.G. Muller            */
/* Allows micro-Max stand-alone versions, as well  */
/* as Toledo_nanochess to play under WinBoard.     */
/* Use "Max2WB <engine>" as WinBoard chess-program */
/* name, where <engine> is the name of the engine  */
/* executable file.                                */
/***************************************************/

#include <stdio.h>
#include <signal.h>

char before&#91;80&#93;, after&#91;80&#93;;
FILE *f;

void ReadBoard&#40;int inp, char *board&#41;
&#123;       // read 64 non-blank characters
        int i;
        char c, *oldBoard = board;

        for&#40;i=0; i<64; i++) &#123;
            do&#123; read&#40;inp, &c, 1&#41;;
            &#125; while&#40;c == '\n' || c == ' ' || c == '\t' || c == '\r');
            *board++ = c;
        &#125;
        fprintf&#40;f, "E> '%s'\n", oldBoard&#41;; fflush&#40;f&#41;;
&#125;

void ProduceMove&#40;int from, int to&#41;
&#123;
        int ifrom=-1, ito=-1, i;
        write&#40;to, "\n", 1&#41;;      // send thinking command &#40;empty line&#41;
        ReadBoard&#40;from, after&#41;;  // should respond with board;
        for&#40;i=0; i<64; i++) &#123;
            if&#40;before&#91;i&#93; == after&#91;i&#93;) continue;
            if&#40;after&#91;i&#93; == '.') &#123;
                // square became empty&#58; from-square
                if&#40;ifrom <= 0 || ifrom == 7 || ifrom == 070 || ifrom == 077&#41;
                     ifrom = i;  // first one, or overwrite corner Rook
                else if&#40;ifrom != 4 && ifrom != 074&#41;
                     ifrom = i+64; // two fromSqr, and not K/R&#58; must be e.p.
            &#125; else &#123;
                // if not empty after move, must be to-square
                if&#40;ito < 0 || ito != 2 && ito != 072&#41;
                     ito = i; // first one, or write King over Rook
            &#125;
        &#125;
        if&#40;ifrom >= 64&#41; // calculate e.p. fromSqr
            ifrom = ifrom-64 == &#40;ito^010&#41; ? ifrom - 65 &#58; ifrom - 64;
        if&#40;before&#91;ifrom&#93; != after&#91;ito&#93;) // piece changed&#58; promotion
            printf&#40;"move %c%c%c%c%c\n", 'a'+&#40;ifrom&7&#41;, '8'-&#40;ifrom>>3&#41;,
                                        'a'+&#40;ito&7&#41;,   '8'-&#40;ito>>3&#41;,
                                        after&#91;ito&#93;|32&#41;;
        else
            printf&#40;"move %c%c%c%c\n", 'a'+&#40;ifrom&7&#41;, '8'-&#40;ifrom>>3&#41;,
                                      'a'+&#40;ito&7&#41;,   '8'-&#40;ito>>3&#41;);
        fflush&#40;stdout&#41;;
&#125;

main&#40;int argc, char **argv&#41;
&#123;
        int toPipe&#91;2&#93;, fromPipe&#91;2&#93;, from_prog, to_prog, pid, pid2, i, j;
        char c, buf&#91;256&#93;, command&#91;256&#93;, *name;

        if&#40;argc < 2&#41;
            exit&#40;0&#41;;   // first arg must be engine

        name = argv&#91;1&#93;; while&#40;*name&#41; name++;
        while&#40;name-argv&#91;1&#93; > 0 && name&#91;-1&#93; != '\\' && name&#91;-1&#93; != '/') name--;

        /* OK, so we now send through to_prog, receive through from_prog */
        
        f = fopen&#40;"log", "w");

        &#123;   /* cmain loop ommunicates to WinBoard */
            int forceMode = 0;

            setbuf&#40;stdin, NULL&#41;;
            i = 0;
            while&#40;&#40;c=getchar&#40;)) != EOF&#41; &#123;
                if&#40;c == '\r') continue;
                if&#40;c != '\n') &#123; buf&#91;i++&#93; = c; continue; &#125;
                buf&#91;i&#93; = 0;
                fprintf&#40;f, "WB> %s\n", buf&#41;; fflush&#40;f&#41;;

                sscanf&#40;buf, "%s", command&#41;;
                if&#40;!strcmp&#40;command, "quit"))       // pass on & kill
                    close&#40;to_prog&#41;,
                    close&#40;from_prog&#41;,
                    kill&#40;pid,  SIGKILL&#41;,
                    fclose&#40;f&#41;,
                    exit&#40;0&#41;;
                else if&#40;!strcmp&#40;command, "result")) // pass on & kill
                    close&#40;to_prog&#41;,
                    close&#40;from_prog&#41;,
                    kill&#40;pid,  SIGKILL&#41;;
                else if&#40;!strcmp&#40;command, "protover"))
                    printf&#40;"feature myname=\"%s\" done=1\n", name&#41;,
                    fflush&#40;stdout&#41;;
                else if&#40;!strcmp&#40;command, "go"))    // end force mode & think
                    forceMode=0,
                    ProduceMove&#40;from_prog, to_prog&#41;;
                else if&#40;!strcmp&#40;command, "force")) // remember
                    forceMode = 1;
                else if&#40;!strcmp&#40;command, "new")) &#123; // start new engine proc
                    /* close old pipes */
                    if&#40;from_prog&#41;close&#40;from_prog&#41;;
                    if&#40;to_prog&#41;close&#40;to_prog&#41;;
                    if&#40;pid&#41; kill&#40;pid, SIGKILL&#41;;
                    /* set up pipes */
                    pipe&#40;toPipe&#41;;
                    pipe&#40;fromPipe&#41;;
                    /* create engine */
                    if&#40;&#40;pid = fork&#40;)) == 0&#41;
                    &#123;   /* child */
                        fclose&#40;stderr&#41;;
                        close&#40;toPipe&#91;1&#93;);
                        close&#40;fromPipe&#91;0&#93;);
                        dup2&#40;toPipe&#91;0&#93;, 0&#41;;
                        dup2&#40;fromPipe&#91;1&#93;, 1&#41;;
                        if&#40;toPipe&#91;0&#93; >= 2&#41; close&#40;toPipe&#91;0&#93;);
                        close&#40;fromPipe&#91;1&#93;);
                        execv&#40;argv&#91;1&#93;, argv+1&#41;;
                        perror&#40;argv&#91;1&#93;);
                        exit&#40;1&#41;;
                    &#125;
                    /* parent */
                    close&#40;toPipe&#91;0&#93;);
                    close&#40;fromPipe&#91;1&#93;);
                    from_prog = fromPipe&#91;0&#93;;
                    to_prog   = toPipe&#91;1&#93;;
                    forceMode = 0;
                    ReadBoard&#40;from_prog, before&#41;;
                &#125; else if&#40;&#40;buf&#91;4&#93; == 0 || buf&#91;5&#93; == 0&#41; &&
                        command&#91;0&#93; >= 'a' && command&#91;0&#93; <= 'h' &&
                        command&#91;2&#93; >= 'a' && command&#91;2&#93; <= 'h' &&
                        command&#91;1&#93; >= '1' && command&#91;1&#93; <= '8' &&
                        command&#91;3&#93; >= '1' && command&#91;3&#93; <= '8'   ) // pass on
                &#123; /* move */ char c; int k;
                    if&#40;c = buf&#91;4&#93;) &#123;
                        k = buf&#91;0&#93;-'a'+('8'-buf&#91;1&#93;)*8;
                        if&#40;after&#91;k&#93;=='+' || after&#91;k&#93; == '*') // uMax
                             k = c=='q'?0&#58;c=='r'?1&#58;c=='b'?2&#58;c=='n'?3&#58;4;
                        else k = c=='q'?6&#58;c=='r'?5&#58;c=='b'?4&#58;c=='n'?3&#58;2;
                        sprintf&#40;command, "%c%c%c%c%d\n",
                                buf&#91;0&#93;, buf&#91;1&#93;, buf&#91;2&#93;, buf&#91;3&#93;, k&#41;;
                        write&#40;to_prog, command, 6&#41;;
                    &#125; else &#123; sprintf&#40;command, "%c%c%c%c\n",
                                     buf&#91;0&#93;, buf&#91;1&#93;, buf&#91;2&#93;, buf&#91;3&#93;);
                        write&#40;to_prog, command, 5&#41;;
                    &#125;
                    fprintf&#40;f, "E< %s", command&#41;; fflush&#40;f&#41;;
                    ReadBoard&#40;from_prog, before&#41;; // read board after forced move
                    if&#40;!forceMode&#41;     // go thinking if not in force mode
                        ProduceMove&#40;from_prog, to_prog&#41;;
                &#125;
                i = 0;
            &#125;
        &#125;
&#125;
In my testing of Toleo_nanochess against uMax I have now seen all kind of moves, includng e.p. capture and under-promotion, so everyting is tested now. (And seems to work fine.)

User avatar
Jim Ablett
Posts: 1327
Joined: Fri Jul 14, 2006 5:56 am
Location: London, England
Contact:

Re: Current world's smallest chess program

Post by Jim Ablett » Mon Feb 23, 2009 10:53 pm

New compiles:
Max2WB (final) + Toledo Nanochess (patched).
Download:
http://www.mediafire.com/?4zu4yyzmymn

And working very nicely too :)

Jim.

Christopher Conkie
Posts: 6068
Joined: Sat Apr 01, 2006 7:34 pm
Location: Scotland
Contact:

Re: Current world's smallest chess program

Post by Christopher Conkie » Mon Feb 23, 2009 11:42 pm

Jim Ablett wrote:New compiles:
Max2WB (final) + Toledo Nanochess (patched).
Download:
http://www.mediafire.com/?4zu4yyzmymn

And working very nicely too :)

Jim.
Thank you Jim for the welcome diversion. I suppose even I can have some fun now.

:)

Christopher

Post Reply