/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see * * http://www.gnu.org/software/gnugo/ for more information. * * * * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * * 2008 and 2009 by the Free Software Foundation. * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation - version 3 or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License in file COPYING for more details. * * * * You should have received a copy of the GNU General Public * * License along with this program; if not, write to the Free * * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02111, USA. * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "random.h" #include "gnugo.h" #include #include #include #include #include "liberty.h" #include "cache.h" #include "sgftree.h" /* ================================================================ */ /* The transposition table */ /* ---------------------------------------------------------------- */ static void tt_init(Transposition_table *table, int memsize); static void tt_clear(Transposition_table *table); /* The transposition table itself. */ Transposition_table ttable; /* Arrays with random numbers for Zobrist hashing of input data (other * than the board position). If you add an array here, do not forget * to also initialize it in keyhash_init() below. */ static Hash_data target1_hash[BOARDMAX]; static Hash_data target2_hash[BOARDMAX]; static Hash_data routine_hash[NUM_CACHE_ROUTINES]; static void keyhash_init(void) { static int is_initialized = 0; if (!is_initialized) { INIT_ZOBRIST_ARRAY(target1_hash); INIT_ZOBRIST_ARRAY(target2_hash); INIT_ZOBRIST_ARRAY(routine_hash); is_initialized = 1; } } static void calculate_hashval_for_tt(Hash_data *hashdata, int routine, int target1, int target2, Hash_data *extra_hash) { *hashdata = board_hash; /* from globals.c */ hashdata_xor(*hashdata, routine_hash[routine]); hashdata_xor(*hashdata, target1_hash[target1]); if (target2 != NO_MOVE) hashdata_xor(*hashdata, target2_hash[target2]); if (extra_hash) hashdata_xor(*hashdata, *extra_hash); } /* Initialize the transposition table. Non-positive memsize means use * the default size of DEFAULT_NUMBER_OF_CACHE_ENTRIES entries. */ static void tt_init(Transposition_table *table, int memsize) { int num_entries; /* Make sure the hash system is initialized. */ hash_init(); keyhash_init(); if (memsize > 0) num_entries = memsize / sizeof(table->entries[0]); else num_entries = DEFAULT_NUMBER_OF_CACHE_ENTRIES; table->num_entries = num_entries; table->entries = malloc(num_entries * sizeof(table->entries[0])); if (table->entries == NULL) { perror("Couldn't allocate memory for transposition table. \n"); exit(1); } table->is_clean = 0; tt_clear(table); } /* Clear the transposition table. */ static void tt_clear(Transposition_table *table) { if (!table->is_clean) { memset(table->entries, 0, table->num_entries * sizeof(table->entries[0])); table->is_clean = 1; } } /* Free the transposition table. */ void tt_free(Transposition_table *table) { free(table->entries); } /* Get result and move. Return value: * 0 if not found * 1 if found, but depth too small to be trusted. In this case the move * can be used for move ordering. * 2 if found and depth is enough so that the result can be trusted. */ int tt_get(Transposition_table *table, enum routine_id routine, int target1, int target2, int remaining_depth, Hash_data *extra_hash, int *value1, int *value2, int *move) { Hash_data hashval; Hashentry *entry; Hashnode *node; /* Sanity check. */ if (remaining_depth < 0 || remaining_depth > HN_MAX_REMAINING_DEPTH) return 0; /* Get the combined hash value. */ calculate_hashval_for_tt(&hashval, routine, target1, target2, extra_hash); /* Get the correct entry and node. */ entry = &table->entries[hashdata_remainder(hashval, table->num_entries)]; if (hashdata_is_equal(hashval, entry->deepest.key)) node = &entry->deepest; else if (hashdata_is_equal(hashval, entry->newest.key)) node = &entry->newest; else return 0; stats.read_result_hits++; /* Return data. Only set the result if remaining depth in the table * is big enough to be trusted. The move can always be used for move * ordering if nothing else. */ if (move) *move = hn_get_move(node->data); if (remaining_depth <= (int) hn_get_remaining_depth(node->data)) { if (value1) *value1 = hn_get_value1(node->data); if (value2) *value2 = hn_get_value2(node->data); stats.trusted_read_result_hits++; return 2; } return 1; } /* Update a transposition table entry. */ void tt_update(Transposition_table *table, enum routine_id routine, int target1, int target2, int remaining_depth, Hash_data *extra_hash, int value1, int value2, int move) { Hash_data hashval; Hashentry *entry; Hashnode *deepest; Hashnode *newest; unsigned int data; /* Get routine costs definitions from liberty.h. */ static const int routine_costs[] = { ROUTINE_COSTS }; gg_assert(routine_costs[NUM_CACHE_ROUTINES] == -1); /* Sanity check. */ if (remaining_depth < 0 || remaining_depth > HN_MAX_REMAINING_DEPTH) return; /* Get the combined hash value. */ calculate_hashval_for_tt(&hashval, routine, target1, target2, extra_hash); data = hn_create_data(remaining_depth, value1, value2, move, routine_costs[routine]); /* Get the entry and nodes. */ entry = &table->entries[hashdata_remainder(hashval, table->num_entries)]; deepest = &entry->deepest; newest = &entry->newest; /* See if we found an already existing node. */ if (hashdata_is_equal(hashval, deepest->key) && remaining_depth >= (int) hn_get_remaining_depth(deepest->data)) { /* Found deepest */ deepest->data = data; } else if (hashdata_is_equal(hashval, newest->key) && remaining_depth >= (int) hn_get_remaining_depth(newest->data)) { /* Found newest */ newest->data = data; /* If newest has become deeper than deepest, then switch them. */ if (hn_get_remaining_depth(newest->data) > hn_get_remaining_depth(deepest->data)) { Hashnode temp; temp = *deepest; *deepest = *newest; *newest = temp; } } else if (hn_get_total_cost(data) > hn_get_total_cost(deepest->data)) { if (hn_get_total_cost(newest->data) < hn_get_total_cost(deepest->data)) *newest = *deepest; deepest->key = hashval; deepest->data = data; } else { /* Replace newest. */ newest->key = hashval; newest->data = data; } stats.read_result_entered++; table->is_clean = 0; } static const char *routine_names[] = { ROUTINE_NAMES }; /* Convert a routine as used in the cache table to a string. */ const char * routine_id_to_string(enum routine_id routine) { return routine_names[(int) routine]; } /* Initialize the cache for read results, using at most the given * number of bytes of memory. If the memory isn't sufficient to * allocate a single node or if the allocation fails, the caching is * disabled. */ void reading_cache_init(int bytes) { tt_init(&ttable, bytes); } /* Clear the cache for read results. */ void reading_cache_clear() { tt_clear(&ttable); } float reading_cache_default_size() { return DEFAULT_NUMBER_OF_CACHE_ENTRIES * sizeof(Hashentry) / 1024.0 / 1024.0; } /* Write reading trace data to an SGF file. Normally called through the * macro SGFTRACE in cache.h. */ void sgf_trace(const char *func, int str, int move, int result, const char *message) { char buf[100]; sprintf(buf, "%s %c%d: ", func, J(str) + 'A' + (J(str) >= 8), board_size - I(str)); if (result == 0) sprintf(buf + strlen(buf), "0"); else if (ON_BOARD(move)) sprintf(buf + strlen(buf), "%s %c%d", result_to_string(result), J(move) + 'A' + (J(move) >= 8), board_size - I(move)); else if (is_pass(move)) sprintf(buf + strlen(buf), "%s PASS", result_to_string(result)); else sprintf(buf + strlen(buf), "%s [%d]", result_to_string(result), move); if (message) sprintf(buf + strlen(buf), " (%s)", message); sgftreeAddComment(sgf_dumptree, buf); } /* Write two group reading (connection) trace data to an SGF file. * Normally called through the macro SGFTRACE2 in cache.h. */ void sgf_trace2(const char *func, int str1, int str2, int move, const char *result, const char *message) { char buf[100]; sprintf(buf, "%s %c%d %c%d: ", func, J(str1) + 'A' + (J(str1) >= 8), board_size - I(str1), J(str2) + 'A' + (J(str2) >= 8), board_size - I(str2)); if (ON_BOARD(move)) sprintf(buf + strlen(buf), "%s %c%d", result, J(move) + 'A' + (J(move) >= 8), board_size - I(move)); else if (is_pass(move)) sprintf(buf + strlen(buf), "%s PASS", result); else sprintf(buf + strlen(buf), "%s [%d]", result, move); if (message) sprintf(buf + strlen(buf), " (%s)", message); sgftreeAddComment(sgf_dumptree, buf); } /* Write semeai reading trace data to an SGF file. Normally called * through the macro SGFTRACE_SEMEAI in cache.h. */ void sgf_trace_semeai(const char *func, int str1, int str2, int move, int result1, int result2, const char *message) { char buf[100]; sprintf(buf, "%s %c%d %c%d: ", func, J(str1) + 'A' + (J(str1) >= 8), board_size - I(str1), J(str2) + 'A' + (J(str2) >= 8), board_size - I(str2)); if (ON_BOARD(move)) sprintf(buf + strlen(buf), "%s %s %c%d", result_to_string(result1), result_to_string(result2), J(move) + 'A' + (J(move) >= 8), board_size - I(move)); else if (is_pass(move)) sprintf(buf + strlen(buf), "%s %s PASS", result_to_string(result1), result_to_string(result2)); else sprintf(buf + strlen(buf), "%s %s [%d]", result_to_string(result1), result_to_string(result2), move); if (message) sprintf(buf + strlen(buf), " (%s)", message); sgftreeAddComment(sgf_dumptree, buf); } /* * Local Variables: * tab-width: 8 * c-basic-offset: 2 * End: */