This is the doxygen documentation for gtkboard.

.
Main Page   Data Structures   File List   Data Fields   Globals  

ui.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 */
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 }