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 */ 00019 #include <time.h> 00020 #include <assert.h> 00021 #include <unistd.h> 00022 #include <string.h> 00023 #include <stdlib.h> 00024 #include <signal.h> 00025 #include <ctype.h> 00026 #include <sys/types.h> 00027 #include <sys/wait.h> 00028 00029 #include <glib.h> 00030 00031 #ifndef G_MODULE_IMPORT 00032 #include <gmodule.h> // Why isn't this included by glib.h??? 00033 #endif 00034 00035 #include <gtk/gtk.h> 00036 #include <gdk/gdk.h> 00037 00038 #include "config.h" 00039 #include "ui.h" 00040 #include "prefs.h" 00041 #include "move.h" 00042 #include "menu.h" 00043 #include "ui_common.h" 00044 #include "board.h" 00045 00047 #define DEF_TIME_PER_MOVE 2000 00048 00049 extern Game 00050 Othello, Samegame, Rgb, Fifteen, Memory, 00051 Tetris, Chess, Antichess, Hiq, Checkers, 00052 Plot4, Maze, Infiltrate, Hypermaze, Ataxx, 00053 Pentaline, Mastermind, Pacman, Flw, Wordtris, 00054 Ninemm, Stopgate, Knights, Breakthrough, 00055 CapturePento, Towers, Quarto, Kttour, Eightqueens, Dnb, 00056 Blet 00057 ; 00058 00059 // TODO: these should be sorted at runtime instead of by hand 00060 Game *games[] = { 00061 &Antichess, &Ataxx, &Blet, &Breakthrough, &Checkers, &Chess, 00062 &CapturePento, &Dnb, &Eightqueens, &Fifteen, &Flw, &Hiq, 00063 &Hypermaze, &Infiltrate, &Knights, &Kttour, &Mastermind, 00064 &Maze, &Memory, &Ninemm, &Othello, &Pacman, &Pentaline, 00065 &Plot4, &Quarto, &Rgb, &Samegame, &Stopgate, &Tetris, &Towers, 00066 &Wordtris}; 00067 00068 const int num_games = sizeof (games) / sizeof (games[0]); 00069 00070 gboolean engine_flag = FALSE; // are we client or server. server will set it to TRUE 00071 00072 /* streams to communicate with engine */ 00073 FILE *move_fin, *move_fout; 00074 00075 static GIOChannel *ui_in = NULL; 00076 00077 Pos cur_pos = {NULL, NULL, WHITE, NULL, 0, NULL}; 00078 00079 int board_wid, board_heit; 00080 00081 static int engine_pid = -1; 00082 00083 static gint animate_tag = -1; 00084 00085 //int state_player = WHITE; 00086 00087 gboolean game_allow_undo = FALSE; 00088 00089 gboolean game_single_player = FALSE; 00090 gboolean game_animation_use_movstack = TRUE; 00091 gboolean game_allow_back_forw = TRUE; 00092 int game_animation_time = 0; 00093 00094 gchar *game_doc_about = NULL; 00095 gchar *game_doc_rules = NULL; 00096 gchar *game_doc_strategy = NULL; 00097 00098 gchar *game_white_string = "White", *game_black_string = "Black"; 00099 00100 gboolean ui_gameover = FALSE; 00101 gboolean ui_stopped = TRUE; 00102 gboolean ui_cheated = FALSE; 00103 gboolean game_stateful = FALSE; 00104 gboolean state_gui_active = FALSE; 00105 gboolean game_draw_cell_boundaries = FALSE; 00106 gboolean game_start_immediately = FALSE; 00107 gboolean game_allow_flip = FALSE; 00108 gboolean game_file_label = 0, game_rank_label = 0; 00109 00110 char *game_highlight_colors = NULL; 00111 char game_highlight_colors_def[9] = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0}; 00112 00113 HeurTab *game_htab = NULL; 00114 int game_state_size = 0; 00115 00116 SCORE_FIELD * game_score_fields = prefs_score_fields_def; 00117 gchar **game_score_field_names = prefs_score_field_names_def; 00118 00119 char **game_bg_pixmap = NULL; 00120 00121 Game *opt_game = NULL; 00122 FILE *opt_infile = NULL; 00123 FILE *opt_logfile = NULL; 00124 int opt_delay = DEF_TIME_PER_MOVE; 00125 int opt_quiet = 0; 00126 int opt_white = NONE; 00127 int opt_black = NONE; 00128 int ui_white = NONE; 00129 int ui_black = NONE; 00130 int opt_verbose = 0; 00131 00132 extern void engine_main (int, int); 00133 extern ResultType engine_eval (Pos *, Player, float *); 00134 00135 gboolean impl_check (); 00136 00137 void ui_check_who_won (); 00138 void game_set_init_pos_def (Pos *); 00139 int ui_get_machine_move (); 00140 void ui_make_human_move (byte *, int *); 00141 void set_game_params (); 00142 00143 ResultType (*game_eval) (Pos *, Player, float *) = NULL; 00144 ResultType (*game_eval_incr) (Pos *, Player, byte *, float *) = NULL; 00145 gboolean (*game_use_incr_eval) (Pos *, Player) = NULL; 00146 float (*game_eval_white) (Pos *, int) = NULL; 00147 float (*game_eval_black) (Pos *, int) = NULL; 00148 void (*game_search) (Pos *, byte **) = NULL; 00149 byte * (*game_movegen) (Pos *) = NULL; 00150 InputType (*game_event_handler) (Pos *, GtkboardEvent *, MoveInfo *) = NULL; 00151 int (*game_getmove) (Pos *, int, int, GtkboardEventType, Player, byte **, int **) = NULL; 00152 int (*game_getmove_kb) (Pos *, int, Player, byte **, int **) = NULL; 00153 ResultType (*game_who_won) (Pos *, Player, char **) = NULL; 00154 int (*game_animate) (Pos *, byte **) = NULL; 00155 char **( *game_get_pixmap) (int, int) = NULL; 00156 guchar *( *game_get_rgbmap) (int, int) = NULL; 00157 void (*game_free) () = NULL; 00158 void * (*game_newstate) (Pos *, byte *) = NULL; 00159 void (*game_set_init_pos) (Pos *) = game_set_init_pos_def; 00160 void (*game_set_init_render) (Pos *) = NULL; 00161 void (*game_get_render) (Pos *, byte *, int **) = NULL; 00162 void (*game_reset_uistate) () = NULL; 00163 int (*game_scorecmp) (gchar *, int, gchar*, int) = NULL; 00164 int (*game_scorecmp_def_dscore) (gchar *, int, gchar*, int) = prefs_scorecmp_dscore; 00165 int (*game_scorecmp_def_iscore) (gchar *, int, gchar*, int) = prefs_scorecmp_iscore; 00166 int (*game_scorecmp_def_time) (gchar *, int, gchar*, int) = prefs_scorecmp_time; 00167 00168 GtkWidget *main_window, *board_area = NULL; 00169 GtkWidget *board_rowbox = NULL, *board_colbox = NULL; 00170 00171 static void ignore() {} 00172 00173 void ui_cleanup () 00174 { 00175 if (opt_game) 00176 ui_terminate_game(); 00177 signal (SIGCHLD, ignore); 00178 if (engine_pid > 0) 00179 kill (engine_pid, SIGKILL); 00180 if (opt_verbose) 00181 printf ("gtkboard: normal exit.\n"); 00182 exit (0); 00183 } 00184 00185 void ui_segv_cleanup () 00186 { 00187 signal (SIGCHLD, ignore); 00188 if (engine_pid > 0) 00189 kill (engine_pid, SIGKILL); 00190 fprintf (stderr, "gtkboard: caught segv, exiting.\n"); 00191 exit (1); 00192 } 00193 00194 00195 void ui_child_cleanup () 00196 { 00197 int status; 00198 waitpid (engine_pid, &status, WNOHANG | WUNTRACED); 00199 if (WIFSTOPPED (status)) 00200 return; 00201 fprintf (stderr, "gtkboard: engine appears to have died, exiting.\n"); 00202 exit (1); 00203 } 00204 00205 00206 int ui_animate_cb () 00207 { 00208 int ret; 00209 byte *move; 00210 if (ui_stopped) return TRUE; 00211 if (!game_animate) return FALSE; 00212 if (game_animate (&cur_pos, &move) > 0) 00213 { 00214 if (game_animation_use_movstack) 00215 ui_make_human_move (move, NULL); 00216 else 00217 board_apply_refresh (move, NULL); 00218 } 00219 return TRUE; 00220 } 00221 00222 // Will be called on both ui and engine 00223 void game_set_init_pos_def (Pos *pos) 00224 { 00225 int x, y; 00226 00227 for (x=0; x<board_wid; x++) 00228 for (y=0; y<board_heit; y++) 00229 pos->board[y * board_wid + x] = 00230 opt_game->init_pos ? 00231 opt_game->init_pos [(board_heit -1 - y) * board_wid + x] : 0; 00232 } 00233 00234 00235 // Will be called on both ui and engine 00236 void reset_game_params () 00237 { 00238 if (game_free) game_free (); 00239 game_htab = NULL; 00240 game_eval = NULL; 00241 game_eval_incr = NULL; 00242 game_use_incr_eval = NULL; 00243 game_eval_white = NULL; 00244 game_eval_black = NULL; 00245 game_search = NULL; 00246 game_movegen = NULL; 00247 game_event_handler = NULL; 00248 game_getmove = NULL; 00249 game_getmove_kb = NULL; 00250 game_who_won = NULL; 00251 game_get_pixmap = NULL; 00252 game_get_rgbmap = NULL; 00253 game_set_init_pos = game_set_init_pos_def; 00254 game_set_init_render = NULL; 00255 game_get_render = NULL; 00256 game_animate = NULL; 00257 game_free = NULL; 00258 game_scorecmp = NULL; 00259 game_stateful = FALSE; 00260 game_animation_use_movstack = TRUE; 00261 game_allow_back_forw = TRUE; 00262 game_single_player = FALSE; 00263 game_allow_undo = FALSE; 00264 game_doc_about = NULL; 00265 game_doc_rules = NULL; 00266 game_doc_strategy = NULL; 00267 game_white_string = "White"; 00268 game_black_string = "Black"; 00269 //state_player = WHITE; 00270 // TODO: replace state_player by cur_pos.player globally 00271 cur_pos.player = WHITE; 00272 game_state_size = 0; 00273 game_newstate = NULL; 00274 game_reset_uistate = NULL; 00275 game_highlight_colors = game_highlight_colors_def; 00276 game_draw_cell_boundaries = FALSE; 00277 game_start_immediately = FALSE; 00278 game_allow_flip = FALSE; 00279 game_file_label = FILERANK_LABEL_TYPE_NONE; 00280 game_rank_label = FILERANK_LABEL_TYPE_NONE; 00281 game_score_fields = prefs_score_fields_def; 00282 game_score_field_names = prefs_score_field_names_def; 00283 game_bg_pixmap = NULL; 00284 if (cur_pos.board) free (cur_pos.board); 00285 if (cur_pos.render) free (cur_pos.render); 00286 cur_pos.board = NULL; 00287 cur_pos.render = NULL; 00288 cur_pos.state = NULL; 00289 cur_pos.ui_state = NULL; 00290 cur_pos.num_moves = 0; 00291 } 00292 00293 void ui_terminate_game () 00294 { 00295 // FIXME: are we sure -1 is an invalid value? 00296 if (animate_tag >= 0) 00297 { 00298 gtk_timeout_remove (animate_tag); 00299 animate_tag = -1; 00300 } 00301 if (game_single_player) 00302 prefs_save_scores (opt_game->name); 00303 board_free (); 00304 reset_game_params (); 00305 if (opt_infile) 00306 { 00307 fclose (opt_infile); 00308 opt_infile = NULL; 00309 } 00310 if (game_reset_uistate) game_reset_uistate(); 00311 sb_reset_human_time (); 00312 sb_update(); 00313 ui_stopped = TRUE; 00314 ui_cheated = FALSE; 00315 } 00316 00317 void ui_start_game () 00318 { 00319 if (opt_game->game_init) 00320 opt_game->game_init(); 00321 if (game_single_player) 00322 { 00323 ui_white = HUMAN; 00324 ui_black = HUMAN; 00325 } 00326 else if (opt_infile) 00327 { 00328 ui_white = NONE; 00329 ui_black = NONE; 00330 } 00331 else 00332 { 00333 ui_white = HUMAN; 00334 ui_black = MACHINE; 00335 } 00336 set_game_params (); 00337 board_set_game_params (); // FIXME: this is ugly 00338 if (game_animate) // FIXME: shouldn't this be done somewhere else? 00339 animate_tag = gtk_timeout_add (game_animation_time, ui_animate_cb, NULL); 00340 board_init (); 00341 board_redraw_all (); 00342 menu_put_player (FALSE); 00343 menu_set_eval_function (); 00344 if (game_single_player) 00345 prefs_load_scores (opt_game->name); 00346 ui_check_who_won(); 00347 if (game_single_player && game_start_immediately) 00348 ui_stopped = FALSE; 00349 } 00350 00351 00353 // FIXME: fork this into 2 functions for client and server 00354 void set_game_params () 00355 { 00356 Game *game = opt_game; 00357 if (!game) return; 00358 board_wid = game->board_wid; 00359 board_heit = game->board_heit; 00360 00361 cur_pos.board = (byte *) malloc (board_wid * board_heit); 00362 assert (cur_pos.board); 00363 00364 if (!engine_flag) 00365 { 00366 cur_pos.render = (int *) malloc (sizeof (int) * board_wid * board_heit); 00367 memset (cur_pos.render, 0, sizeof (int) * board_wid * board_heit); 00368 assert (cur_pos.render); 00369 } 00370 00371 if (engine_flag) 00372 // server always executes this 00373 game_set_init_pos (&cur_pos); 00374 else 00375 { 00376 if (game_set_init_pos == game_set_init_pos_def) 00377 // client executes only if it is the default 00378 game_set_init_pos (&cur_pos); 00379 00380 if (game_set_init_render) 00381 game_set_init_render (&cur_pos); 00382 } 00383 00384 if (!engine_flag) 00385 if (move_fout) 00386 { 00387 fprintf (move_fout, "NEW_GAME %s\n", game->name); 00388 fflush (move_fout); 00389 // read the initial position 00390 if (game_set_init_pos != game_set_init_pos_def) 00391 fread (cur_pos.board, board_wid * board_heit, 1, move_fin); 00392 fprintf (move_fout, "MSEC_PER_MOVE %d\n", opt_delay); 00393 fflush (move_fout); 00394 } 00395 } 00396 00397 void ui_check_who_won() 00398 { 00399 char *line, *who_str = NULL; 00400 int who, len; 00401 if (!move_fout) 00402 return; 00403 fprintf (move_fout, "WHO_WON \n"); 00404 fflush (move_fout); 00405 line = line_read(move_fin); 00406 if (g_strncasecmp(line, "ACK", 3)) 00407 { 00408 // NAK ==> not implemented 00409 ui_gameover = FALSE; 00410 sb_set_score (""); 00411 return; 00412 } 00413 line += 4; 00414 line = g_strstrip(line); 00415 who_str = line; 00416 while(!isspace(*line) && *line) line++; 00417 while(isspace(*line)) line++; 00418 sb_set_score (line); 00419 if (!g_strncasecmp(who_str, "NYET", 4)) 00420 { 00421 ui_gameover = FALSE; 00422 return; 00423 } 00424 ui_stopped = TRUE; 00425 ui_gameover = TRUE; 00426 if (opt_logfile) 00427 fprintf(opt_logfile, "RESULT: %s\n", who_str); 00428 if (!state_gui_active) 00429 ui_cleanup(); 00430 sb_update (); 00431 if (game_single_player && !ui_cheated && !g_strncasecmp(who_str, "WON", 3)) 00432 prefs_add_highscore (line, sb_get_human_time ()); 00433 } 00434 00435 void ui_send_make_move () 00436 { 00437 if (ui_stopped) 00438 return; 00439 if (player_to_play == HUMAN) 00440 return; 00441 if (move_fout && player_to_play == MACHINE) 00442 { 00443 fprintf (move_fout, "MAKE_MOVE \n"); 00444 fflush (move_fout); 00445 } 00446 00447 if (opt_infile) 00448 g_timeout_add (opt_delay, ui_get_machine_move, NULL); 00449 00450 else 00451 g_io_add_watch (ui_in, G_IO_IN, (GIOFunc) ui_get_machine_move, NULL); 00452 } 00453 00454 void ui_make_human_move (byte *move, int *rmove) 00455 { 00456 board_apply_refresh (move, rmove); 00457 if (!move) return; 00458 if (move_fout) 00459 { 00460 fprintf (move_fout, "TAKE_MOVE "); 00461 move_fwrite (move, move_fout); 00462 if (opt_logfile) 00463 move_fwrite (move, opt_logfile); 00464 } 00465 if (!game_single_player) 00466 { 00467 cur_pos.player = (cur_pos.player == WHITE ? BLACK : WHITE); 00468 } 00469 cur_pos.num_moves ++; 00470 ui_check_who_won (); 00471 sb_update (); 00472 ui_send_make_move (); 00473 } 00474 00475 int ui_get_machine_move () 00476 { 00477 byte *move; 00478 if (player_to_play == HUMAN || ui_stopped) 00479 return FALSE; 00480 if (!opt_infile) 00481 { 00482 move = move_fread_ack (move_fin); 00483 if (!move) 00484 { 00485 sb_error ("Couldn't make move\n", TRUE); 00486 ui_stopped = TRUE; 00487 sb_update (); 00488 return FALSE; 00489 } 00490 if (opt_logfile) 00491 move_fwrite (move, opt_logfile); 00492 } 00493 else // file mode 00494 { 00495 //TODO: should communicate the move to the engine 00496 move = move_fread (opt_infile); 00497 if (opt_logfile) 00498 move_fwrite (move, opt_logfile); 00499 } 00500 board_apply_refresh (move, NULL); 00501 if (!game_single_player) 00502 cur_pos.player = (cur_pos.player == WHITE ? BLACK : WHITE); 00503 cur_pos.num_moves ++; 00504 ui_check_who_won (); 00505 sb_update (); 00506 ui_send_make_move (); 00507 return FALSE; 00508 } 00509 00510 int ui_move_now_cb () 00511 { 00512 if (player_to_play == HUMAN || ui_stopped) 00513 return FALSE; 00514 g_assert (engine_pid >= 0); 00515 fprintf (move_fout, "MOVE_NOW \n"); 00516 fflush (move_fout); 00517 ui_get_machine_move (); 00518 } 00519 00520 void ui_cancel_move () 00521 { 00522 if (player_to_play != MACHINE) return; 00523 if (!opt_infile) 00524 { 00525 g_assert (engine_pid >= 0); 00526 fprintf (move_fout, "CANCEL_MOVE \n"); 00527 fflush (move_fout); 00528 } 00529 } 00530 00531 void ui_start_player () 00532 { 00533 int fd[2][2], ret, i; 00534 00535 for (i=0; i<2; i++) 00536 { 00537 ret = pipe (fd[i]); 00538 assert (!ret); 00539 } 00540 00541 /* fork a child to do the crunching */ 00542 if (!(engine_pid = fork ())) 00543 { 00544 engine_main (fd[0][0], fd[1][1]); 00545 exit (0); 00546 } 00547 if (opt_verbose) printf ("forked engine pid = %d\n", engine_pid); 00548 move_fin = fdopen (fd[1][0], "r"); 00549 move_fout = fdopen (fd[0][1], "w"); 00550 ui_in = g_io_channel_unix_new (fd[1][0]); 00551 } 00552 00553 gboolean impl_check () 00554 { 00555 /* check if reqd functions have been implemented */ 00556 if (!opt_infile) 00557 { 00558 if (!game_single_player) 00559 if ((ui_white == MACHINE || ui_black == MACHINE) 00560 && (!game_movegen || !game_eval) && !game_search) 00561 return FALSE; 00562 if ((ui_white == HUMAN || ui_black == HUMAN) 00563 && !game_getmove && !game_getmove_kb && !game_event_handler) 00564 return FALSE; 00565 } 00566 return TRUE; 00567 } 00568 00569 00570 static void parse_opts (int argc, char **argv) 00571 { 00572 char *wheur = NULL, *bheur = NULL; 00573 int c, i; 00574 while ((c = getopt (argc, argv, "g:G:d:f:l:p:w:b:qvh")) != -1) 00575 { 00576 switch (c) 00577 { 00578 case 'g': 00579 { 00580 int found = 0; 00581 for (i=0; i<num_games; i++) 00582 if (!strcasecmp (optarg, games[i]->name)) 00583 { 00584 opt_game = games[i]; 00585 if (opt_game->game_init) 00586 opt_game->game_init(); 00587 found = 1; 00588 } 00589 if (!found) 00590 { 00591 fprintf (stderr, "%s: no such game\n", optarg); 00592 exit(1); 00593 } 00594 } 00595 break; 00596 00597 case 'G': 00598 { 00599 void *handle; 00600 char *error; 00601 Game **game; 00602 GModule *module; 00603 module = g_module_open (optarg, G_MODULE_BIND_LAZY); 00604 if (!module) 00605 { 00606 fprintf (stderr, 00607 "Failed to load plugin from file \"%s\": %s\nTry specifying an absolute file name\n", 00608 optarg, g_module_error ()); 00609 exit (1); 00610 } 00611 00612 if (!g_module_symbol (module, "plugin_game", (gpointer *) &game)) 00613 //if ((error = dlerror()) != NULL) 00614 { 00615 fprintf (stderr, 00616 "Failed to load plugin from file \"%s\": %s\n", 00617 optarg, g_module_error ()); 00618 exit(1); 00619 } 00620 //dlclose(handle); 00621 printf ("Successfully loaded game %s\n", (*game)->name); 00622 opt_game = *game; 00623 if (opt_game->game_init) 00624 opt_game->game_init(); 00625 } 00626 break; 00627 /* FIXME : make these long options */ 00628 case 'w': 00629 wheur = optarg; 00630 break; 00631 case 'b': 00632 bheur = optarg; 00633 break; 00634 00635 case 'p': 00636 switch (optarg[0]) 00637 { 00638 case 'h': 00639 opt_white = HUMAN; 00640 break; 00641 case 'm': 00642 opt_white = MACHINE; 00643 break; 00644 default: 00645 printf ("player must be h(human) or m(machine)\n"); 00646 exit (1); 00647 } 00648 switch (optarg[1]) 00649 { 00650 case 'h': 00651 opt_black = HUMAN; 00652 break; 00653 case 'm': 00654 opt_black = MACHINE; 00655 break; 00656 default: 00657 printf ("player must be h(human) or m(machine)\n"); 00658 exit (1); 00659 } 00660 break; 00661 case 'f': 00662 if (opt_white != NONE || opt_black != NONE) 00663 { 00664 printf ("can't specify -f with -p\n"); 00665 exit (1); 00666 } 00667 opt_infile = fopen (optarg, "r"); 00668 if (!opt_infile) 00669 { 00670 fprintf (stderr, "can't open %s for reading\n", optarg); 00671 exit (1); 00672 } 00673 break; 00674 case 'l': 00675 opt_logfile = fopen (optarg, "a"); 00676 if (!opt_logfile) 00677 { 00678 fprintf (stderr, "can't open %s for writing\n", optarg); 00679 exit (1); 00680 } 00681 break; 00682 case 'd': 00683 opt_delay = atoi (optarg); 00684 if (opt_delay <= 0) 00685 opt_delay = 3000; 00686 break; 00687 case 'q': 00688 opt_quiet = 1; 00689 break; 00690 case 'v': 00691 opt_verbose = 1; 00692 break; 00693 case 'h': 00694 printf ("Usage: gtkboard \t[-qvh] " 00695 "[-g game] [-G file] [-f file] [-l logfile] [-d msec]\n" 00696 "\t\t\t[-p XX] [-w wheur -b bheur] " 00697 "\n" 00698 "\t-g\tname of the game\n" 00699 "\t-G\tplugin file to load game from\n" 00700 "\t-f\tfile to load game from\n" 00701 "\t-l\tlog file to record game\n" 00702 "\t-q\tdon't show board\n" 00703 "\t-d\tdelay in milliseconds\n" 00704 "\t-p\thuman or machine players. Each X must be 'h' or 'm'\n" 00705 "\t-w\tname of heuristic function for white\n" 00706 "\t-b\tname of heuristic function for black\n" 00707 "\t-v\tbe verbose\n" 00708 "\t-h\tprint this help\n" 00709 ); 00710 exit (0); 00711 default: 00712 exit (1); 00713 } 00714 00715 } 00716 00717 /* check sanity */ 00718 if ((wheur && !bheur) || (bheur && !wheur)) 00719 { 00720 fprintf (stderr, "specify heuristic for both players or neither\n"); 00721 exit(1); 00722 } 00723 if (opt_infile && (opt_white != NONE || opt_black != NONE)) 00724 { 00725 fprintf (stderr, "can't specify -f with -p\n"); 00726 exit (1); 00727 } 00728 if (opt_quiet && (opt_white == HUMAN || opt_black == HUMAN)) 00729 { 00730 fprintf (stderr, "can't be quiet with human players\n"); 00731 exit (1); 00732 } 00733 if (game_single_player && (opt_infile)) 00734 { 00735 fprintf (stderr, "can't load from file for single player game\n"); 00736 exit (1); 00737 } 00738 if (opt_quiet && (opt_white != MACHINE || opt_black != MACHINE)) 00739 { 00740 fprintf (stderr, "both white and black have to be machine for quiet mode.\n"); 00741 exit (1); 00742 } 00743 if (opt_quiet && !opt_game) 00744 { 00745 fprintf (stderr, "game must be specified for quiet mode.\n"); 00746 exit (1); 00747 } 00748 if (opt_quiet && !opt_logfile) 00749 { 00750 fprintf (stderr, "warning: no logfile specified in quiet mode.\n"); 00751 } 00752 00753 if (wheur && bheur) 00754 { 00755 int i = 0; 00756 if (!opt_game) 00757 { 00758 fprintf (stderr, "heur fn specified but no game specified\n"); 00759 exit (1); 00760 } 00761 if (!game_htab) 00762 { 00763 fprintf (stderr, "no support for changing eval fn. in %s\n", opt_game->name); 00764 exit(1); 00765 } 00766 for (i=0; game_htab[i].name; i++) 00767 { 00768 if (!strcasecmp(wheur, game_htab[i].name)) 00769 game_eval_white = game_htab[i].eval_fun; 00770 if (!strcasecmp(bheur, game_htab[i].name)) 00771 game_eval_black = game_htab[i].eval_fun; 00772 } 00773 if (!game_eval_white) 00774 { 00775 fprintf (stderr, "%s: no such eval fn. in %s\n", wheur, opt_game->name); 00776 exit(1); 00777 } 00778 if (!game_eval_black) 00779 { 00780 fprintf (stderr, "%s: no such eval fn. in %s\n", bheur, opt_game->name); 00781 exit(1); 00782 } 00783 // FIXME: engine should parse opts separately 00784 game_eval = engine_eval; 00785 } 00786 00787 /* if (game_single_player) 00788 { 00789 opt_white = HUMAN; 00790 opt_black = HUMAN; 00791 } 00792 */ 00793 else if (!opt_infile) 00794 { 00795 // default is human vs. machine 00796 if (opt_white == NONE) opt_white = HUMAN; 00797 if (opt_black == NONE) opt_black = MACHINE; 00798 } 00799 ui_white = opt_white; 00800 ui_black = opt_black; 00801 } 00802 00803 void gui_init () 00804 { 00805 GtkWidget *hbox = NULL, *vbox = NULL, *vbox1 = NULL, *frame = NULL; 00806 GtkWidget *separator; 00807 GtkAccelGroup *ag; 00808 GtkItemFactoryEntry game_items [num_games+1]; 00809 GtkItemFactoryEntry items[] = 00810 { 00811 #if GTK_MAJOR_VERSION == 1 00812 { "/_File", NULL, NULL, 0, "<Branch>" }, 00813 { "/File/_Load game", "<control>L", menu_load_file_dialog, 0, "" }, 00814 { "/File/_Save game", NULL, NULL, 0, "" }, 00815 { "/File/_Quit", "<control>Q", (GtkSignalFunc) ui_cleanup, 0, "" }, 00816 { "/_Game", NULL, NULL, 0, "<Branch>" }, 00817 { "/Game/Select _Game", NULL, NULL, 0, "<LastBranch>" }, 00818 { "/Game/Sep1", NULL, NULL, 0, "<Separator>" }, 00819 { "/Game/_New", "<control>N", menu_start_stop_game, MENU_RESET_GAME, "" }, 00820 { "/Game/_Start", "<control>G", menu_start_stop_game, MENU_START_GAME, "" }, 00821 { "/Game/_Pause", "<control>P", menu_start_stop_game, MENU_STOP_GAME, "" }, 00822 { "/Game/Sep2", NULL, NULL, 0, "<Separator>" }, 00823 { "/Game/_Highscores", NULL, prefs_show_scores, 0, ""}, 00824 { "/Game/_Zap Highscores", NULL, prefs_zap_highscores, 0, ""}, 00825 { "/_Move", NULL, NULL, 0, "<Branch>" }, 00826 { "/Move/_Back", "<control>B", menu_back_forw, MENU_BACK, "" }, 00827 { "/Move/_Forward", "<control>F", menu_back_forw, MENU_FORW, "" }, 00828 { "/Move/Sep1", NULL, NULL, 0, "<Separator>" }, 00829 { "/Move/_Move Now", "<control>M", 00830 (GtkItemFactoryCallback) ui_move_now_cb, 0, "" }, 00831 #else 00832 { "/_File", NULL, NULL, 0, "<Branch>" }, 00833 { "/File/_Load game", "<control>L", menu_load_file_dialog, 0, 00834 "<StockItem>", GTK_STOCK_OPEN }, 00835 { "/File/_Save game", NULL, menu_save_file_dialog, 0, 00836 "<StockItem>", GTK_STOCK_SAVE }, 00837 { "/File/_Quit", "<control>Q", (GtkSignalFunc) ui_cleanup, 0, 00838 "<StockItem>", GTK_STOCK_QUIT }, 00839 { "/_Game", NULL, NULL, 0, "<Branch>" }, 00840 { "/Game/Select _Game", NULL, NULL, 0, "<LastBranch>" }, 00841 { "/Game/Sep1", NULL, NULL, 0, "<Separator>" }, 00842 { "/Game/_New", "<control>N", menu_start_stop_game, MENU_RESET_GAME, 00843 "<StockItem>", GTK_STOCK_NEW }, 00844 { "/Game/_Start", "<control>G", menu_start_stop_game, MENU_START_GAME, 00845 "<StockItem>", GTK_STOCK_YES }, 00846 { "/Game/_Pause", "<control>P", menu_start_stop_game, MENU_STOP_GAME, 00847 "<StockItem>", GTK_STOCK_STOP }, 00848 { "/Game/Sep2", NULL, NULL, 0, "<Separator>" }, 00849 //FIXME: there's a scores stock item but I can't seem to find it 00850 { "/Game/_Highscores", NULL, prefs_show_scores, 0, ""}, 00851 { "/Game/_Zap Highscores", NULL, prefs_zap_highscores, 0, ""}, 00852 { "/_Move", NULL, NULL, 0, "<Branch>" }, 00853 { "/Move/_Back", "<control>B", menu_back_forw, 1, 00854 "<StockItem>", GTK_STOCK_GO_BACK }, 00855 { "/Move/_Forward", "<control>F", menu_back_forw, 2, 00856 "<StockItem>", GTK_STOCK_GO_FORWARD }, 00857 { "/Move/Sep1", NULL, NULL, 0, "<Separator>" }, 00858 { "/Move/_Move Now", "<control>M", 00859 (GtkItemFactoryCallback) ui_move_now_cb, 0, "" }, 00860 #endif 00861 { "/_Settings", NULL, NULL, 0, "<Branch>" }, 00862 { "/Settings/_Player", NULL, NULL, 0, "<Branch>" }, 00863 { "/Settings/Player/File", NULL, NULL, 0, "<RadioItem>" }, 00864 { "/Settings/Player/Human-Human", NULL, menu_set_player, 1, "/Settings/Player/File" }, 00865 { "/Settings/Player/Human-Machine", NULL, menu_set_player, 2, 00866 "/Settings/Player/File" }, 00867 { "/Settings/Player/Machine-Human", NULL, menu_set_player, 3, 00868 "/Settings/Player/File" }, 00869 { "/Settings/Player/Machine-Machine", NULL, menu_set_player, 4, 00870 "/Settings/Player/File" }, 00871 // { "/Settings/_Eval function", NULL, NULL, 0, "<Branch>" }, 00872 // { "/Settings/_Eval function/_White", NULL, NULL, 0, "<Branch>" }, 00873 // { "/Settings/_Eval function/_Black", NULL, NULL, 0, "<Branch>" }, 00874 { "/Settings/_Flip Board", "<control>T", menu_board_flip_cb, 0, "" }, 00875 { "/Settings/_Time per move", NULL, NULL, 0, "<Branch>" }, 00876 { "/Settings/_Time per move/Default", NULL, 00877 menu_set_delay_cb, DEF_TIME_PER_MOVE, "<RadioItem>" }, 00878 { "/Settings/_Time per move/100 milliseconds", NULL, 00879 menu_set_delay_cb, 100, "/Settings/Time per move/Default" }, 00880 { "/Settings/Time per move/200 milliseconds", NULL, 00881 menu_set_delay_cb, 200, "/Settings/Time per move/Default" }, 00882 { "/Settings/Time per move/500 milliseconds", NULL, 00883 menu_set_delay_cb, 500, "/Settings/Time per move/Default" }, 00884 { "/Settings/Time per move/1 second", NULL, 00885 menu_set_delay_cb, 1000, "/Settings/Time per move/Default" }, 00886 { "/Settings/Time per move/2 seconds", NULL, 00887 menu_set_delay_cb, 2000, "/Settings/Time per move/Default" }, 00888 { "/Settings/Time per move/5 seconds", NULL, 00889 menu_set_delay_cb, 5000, "/Settings/Time per move/Default" }, 00890 { "/Settings/Time per move/10 seconds", NULL, 00891 menu_set_delay_cb, 10000, "/Settings/Time per move/Default" }, 00892 { "/Settings/Time per move/30 seconds", NULL, 00893 menu_set_delay_cb, 30000, "/Settings/Time per move/Default" }, 00894 { "/Settings/Time per move/1 minute", NULL, 00895 menu_set_delay_cb, 600000, "/Settings/Time per move/Default" }, 00896 { "/_Help", NULL, NULL, 0, "<LastBranch>" }, 00897 { "/Help/_About", NULL, menu_show_about_dialog, 0, ""}, 00898 // { "/Help/_Begging", NULL, menu_show_begging_dialog, 0, ""}, 00899 // TODO: implement context help 00900 // { "/Help/_Context help", NULL, ui_set_context_help, 0, ""}, 00901 }; 00902 int i; 00903 gdk_rgb_init (); 00904 main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL); 00905 gtk_window_set_policy (GTK_WINDOW (main_window), FALSE, FALSE, TRUE); 00906 gtk_signal_connect (GTK_OBJECT (main_window), "delete_event", 00907 GTK_SIGNAL_FUNC(ui_cleanup), NULL); 00908 gtk_window_set_title (GTK_WINDOW (main_window), "GTKBoard"); 00909 00910 ag = gtk_accel_group_new(); 00911 menu_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", ag); 00912 gtk_window_add_accel_group (GTK_WINDOW (main_window), ag); 00913 00914 gtk_item_factory_create_items (menu_factory, 00915 sizeof (items) / sizeof (items[0]), items, NULL); 00916 for (i=0; i<=num_games; i++) 00917 { 00918 game_items[i].path = g_strdup_printf ("/Game/Select Game/%s", 00919 i ? games[i-1]->name : "none"); 00920 game_items[i].accelerator = NULL; 00921 game_items[i].callback = menu_set_game; 00922 game_items[i].callback_action = i-1; 00923 game_items[i].item_type = (i == 0 ? "<RadioItem>": "/Game/Select Game/none"); 00924 } 00925 gtk_item_factory_create_items (menu_factory, 00926 num_games+1, game_items, NULL); 00927 // ugly hack to create a group of radio button with no button selected by default 00928 gtk_item_factory_delete_item (menu_factory, "/Game/Select Game/none"); 00929 00930 menu_main = gtk_item_factory_get_widget (menu_factory, "<main>"); 00931 gtk_widget_set_state (gtk_item_factory_get_widget (menu_factory, 00932 "/Settings/Player/File"), GTK_STATE_INSENSITIVE); 00933 menu_set_eval_function (); 00934 vbox = gtk_vbox_new (FALSE, 0); 00935 gtk_box_pack_start (GTK_BOX(vbox), menu_main, FALSE, FALSE, 0); 00936 00937 frame = gtk_frame_new (NULL); 00938 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); 00939 00940 { 00941 GtkWidget *innerframe; 00942 board_colbox = gtk_vbox_new (FALSE, 0); 00943 board_area = gtk_drawing_area_new (); 00944 hbox = gtk_hbox_new (FALSE, 0); 00945 gtk_box_pack_start (GTK_BOX (hbox), board_colbox, FALSE, FALSE, 0); 00946 vbox1 = gtk_vbox_new (FALSE, 0); 00947 board_rowbox = gtk_hbox_new (FALSE, 0); 00948 innerframe = gtk_frame_new (NULL); 00949 gtk_frame_set_shadow_type (GTK_FRAME (innerframe), GTK_SHADOW_IN); 00950 gtk_container_add (GTK_CONTAINER (vbox1), innerframe); 00951 gtk_container_add (GTK_CONTAINER (innerframe), board_area); 00952 gtk_container_add (GTK_CONTAINER (vbox1), board_rowbox); 00953 gtk_box_pack_start (GTK_BOX (hbox), vbox1, TRUE, FALSE, 0); 00954 gtk_container_add (GTK_CONTAINER (frame), hbox); 00955 00956 gtk_signal_connect (GTK_OBJECT (board_area), "expose_event", 00957 GTK_SIGNAL_FUNC (board_redraw), NULL); 00958 00959 gtk_widget_set_events(board_area, 00960 gtk_widget_get_events (board_area) 00961 | GDK_BUTTON_PRESS_MASK 00962 | GDK_BUTTON_RELEASE_MASK 00963 | GDK_POINTER_MOTION_MASK 00964 | GDK_KEY_PRESS_MASK 00965 | GDK_KEY_RELEASE_MASK 00966 | GDK_LEAVE_NOTIFY_MASK 00967 ); 00968 00969 gtk_signal_connect (GTK_OBJECT (board_area), "leave_notify_event", 00970 GTK_SIGNAL_FUNC (board_signal_handler), NULL); 00971 gtk_signal_connect (GTK_OBJECT (board_area), "motion_notify_event", 00972 GTK_SIGNAL_FUNC (board_signal_handler), NULL); 00973 gtk_signal_connect (GTK_OBJECT (board_area), "button_release_event", 00974 GTK_SIGNAL_FUNC (board_signal_handler), NULL); 00975 gtk_signal_connect (GTK_OBJECT (board_area), "button_press_event", 00976 GTK_SIGNAL_FUNC (board_signal_handler), NULL); 00977 gtk_signal_connect (GTK_OBJECT (main_window), "key_press_event", 00978 GTK_SIGNAL_FUNC (board_signal_handler), NULL); 00979 gtk_signal_connect (GTK_OBJECT (main_window), "key_release_event", 00980 GTK_SIGNAL_FUNC (board_signal_handler), NULL); 00981 hbox = gtk_hbox_new (FALSE, 0); 00982 sb_game_label = gtk_label_new (opt_game ? opt_game->name : NULL); 00983 gtk_box_pack_start (GTK_BOX (hbox), sb_game_label, FALSE, FALSE, 3); 00984 sb_game_separator = gtk_vseparator_new (); 00985 gtk_box_pack_start (GTK_BOX (hbox), sb_game_separator, FALSE, FALSE, 0); 00986 sb_player_label = gtk_label_new (NULL); 00987 gtk_box_pack_start (GTK_BOX (hbox), sb_player_label, FALSE, FALSE, 3); 00988 sb_player_separator = gtk_vseparator_new (); 00989 gtk_box_pack_start (GTK_BOX (hbox), sb_player_separator, FALSE, FALSE, 0); 00990 sb_who_label = gtk_label_new (NULL); 00991 gtk_box_pack_start (GTK_BOX (hbox), sb_who_label, FALSE, FALSE, 3); 00992 sb_who_separator = gtk_vseparator_new (); 00993 gtk_box_pack_start (GTK_BOX (hbox), sb_who_separator, FALSE, FALSE, 0); 00994 sb_score_label = gtk_label_new (NULL); 00995 gtk_box_pack_start (GTK_BOX (hbox), sb_score_label, FALSE, FALSE, 3); 00996 sb_score_separator = gtk_vseparator_new (); 00997 gtk_box_pack_start (GTK_BOX (hbox), sb_score_separator, FALSE, FALSE, 0); 00998 #if GTK_MAJOR_VERSION == 2 00999 sb_turn_image = gtk_image_new_from_stock (GTK_STOCK_YES, GTK_ICON_SIZE_MENU); 01000 gtk_box_pack_end (GTK_BOX (hbox), sb_turn_image, FALSE, FALSE, 0); 01001 sb_turn_separator = gtk_vseparator_new (); 01002 gtk_box_pack_end (GTK_BOX (hbox), sb_turn_separator, FALSE, FALSE, 0); 01003 #endif 01004 sb_time_label = gtk_label_new (NULL); 01005 gtk_box_pack_end (GTK_BOX (hbox), sb_time_label, FALSE, FALSE, 0); 01006 sb_time_separator = gtk_vseparator_new (); 01007 gtk_box_pack_end (GTK_BOX (hbox), sb_time_separator, FALSE, FALSE, 0); 01008 } 01009 01010 separator = gtk_hseparator_new (); 01011 gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0); 01012 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); 01013 separator = gtk_hseparator_new (); 01014 gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0); 01015 gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); 01016 separator = gtk_hseparator_new (); 01017 gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0); 01018 sb_message_label = gtk_label_new (NULL); 01019 gtk_misc_set_alignment (GTK_MISC (sb_message_label), 0, 0.5); 01020 hbox = gtk_hbox_new (TRUE, 0); 01021 gtk_box_pack_start (GTK_BOX (hbox), sb_message_label, TRUE, TRUE, 3); 01022 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); 01023 gtk_container_add (GTK_CONTAINER (main_window), vbox); 01024 // FIXME: board_init() needs show() to be called to get a gc, but 01025 // leads to the whole window not popping up at once 01026 gtk_widget_show_all (main_window); 01027 01028 if (!opt_game) board_init (); 01029 01030 gtk_timeout_add (100, sb_update_periodic, NULL); 01031 01032 // this should be called before setting state_gui_active = TRUE 01033 if (opt_game) menu_put_game (); 01034 state_gui_active = TRUE; 01035 01036 if (opt_game) menu_start_game (); 01037 menu_put_player (TRUE); 01038 if (!opt_game) sb_message ("Select a game from the Game menu", FALSE); 01039 sb_update (); 01040 } 01041 01042 int main (int argc, char **argv) 01043 { 01044 srandom (time(0)); 01045 reset_game_params (); 01046 parse_opts (argc, argv); 01047 ui_start_player (); 01048 01049 signal (SIGINT, ui_cleanup); 01050 signal (SIGTERM, ui_cleanup); 01051 signal (SIGSEGV, ui_segv_cleanup); 01052 signal (SIGCHLD, ui_child_cleanup); 01053 if (!opt_quiet) 01054 { 01055 gtk_init(&argc,&argv); 01056 gdk_rgb_init(); 01057 gui_init (); 01058 gtk_main (); 01059 } 01060 else // background mode 01061 { 01062 GMainLoop *loop; 01063 signal (SIGHUP, ignore); 01064 set_game_params (); 01065 ui_stopped = FALSE; 01066 ui_send_make_move (); 01067 #if GLIB_MAJOR_VERSION > 1 01068 loop = g_main_loop_new (NULL, TRUE); 01069 #else 01070 loop = g_main_new (TRUE); 01071 #endif 01072 g_main_run (loop); 01073 01074 } 01075 return 0; 01076 }