This is the doxygen documentation for gtkboard.
.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