This is the doxygen documentation for gtkboard.

.
Main Page   Data Structures   File List   Data Fields   Globals  

ab.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 
00020 #include "game.h"
00021 #include "move.h"
00022 #include "engine.h"
00023 
00024 #include <signal.h>
00025 #include <string.h>
00026 #include <stdlib.h>
00027 #include <assert.h>
00028 #include <math.h>
00029 
00030 extern int time_per_move;
00031 
00032 extern gboolean engine_stop_search;
00033 
00034 static gboolean ab_tree_exhausted = FALSE;
00035 
00036 static int ab_leaf_cnt;  // how many leaves were eval'd
00037 
00038 extern int hash_get_eval (byte *, int, int, int, float *);
00039 extern void hash_print_stats ();
00040 extern void hash_insert (byte *, int, int, int, float, byte *move);
00041 extern void hash_clear ();
00042 extern void hash_insert_move (byte *, int, int, byte *);
00043 extern byte * hash_get_move (byte *, int, int);
00044 
00045 extern gboolean opt_verbose;
00046 
00047 // FIXME: move this to ui.c
00048 gboolean game_use_hash = TRUE;
00049         
00050 // FIXME: this function is too complicated
00051 float ab_with_tt (Pos *pos, int player, int level, 
00052                 float alpha, float beta, byte *best_movep)
00053         /* level is the number of ply to search */
00054 {
00055         int to_play = player;
00056         float val, cacheval, best= 0, retval;
00057         gboolean first = TRUE;
00058         byte *movlist, *move;
00059         byte best_move [4096];
00060         Pos newpos;
00061         gboolean hashed_move = TRUE;
00062         float local_alpha = -1e+16, local_beta = 1e+16;
00063         byte *orig_move;
00064         best_move [0] = -1;
00065         
00066         engine_poll ();
00067         if (engine_stop_search) { ab_tree_exhausted = FALSE; return 0; }
00068 
00069         movlist = game_movegen (pos);
00070         if (movlist[0] == -2)           /* we have no move left */
00071         {
00072                 free (movlist);
00073                 game_eval (pos, to_play, &val);
00074                 if (game_use_hash)
00075                         hash_insert (pos->board, board_wid * board_heit, pos->num_moves, level, val, 
00076                                 NULL);
00077                 return val;
00078         }
00079         move = NULL;
00080         orig_move = NULL;
00081         if (game_use_hash && level > 0)
00082                 move = hash_get_move (pos->board, board_wid * board_heit, pos->num_moves);
00083         if (!move)
00084         {
00085                 move = movlist;
00086                 hashed_move = FALSE;
00087         }
00088         // origmove is the owning pointer and move is the aliasing pointer
00089         else orig_move = move = movdup (move);
00090         
00091         newpos.board = (char *) malloc (board_wid * board_heit);
00092         assert (newpos.board);
00093         if (game_stateful)
00094         {
00095                 newpos.state = (void *) malloc (game_state_size);
00096                 assert (newpos.state);
00097         }
00098 
00099         do
00100         {
00101                 if (!orig_move || hashed_move || !movcmp_literal (orig_move, move))
00102                 {
00103                         ResultType result = RESULT_NOTYET;
00104                         memcpy (newpos.board, pos->board, board_wid * board_heit);
00105                         if (game_stateful)
00106                         {
00107                                 void *newstate = game_newstate (pos, move);
00108                                 memcpy (newpos.state, newstate, game_state_size);
00109                         }
00110                         move_apply (newpos.board, move);
00111                         newpos.num_moves = pos->num_moves + 1;
00112                         newpos.player = pos->player == WHITE ? BLACK : WHITE;
00113                         retval = 0;
00114                         if (game_use_hash && level > 0)
00115                                 retval = hash_get_eval (newpos.board, board_wid * board_heit, 
00116                                                 newpos.num_moves, level-1, &cacheval);
00117                         if (retval && fabs (cacheval) < GAME_EVAL_INFTY) val = cacheval;
00118                         else result = game_eval (&newpos, to_play == WHITE ? BLACK : WHITE, &val);
00119                         if (level == 0)
00120                         {
00121                                 ab_leaf_cnt ++;
00122                                 ab_tree_exhausted = FALSE;
00123 /*                              if (game_use_hash)
00124                                         hash_insert (newpos.board, board_wid * board_heit, 
00125                                                 pos->num_moves, level, val, NULL);
00126 */
00127                         }
00128                         else 
00129                         {
00130                                 if (fabs (val) >= GAME_EVAL_INFTY)
00131                                         val *= (1 + level);
00132                                 else if (result == RESULT_WHITE || result == RESULT_BLACK)
00133                                         val *= (1 + level);
00134                                 else if (result == RESULT_TIE)
00135                                         ;
00136                                 else
00137                                 {
00138                                         val = ab_with_tt (&newpos, player == WHITE ? BLACK : WHITE, 
00139                                                                 level-1, alpha, beta, best_move);
00140                                 }
00141                         }
00142                         if((player == WHITE && val > local_alpha) 
00143                                         || (player == BLACK && val < local_beta))
00144                         {
00145                                 if (best_movep) movcpy (best_movep, move);
00146                                 if (player == WHITE) local_alpha = val; else local_beta = val;
00147                         }
00148 
00149                         if((player == WHITE && val > alpha))
00150                                 alpha = val;
00151                         if ((player == BLACK && val < beta))
00152                                 beta  = val;
00153                         if (alpha >= beta || alpha >= GAME_EVAL_INFTY || beta <= -GAME_EVAL_INFTY)
00154                                 break;
00155                 }
00156                 if (hashed_move)
00157                         move = movlist;
00158                 else
00159                         move = movlist_next (move);
00160                 hashed_move = FALSE;
00161         }
00162         while (move[0] != -2);
00163         free (newpos.board);
00164         if (game_stateful) free (newpos.state);
00165         free (movlist);
00166         free (orig_move);
00167         if (game_use_hash)
00168                 hash_insert (pos->board, board_wid * board_heit, pos->num_moves, level,
00169                         player == WHITE ? alpha : beta, best_movep);
00170         return player == WHITE ? alpha : beta;
00171 }
00172 
00173 // TODO: this is currently unused, and must be merged with the previous function 
00174 float ab_with_tt_incr (Pos *pos, int player, int level, 
00175                 float eval, float alpha, float beta, byte *best_movep)
00176         /* level is the number of ply to search */
00177 {
00178         int to_play = player;
00179         float val, cacheval, best= 0, retval;
00180         int first = 1;
00181         byte *movlist, *move;
00182         byte best_move [4096];
00183         void *oldstate = NULL; // use the recursion to implement stack of states
00184         engine_poll ();
00185         if (engine_stop_search) 
00186         { 
00187                 ab_tree_exhausted = FALSE; 
00188                 return 0; 
00189         }
00190         if (game_stateful)
00191         {
00192                 oldstate = (void *) malloc (game_state_size);
00193                 assert (oldstate);
00194         }
00195         movlist = game_movegen (pos);
00196         if (movlist[0] == -2)           /* we have no move left */
00197         {
00198                 free (movlist);
00199                 game_eval (pos, to_play, &val);
00200                 return val;
00201         }
00202         move = movlist;
00203         do
00204         {
00205                 float neweval = 0, incr_eval;
00206                 ResultType result;
00207                 result = game_eval_incr (pos, to_play, move, &incr_eval);
00208                 neweval = eval + incr_eval;
00209                 
00210                 if (level == 0)
00211                 {
00212                         ab_leaf_cnt ++;
00213                         ab_tree_exhausted = FALSE;
00214                         val = neweval;
00215                 }
00216                 else if (fabs (incr_eval) >= GAME_EVAL_INFTY 
00217                                 || result != RESULT_NOTYET)
00218                         // one side has won; search no more
00219                 {
00220                         ab_leaf_cnt ++;
00221                         val = neweval * (1 + level); // the sooner the better
00222                 }
00223                 else 
00224                 {
00225                         int found = 0;
00226                         byte *movinv = mov_getinv (pos->board, move);
00227                         move_apply (pos->board, move);
00228                         if (game_stateful)
00229                         {
00230                                 void *newstate = game_newstate (pos, move);
00231                                 memcpy (oldstate, pos->state, game_state_size);
00232                                 memcpy (pos->state, newstate, game_state_size);
00233                         }
00234                         pos->num_moves++;
00235                         pos->player = pos->player == WHITE ? BLACK : WHITE;
00236                         val = 0; // stop compiler warning
00237                         if (level >= 1)
00238                         {
00239                                 retval = hash_get_eval (pos->board, board_wid * board_heit, 
00240                                                 pos->num_moves, level, &cacheval);
00241                                 if (retval && cacheval < GAME_EVAL_INFTY)  
00242                                         { val = cacheval; found = 1; }
00243                         }
00244                         if (!found)
00245                                 val = ab_with_tt_incr
00246                                         (pos, player == WHITE ? BLACK : WHITE, 
00247                                                 level-1, neweval, alpha, beta, best_move);
00248                         if (level >= 1)
00249                                 hash_insert (pos->board, board_wid * board_heit, 
00250                                                 pos->num_moves, level, val, NULL);
00251                         move_apply (pos->board, movinv);
00252                         free (movinv);
00253                         memcpy (pos->state, oldstate, game_state_size);
00254                         pos->num_moves--;
00255                         pos->player = pos->player == WHITE ? BLACK : WHITE;
00256                 }
00257                 if (first)
00258                 {
00259                         if (best_movep) movcpy (best_movep, move);
00260                         first = 0;
00261                 }
00262                 if ((player == WHITE && val > alpha) || (player == BLACK && val < beta))
00263                 {
00264                         if (best_movep) movcpy (best_movep, move);
00265                         if (player == WHITE) alpha = val; else beta = val;
00266                 }
00267                 if (alpha >= beta)
00268                         break;
00269                 move = movlist_next (move);
00270         }
00271         while (move[0] != -2);
00272         free (movlist);
00273         if (game_stateful)
00274                 free (oldstate);
00275         return player == WHITE ? alpha : beta;
00276 }
00277 
00278 static void catch_USR1 (int sig)
00279 {
00280         engine_stop_search = 1;
00281         signal (SIGUSR1, catch_USR1);
00282 }
00283 
00284 byte * ab_dfid (Pos *pos, int player)
00285 {
00286         static byte best_move[4096];
00287         byte local_best_move[4096];
00288         int ply;
00289         float val = 0, eval = 0, oldval = 0;
00290         static GTimer *timer = NULL;
00291         gboolean found = FALSE;
00292         byte *move_list;
00293         engine_stop_search = 0;
00294         if (!game_movegen || !game_eval)
00295                 return NULL;
00296         signal (SIGUSR1, catch_USR1);
00297         ab_leaf_cnt=0;
00298 
00299         move_list = game_movegen (pos);
00300         if (move_list[0] == -2)
00301         {
00302                 free (move_list);
00303                 if (opt_verbose) printf ("No move\n");
00304                 return NULL;
00305         }
00306         if (movlist_next (move_list)[0] == -2)
00307         {
00308                 movcpy (best_move, move_list);
00309                 if (opt_verbose) printf ("Only one legal move\n");
00310                 return best_move;
00311         }
00312 
00313         if (!timer) timer = g_timer_new ();
00314         g_timer_start (timer);
00315         
00316         for (ply = 0; !engine_stop_search; ply++)
00317         {
00318                 oldval = val;
00319                 ab_tree_exhausted = TRUE;
00320                 val = ab_with_tt (pos, player, ply, -1e+16, 1e+16, local_best_move);
00321                 if (!engine_stop_search)
00322                 {
00323                         movcpy (best_move, local_best_move);
00324                         found = TRUE;
00325                 }
00326                 
00327                 if (ab_tree_exhausted)
00328                 {
00329                         if (opt_verbose)
00330                                 printf ("Searched whole tree. Moves=%d;\t Ply=%d\n",
00331                                         pos->num_moves, ply);
00332                         ply++;
00333                         break;
00334                 }
00335 
00336                 {
00337                         gulong micro_sec;
00338                         float time_taken;
00339                         time_taken = g_timer_elapsed (timer, &micro_sec);
00340                         time_taken += micro_sec / 1000000.0;
00341                         if (time_taken * 1000 > time_per_move / 2)
00342                         {
00343                                 ply++;
00344                                 break;
00345                         }
00346                 }
00347         }
00348         
00349         if (game_use_hash)
00350         {
00351                 hash_print_stats ();
00352                 hash_clear ();
00353         }
00354         
00355         if (opt_verbose) 
00356         { 
00357                 printf ("ab_dfid(): leaves=%d \tply=%d\teval=%.1f\n", 
00358                                 ab_leaf_cnt, ply, oldval);
00359                 printf ("ab_dfid(): move= "); 
00360                 move_fwrite (best_move, stdout); 
00361         }
00362         return found ? best_move : NULL;
00363 }