This is the doxygen documentation for gtkboard.

.
Main Page   Data Structures   File List   Data Fields   Globals  

engine.c

Go to the documentation of this file.
00001 /*  This file is a part of gtkboard, a board games system.
00002     Copyright (C) 2003, Arvind Narayanan <arvindn@users.sourceforge.net>
00003 
00004     This program is free software; you can redistribute it and/or modify
00005     it under the terms of the GNU General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or
00007     (at your option) any later version.
00008 
00009     This program is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012     GNU General Public License for more details.
00013 
00014     You should have received a copy of the GNU General Public License
00015     along with this program; if not, write to the Free Software
00016     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
00017 
00018 */
00022 #include "game.h"
00023 #include "move.h"
00024 #include "stack.h"
00025 #include "engine.h"
00026 
00027 #include <signal.h>
00028 #include <string.h>
00029 #include <stdlib.h>
00030 #include <assert.h>
00031 #include <unistd.h>
00032 
00033 extern int board_wid, board_heit;
00034 extern int opt_verbose;
00035 //extern int state_player;
00036 
00037 // FIXME: this is ugly. Code should be refactored.
00038 extern gboolean engine_flag;
00039 
00040 extern Pos cur_pos;
00041 
00042 extern Game *opt_game, *games[];
00043 extern int num_games;
00044 byte * engine_search (Pos *);
00045 static FILE *engine_fin, *engine_fout;
00046 
00048 ResultType (*game_eval_white) (Pos *, Player, float *);
00050 ResultType (*game_eval_black) (Pos *, Player, float *);
00051 
00052 // FIXME: following 3 extern decls must be removed by refactoring (i.e, move all fns common to client and server to a new file)
00053 extern void reset_game_params ();
00054 extern void set_game_params ();
00055 extern void game_set_init_pos_def (Pos *);
00056 
00058 extern byte *ab_dfid (Pos *, int);
00059 
00061 static GIOChannel *channel_in = NULL;
00062 
00064 gboolean engine_stop_search = FALSE;
00065 
00067 static gboolean cancel_move = FALSE;
00068 
00070 int time_per_move = 5000;
00071 
00072 gboolean engine_hup_cb ()
00073 {
00074         if (opt_verbose)
00075                 fprintf (stderr, "engine: Connection broken. Exiting.\n");
00076         // FIXME: do we want to free anything here?
00077         exit (1);
00078 }
00079 
00080 ResultType engine_eval (Pos *pos, /*Player player,*/ float *eval)
00081 {
00082         ResultType result;
00083         result = pos->player == WHITE ? game_eval_white(pos, pos->player, eval) :
00084         game_eval_black (pos, pos->player, eval);
00085         return result;
00086 }
00087 
00088 void engine_set_to_play (char *line)
00089 {
00090         if (!strncasecmp (line, "white", 5))
00091                 cur_pos.player = WHITE;
00092         else if (!strncasecmp (line, "black", 5))
00093                 cur_pos.player = BLACK;
00094 }
00095 
00096 void engine_take_move (char *line)
00097 {
00098         byte *move = move_read (line);
00099         movstack_trunc ();
00100         movstack_push (cur_pos.board, move);
00101         if (game_stateful)
00102         {
00103                 void *newstate = game_newstate (&cur_pos, move);
00104                 statestack_push (newstate);
00105                 cur_pos.state = statestack_peek ();
00106         }
00107         move_apply (cur_pos.board, move);
00108         cur_pos.num_moves++;
00109         cur_pos.player = cur_pos.player == WHITE ? BLACK : WHITE;
00110 }
00111 
00112 void engine_make_move ()
00113 {
00114         byte *move;
00115         movstack_trunc ();
00116         cancel_move = FALSE;
00117         move = engine_search (&cur_pos);
00118         if (cancel_move)
00119                 return;
00120         if (!move)
00121         {
00122                 move_fwrite_nak ("Nice try", engine_fout);
00123                 return;
00124         }
00125         movstack_push (cur_pos.board, move);
00126         if (game_stateful)
00127         {
00128                 void *newstate = game_newstate (&cur_pos, move);
00129                 statestack_push (newstate);
00130                 cur_pos.state = statestack_peek ();
00131         }
00132         move_apply (cur_pos.board, move);
00133         cur_pos.num_moves++;
00134         move_fwrite_ack (move, engine_fout);
00135         cur_pos.player = cur_pos.player == WHITE ? BLACK : WHITE;
00136 }
00137 
00138 void engine_new_game (char *gamename)
00139 {
00140         int i, len;
00141         opt_game = NULL;
00142         // strip trailing newline
00143         if (gamename[len = strlen(gamename) - 1] == '\n')
00144                 gamename[len] = 0;
00145         for (i=0; i<num_games; i++)
00146                 if (!strcmp (games[i]->name, gamename))
00147                 {
00148                         opt_game = games[i];
00149                         break;
00150                 }
00151         if (!opt_game)
00152         {
00153                 fprintf (stderr, "engine: unknown game: %s\n", gamename);
00154                 exit(1);
00155         }
00156         reset_game_params ();
00157 //      state_player = WHITE;   
00158         if (opt_game->game_init)
00159                 opt_game->game_init();
00160         set_game_params ();
00161         if (game_set_init_pos != game_set_init_pos_def) 
00162         {
00163                 fwrite (cur_pos.board, board_wid * board_heit, 1, engine_fout);
00164                 fflush (engine_fout);
00165         }
00166         stack_free ();
00167 }
00168 
00169 void engine_reset_game ()
00170 {
00171         stack_free ();
00172         // FIXME : there should be a separate reset_game_params() for engine
00173         cur_pos.player = WHITE;
00174         cur_pos.state = NULL;
00175         cur_pos.num_moves = 0;
00176         game_set_init_pos (&cur_pos);
00177 }
00178 
00179 void engine_back_move ()
00180 {
00181         byte *move = movstack_back ();
00182         if (game_stateful) cur_pos.state = statestack_back ();
00183         if (!move)
00184         {
00185                 move_fwrite_nak (NULL, engine_fout);
00186                 return;
00187         }
00188         move_apply (cur_pos.board, move);
00189         cur_pos.num_moves--;
00190         cur_pos.player = cur_pos.player == WHITE ? BLACK : WHITE;
00191         move_fwrite_ack (move, engine_fout);
00192 }
00193 
00194 void engine_forw_move ()
00195 {
00196         byte *move = movstack_forw ();
00197         void *state;
00198         if (game_stateful && (state = statestack_forw ()))
00199                 cur_pos.state = state;
00200         if (!move)
00201         {
00202                 move_fwrite_nak (NULL, engine_fout);
00203                 return;
00204         }
00205         move_apply (cur_pos.board, move);
00206         cur_pos.num_moves++;
00207         cur_pos.player = cur_pos.player == WHITE ? BLACK : WHITE;
00208         move_fwrite_ack (move, engine_fout);
00209 }
00210 
00211 void engine_msec_per_move (char *line)
00212 {
00213         if (!line) return;
00214         time_per_move = atoi (line);
00215         if (time_per_move < 0)
00216                 time_per_move = 3000;
00217 }
00218 
00219 void engine_who_won (char *line)
00220 {
00221         int who;
00222         char *msg = NULL;
00223         char *who_str;
00224         if (!game_who_won)
00225         {
00226                 move_fwrite_nak (NULL, engine_fout);
00227                 return;
00228         }
00229         who = game_who_won (&cur_pos, cur_pos.player, &msg);
00230         switch(who)
00231         {
00232                 case RESULT_WHITE: who_str = "WHITE"; break;
00233                 case RESULT_BLACK: who_str = "BLACK"; break;
00234                 case RESULT_TIE  : who_str = "TIE"  ; break;
00235                 case RESULT_WON  : who_str = "WON" ; break;
00236                 case RESULT_LOST : who_str = "LOST" ; break;
00237                 case RESULT_MISC : who_str = "MISC" ; break;
00238                 case RESULT_NOTYET:
00239                 default:
00240                                                 who_str = "NYET"; break; // ;^)
00241         }
00242         
00243         if (msg)
00244                 fprintf (engine_fout, "ACK %s %s\n", who_str, msg);
00245         else
00246                 fprintf (engine_fout, "ACK %s\n", who_str);
00247         fflush (engine_fout);
00248 
00249 }
00250 
00251 void engine_move_now (char *line)
00252 {
00253         engine_stop_search = TRUE;
00254 }
00255 
00256 int engine_timeout_cb ()
00257 {
00258         engine_stop_search = TRUE;
00259         return FALSE;
00260 }
00261 
00262 void engine_cancel_move (char *line)
00263 {
00264         engine_stop_search = TRUE;
00265         cancel_move = TRUE;
00266 }
00267 
00268 
00270 Command commands[] = 
00271 {
00272         { "MSEC_PER_MOVE"  , 1 , engine_msec_per_move},
00273         { "SUGGEST_MOVE"    , 0 , NULL},
00274         { "TAKE_MOVE"       , 1 , engine_take_move},
00275         { "BACK_MOVE"       , 1 , engine_back_move},
00276         { "FORW_MOVE"       , 1 , engine_forw_move},
00277         { "MAKE_MOVE"       , 1 , engine_make_move},
00278         { "MOVE_NOW"            , 1 , engine_move_now },
00279         { "CANCEL_MOVE"         , 1 , engine_cancel_move },
00280         { "END_GAME"        , 0 , NULL},
00281         { "RESET_GAME"      , 1 , engine_reset_game},
00282         { "TO_PLAY"         , 1 , engine_set_to_play},
00283         { "SET_POSITION"    , 0 , NULL},
00284         { "NEW_GAME"        , 1 , engine_new_game},
00285         { "GET_EVAL"        , 0 , NULL},
00286         { "SET_HEUR"        , 0 , NULL},
00287         { "SET_STRATEGY"    , 0 , NULL},
00288         { "WHO_WON"                     , 1 , engine_who_won},
00289 };
00290 
00291 #define NUM_COMMANDS (sizeof (commands) / sizeof (commands[0]))
00292 
00294 static void execute_command (char *line)
00295 {
00296         char *tail;
00297         int i;
00298         tail = strpbrk (line, " \t");
00299         if (tail)
00300                 *tail = 0;
00301         for (i=0; i<NUM_COMMANDS; i++)
00302                 if (!strcmp (line, commands[i].proto_str))
00303                 {
00304                         if (!commands[i].isimpl)
00305                         {
00306                                 fprintf (stderr, "warning: command %s not yet implemented\n",
00307                                                 commands[i].proto_str);
00308                                 return;
00309                         }
00310                         commands[i].impl_func (tail ? (tail+1) : 0);
00311                         return;
00312                 }
00313         fprintf (stderr, "warning: unknown command \"%s\" \n", line);
00314 }
00315 
00316 
00317 static GSList *command_list = NULL;
00318 static gboolean process_line ()
00319 {
00320         char *line;
00321         line = (char *) g_slist_nth_data (command_list, 0);
00322         if (!line) return FALSE;
00323         command_list = g_slist_remove (command_list, line);
00324         execute_command (line);
00325         g_free (line);
00326         return TRUE;
00327 }
00328 
00329 static gboolean channel_process_input ()
00330 {
00331         static char linebuf[4096];
00332         char *linep = linebuf;
00333         char *line;
00334         int bytes_read;
00335 #if GLIB_MAJOR_VERSION > 1
00336         // we need to call this again because we will get new events before returning
00337         // from this function
00338         // semantics of add_watch silently changing between glib versions!!!!
00339         g_io_add_watch (channel_in, G_IO_IN, (GIOFunc) channel_process_input, NULL);
00340 #endif
00341         g_io_channel_read (channel_in, linebuf, 4096, &bytes_read);
00342         linebuf[bytes_read] = '\0';
00343         while (*linep != '\0')
00344         {
00345                 line = linep;
00346                 while (*linep++ != '\n')
00347                         g_assert (linep[-1] != '\0');
00348                 linep[-1] = '\0';
00349                 if (opt_verbose) printf ("engine got command \"%s\"\n", line);
00350                 command_list = g_slist_append (command_list, g_strdup (line));
00351         }       
00352         while (process_line ())
00353                 ;
00354 #if GLIB_MAJOR_VERSION == 1
00355         return TRUE;
00356 #else
00357         return FALSE;
00358 #endif
00359 }
00360 
00361 void engine_poll ()
00362 {
00363         static int poll_count = 0;
00364         if (++poll_count == 100)
00365         {
00366                 poll_count = 0;
00367                 // listen for input in the pipe
00368                 while (g_main_iteration (FALSE))
00369                         ;
00370                 // execute pending commands
00371                 // we execute only ONE command so that if there is a CANCEL_MOVE followed by a MAKE_MOVE we won't start on the new move before finishing this one
00372                 process_line ();
00373         }
00374 }
00375 
00376 static void ignore () {}
00377 
00379 void engine_main (int infd, int outfd)
00380 {
00381         GMainLoop *loop;
00382         char *line;
00383         engine_flag = TRUE;
00384         signal (SIGHUP, ignore);
00385         engine_fin = fdopen (infd, "r");
00386         engine_fout = fdopen (outfd, "w");
00387         assert (engine_fin);
00388         assert (engine_fout);
00389         channel_in = g_io_channel_unix_new (infd);
00390         g_io_add_watch (channel_in, G_IO_IN, (GIOFunc) channel_process_input, NULL);
00391         g_io_add_watch (channel_in, G_IO_HUP | G_IO_NVAL, (GIOFunc) engine_hup_cb, NULL);
00392 #if GLIB_MAJOR_VERSION > 1
00393         loop = g_main_loop_new (NULL, TRUE);
00394 #else
00395         loop = g_main_new (TRUE);
00396 #endif
00397         g_main_run (loop);
00398 }
00399 
00400 byte * engine_search (Pos *pos/*, int player*/)
00401 {
00402         int tag;
00403         byte *move;
00404         engine_stop_search = FALSE;
00405         if (game_search)
00406                 game_search (pos, &move);
00407         else if (game_single_player)
00408                 move = NULL;
00409         else
00410         {
00411                 tag = g_timeout_add (2 * time_per_move, engine_timeout_cb, NULL);
00412                 move = ab_dfid (pos, pos->player);
00413                 g_source_remove (tag);
00414         }
00415         return move;
00416 }
00417