This is the doxygen documentation for gtkboard.

.
Main Page   Data Structures   File List   Data Fields   Globals  

hash.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 <stdio.h>
00020 #include <unistd.h>
00021 #include <stdlib.h>
00022 #include <assert.h>
00023 #include <glib.h>
00024 
00025 #include "move.h"
00026 
00048 #ifndef uint
00049 #define uint unsigned
00050 #endif
00051 
00052 #ifndef byte
00053 #define byte gint8
00054 #endif
00055 
00056 // FIXME: write a function called debug or something instead of doing it this way
00057 extern int opt_verbose;
00058 
00059 /* TODO: use a secondary hash fn of 16 bits to use as increment in case
00060    of collision. Also use it for 16 of the 48 bits of the check field */
00061 
00062 /* TODO: hash the state also. games like chess will need it */
00063 
00064 typedef struct
00065 {
00066         guint32 check;  /* should this be 32 or 64 ? */
00067                                 /* hmm.. maybe 48: that would fit nicely into 3 words */
00068         float eval;
00069         int num_moves:16;
00070         int depth:8;
00071         int free:1;
00072         int stale:1;
00073         byte *best_move;
00074 } hash_t;
00075 
00076 static int hash_table_size = 1 << 16;
00077 static int hash_table_max = 3 * 1 << 14;
00078 static int num_hash_coeffts = 1024;
00079 static uint *hash_coeffts = NULL;
00080 static hash_t *hash_table = NULL;
00081 static int hash_filled = 0;
00082 static int hash_eval_hits = 0, hash_eval_misses = 0;
00083 static int hash_move_hits = 0, hash_move_misses = 0;
00084 
00085 static void hash_init ()
00086         /* malloc the stuff */
00087         // FIXME: Do we need to free this?
00088 {
00089         int i;
00090         if (hash_coeffts) return;
00091         hash_coeffts = (uint *) malloc (num_hash_coeffts * sizeof (uint));
00092         assert (hash_coeffts);
00093         for (i=0; i<num_hash_coeffts; i++)
00094                 hash_coeffts[i] = (random() << 1u) + 1;
00095         hash_table = (hash_t *) malloc (hash_table_size * sizeof (hash_t));
00096         assert (hash_table);
00097         for (i=0; i<hash_table_size; i++)
00098                 hash_table[i].free = 1;
00099 }
00100 
00101 static uint get_hash (byte *pos, int poslen)
00102 {
00103         int i;
00104         uint hash = 0;
00105         if (!hash_coeffts)
00106                 hash_init ();
00107         for (i = 0; i < poslen; i++)
00108                 hash += (pos[i] * hash_coeffts[i%num_hash_coeffts]);
00109         return hash;
00110 }
00111 
00112 static uint get_check (byte *pos, int poslen)
00113         /* just another hash function */
00114 {
00115         int i;
00116         uint check = 0;
00117         if (!hash_coeffts)
00118                 hash_init ();
00119         for (i=0; i < poslen; i++)
00120                 check += (pos[i] * hash_coeffts
00121                                 [num_hash_coeffts-1-(i%num_hash_coeffts)]);
00122         return check;
00123 }
00124 
00125 // FIXME: hash should work with state as well
00126 void hash_insert (byte *pos, int poslen, int num_moves, int depth, float eval, 
00127                 byte *move)
00128 {
00129         uint start = get_hash (pos, poslen) % hash_table_size;
00130         uint check = get_check (pos, poslen);
00131         int idx, cnt = 0;
00132         for (idx=start; cnt <= 10; idx = (idx+1) % hash_table_size, cnt++)
00133         {
00134                 if (hash_table[idx].free)
00135                 {
00136                         if (hash_filled >= hash_table_max)
00137                                 continue;
00138                         hash_filled++;
00139                         break;
00140                 }
00141                 if (hash_table[idx].stale)
00142                         break;
00143                 /* even if the same position is already there it should be 
00144                         overwritten with the new depth */
00145                 if (hash_table[idx].check == check)
00146                         break;
00147                 if (hash_filled >= hash_table_max && depth > hash_table[idx].depth)
00148                         break;
00149         }
00150         if (hash_table[idx].free == 0 && hash_table[idx].best_move)
00151                 free (hash_table[idx].best_move);
00152         hash_table[idx].free = 0;
00153         hash_table[idx].check = check;
00154         hash_table[idx].num_moves = num_moves;
00155         hash_table[idx].eval = eval;
00156         hash_table[idx].depth = depth;
00157         hash_table[idx].stale = 0;
00158         hash_table[idx].best_move = (move ? movdup (move) : NULL);
00159 }
00160 
00161 int hash_get_eval (byte *pos, int poslen, int num_moves, int depth, float *evalp)
00162         /* get the eval of a pos if it is there in the hash table 
00163            retval = was it found
00164            eval = answer*/
00165 {
00166         uint idx = get_hash (pos, poslen) % hash_table_size;
00167         uint check = get_check (pos, poslen);
00168         for (; hash_table[idx].free == 0; idx = (idx+1) % hash_table_size)
00169         {
00170                 if (hash_table[idx].check == check)     /* found it */
00171                 {
00172                         /* don't compare 2 evals at different depths*/
00173                         if (hash_table[idx].depth == depth 
00174                                         && hash_table[idx].num_moves == num_moves)
00175                         {
00176                                 if (evalp)
00177                                         *evalp = hash_table[idx].eval;
00178                                 hash_table[idx].stale = 0;
00179                                 hash_eval_hits++;
00180                                 return 1;
00181                         }
00182                         break;
00183                 }
00184         }
00185         /* found a free slot before encountering this pos */
00186         hash_eval_misses++;
00187         return 0;
00188 }
00189 
00190 byte * hash_get_move (byte *pos, int poslen, int num_moves)
00191 {
00192         uint idx = get_hash (pos, poslen) % hash_table_size;
00193         uint check = get_check (pos, poslen);
00194         for (; hash_table[idx].free == 0; idx = (idx+1) % hash_table_size)
00195         {
00196                 if (hash_table[idx].check == check)     /* found it */
00197                 {
00198                         if (hash_table[idx].num_moves == num_moves)
00199                         {
00200                                 hash_table[idx].stale = 0;
00201                                 hash_move_hits++;
00202                                 return hash_table[idx].best_move;
00203                         }
00204                         break;
00205                 }
00206         }
00207         /* found a free slot before encountering this pos */
00208         hash_move_misses++;
00209         return NULL;
00210 }
00211 
00212 void hash_clear ()
00213 {
00214         int i;
00215         for (i=0; i<hash_table_size; i++)
00216         {
00217                 if (hash_table[i].free == 0 && hash_table[i].best_move)
00218                         free (hash_table[i].best_move);
00219                 hash_table[i].free = 1;
00220         }
00221         hash_filled = 0;
00222 }
00223 
00224 void hash_print_stats ()
00225 {
00226         int i, stale=0;
00227         if (opt_verbose) printf ("hashtable: size=%d \tfilled=%d \teval_hits=%d \teval_misses=%d \tmove_hits=%d \tmove_misses=%d \t",
00228                         hash_table_size, hash_filled, 
00229                         hash_eval_hits, hash_eval_misses, hash_move_hits, hash_move_misses);
00230         hash_eval_hits = hash_eval_misses = hash_move_hits = hash_move_misses = 0;
00231         for (i=0; i<hash_table_size; i++)
00232         {
00233                 if (!hash_table[i].free && hash_table[i].stale)
00234                         stale++;
00235                 hash_table[i].stale = 1;
00236         }
00237         if (opt_verbose) printf ("stale=%d\n", stale);
00238 }