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 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, µ_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 }