I'm enclosing a c file at the end of this. It makes a program called metamachine. I have certain plans for this but that's a long story I'll have to explain later. In a nutshell I want GNU Go to be able to clone itself (hash, persistent cache and all) then ask the clone questions.
At the moment, this program is working, or seems to work, but there's a couple of problems I need help with.
It emulates a gtp engine by spawning a copy of gnugo and passing commands from stdin to the engine, getting the response, then passing them back to stdout. Though useless in itself, this could be made useful with a bit of work: hack in gmp.c and get a gtp/gmp translator.
I'm not a big unix hacker but wrote this after studying Chapter 6 of Rochkind's "Advanced Unix Programming" and the libc doc. As I said, it seems to work:
$ metamachine boardsize 9 =
komi 5.5 =
showboard A B C D E F G H J 9 . . . . . . . . . 9 8 . . . . . . . . . 8 7 . . + . . . + . . 7 6 . . . . . . . . . 6 5 . . . . + . . . . 5 4 . . . . . . . . . 4 3 . . + . . . + . . 3 2 . . . . . . . . . 2 WHITE has captured 0 stones 1 . . . . . . . . . 1 BLACK has captured 0 stones A B C D E F G H J =
genmove_black = G7
PROBLEM 1: It doesn't kill the gnugo process when it exits. (Run it a few times then ask yourself where all your memory went.) What's the right way to do this?
PROBLEM 2: It hangs when called from twogtp. I can't figure out why.
Dan
#include <stdio.h> #include <unistd.h>
void error(const char *msg);
/* This program uses two pipes to communicate with the * GNU Go client. To an external client it appears to * be a gtp engine. It accomplishes this by intercepting * gtp commands and passing them on to GNU Go. * * This program in and of itself is not useful but it * can be the basis of useful programs. Example: hack * in gmp.c and get a gtp / gmp translator. * * Bugs: the spawned process isn't killed when the * program terminates. Hangs when called from twogtp. (Why?) * * Pipe a: client --> gnugo * Pipe b: gnugo --> client */
int main() { int pfd_a[2]; int pfd_b[2]; char gnugo_line[128], client_line[128]; int length = 0; FILE *to_gnugo_stream, *from_gnugo_stream;
if (pipe(pfd_a) == -1) error("can't open pipe a"); if (pipe(pfd_b) == -1) error("can't open pipe b"); switch(fork()) { case -1: error("fork failed (try chopsticks)"); case 0: /* Attach pipe a to stdin */ if (close(0) == -1) error("close(0) failed"); if (dup(pfd_a[0]) != 0) error("dup pfd_a[0] failed"); /* attach pipe b to stdout" */ if (close(1) == -1) error("close(1) failed"); if (dup(pfd_b[1]) != 1) error("dup pfd_b[1] failed"); execlp("gnugo", "gnugo", "--mode", "gtp", "--quiet", NULL); error("execlp failed"); } /* We use stderr to communicate with the client since stdout is needed. */ /* Attach pipe a to to_gnugo_stream */ to_gnugo_stream = fdopen(pfd_a[1], "w"); /* Attach pipe b to from_gnugo_stream */ from_gnugo_stream = fdopen(pfd_b[0], "r");
while (1) { int length = 0;
if (!fgets(client_line, 128, stdin)) error("can't get command\n"); if (!strncmp(client_line, "quit", 4)) return 1;
if (fprintf(to_gnugo_stream, "%s", client_line) < 0) error("can't write command to to_gnugo_stream"); fflush(to_gnugo_stream); while (length != 1) { if (!fgets(gnugo_line, 128, from_gnugo_stream)) error("can't get response"); length = strlen(gnugo_line); printf(gnugo_line); } } }
void error(const char *msg) { fprintf(stderr, "metamachine: %s\n", msg); abort(); }