378 lines
13 KiB
C
378 lines
13 KiB
C
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
|
|
* 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. *
|
|
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
#ifndef _CACHE_H_
|
|
#define _CACHE_H_
|
|
|
|
#include <stdio.h>
|
|
#include "hash.h"
|
|
|
|
/*
|
|
* This file, together with engine/hash.c implements hashing of go positions
|
|
* using a method known as Zobrist hashing. See the Texinfo documentation
|
|
* (Reading/Hashing) for more information.
|
|
*/
|
|
|
|
|
|
/* Hashnode: a node stored in the transposition table.
|
|
*
|
|
* In addition to the position, the hash lock encodes the following data,
|
|
* all hashed:
|
|
* komaster
|
|
* kom_pos
|
|
* routine
|
|
* str1
|
|
* str2
|
|
* extra hashvalue, optional (e.g. encoding a goal array)
|
|
*
|
|
* The data field packs into 32 bits the following
|
|
* fields:
|
|
*
|
|
* RESERVED : 5 bits
|
|
* value1 : 4 bits
|
|
* value2 : 4 bits
|
|
* move : 10 bits
|
|
* cost : 4 bits
|
|
* remaining_depth: 5 bits (depth - stackp) NOTE: HN_MAX_REMAINING_DEPTH
|
|
*
|
|
* The last 9 bits together give an index for the total costs.
|
|
*/
|
|
typedef struct {
|
|
Hash_data key;
|
|
unsigned int data; /* Should be 32 bits, but only wastes 25% if 64 bits. */
|
|
} Hashnode;
|
|
|
|
#define HN_MAX_REMAINING_DEPTH 31
|
|
|
|
|
|
/* Hashentry: an entry, with two nodes of the hash_table
|
|
*/
|
|
typedef struct {
|
|
Hashnode deepest;
|
|
Hashnode newest;
|
|
} Hashentry;
|
|
|
|
/* Hn is for hash node. */
|
|
#define hn_get_value1(hn) ((hn >> 23) & 0x0f)
|
|
#define hn_get_value2(hn) ((hn >> 19) & 0x0f)
|
|
#define hn_get_move(hn) ((hn >> 9) & 0x3ff)
|
|
#define hn_get_cost(hn) ((hn >> 5) & 0x0f)
|
|
#define hn_get_remaining_depth(hn) ((hn >> 0) & 0x1f)
|
|
#define hn_get_total_cost(hn) ((hn >> 0) & 0x1ff)
|
|
|
|
#define hn_create_data(remaining_depth, value1, value2, move, cost) \
|
|
((((value1) & 0x0f) << 23) \
|
|
| (((value2) & 0x0f) << 19) \
|
|
| (((move) & 0x3ff) << 9) \
|
|
| (((cost) & 0x0f) << 5) \
|
|
| (((remaining_depth & 0x1f) << 0)))
|
|
|
|
|
|
/* Transposition_table: transposition table used for caching. */
|
|
typedef struct {
|
|
unsigned int num_entries;
|
|
Hashentry *entries;
|
|
int is_clean;
|
|
} Transposition_table;
|
|
|
|
extern Transposition_table ttable;
|
|
|
|
/* Number of cache entries to use by default if no cache memory usage
|
|
* has been set explicitly.
|
|
*/
|
|
#define DEFAULT_NUMBER_OF_CACHE_ENTRIES 350000
|
|
|
|
void tt_free(Transposition_table *table);
|
|
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);
|
|
void tt_update(Transposition_table *table, enum routine_id routine,
|
|
int target, int target2, int remaining_depth,
|
|
Hash_data *extra_hash,
|
|
int value1, int value2, int move);
|
|
|
|
|
|
/* ================================================================ */
|
|
|
|
|
|
/* Macros used from reading.c, readconnect.c, and owl.c to store and
|
|
* retrieve read results.
|
|
*/
|
|
|
|
#if TRACE_READ_RESULTS
|
|
|
|
#define TRACE_CACHED_RESULT(result, move) \
|
|
gprintf("%o%s %1m %d %d %1m (cached) ", read_function_name, \
|
|
q, stackp, result, move); \
|
|
dump_stack();
|
|
|
|
#define TRACE_CACHED_RESULT2(result1, result2, move) \
|
|
gprintf("%o%s %1m %1m %d %d %d %1m (cached) ", read_function_name, \
|
|
q1, q2, stackp, result1, result2, move); \
|
|
dump_stack();
|
|
|
|
|
|
#define SETUP_TRACE_INFO(name, str) \
|
|
const char *read_function_name = name; \
|
|
int q = find_origin(str);
|
|
|
|
#define SETUP_TRACE_INFO2(name, str1, str2) \
|
|
const char *read_function_name = name; \
|
|
int q1 = board[str1] == EMPTY ? str1 : find_origin(str1); \
|
|
int q2 = board[str2] == EMPTY ? str2 : find_origin(str2);
|
|
|
|
#else
|
|
|
|
#define TRACE_CACHED_RESULT(result, move)
|
|
#define TRACE_CACHED_RESULT2(result1, result2, move)
|
|
|
|
#define SETUP_TRACE_INFO(name, str) \
|
|
const char *read_function_name = name; \
|
|
int q = str;
|
|
|
|
#define SETUP_TRACE_INFO2(name, str1, str2) \
|
|
const char *read_function_name = name; \
|
|
int q1 = str1; \
|
|
int q2 = str2;
|
|
|
|
#endif
|
|
|
|
/* Trace messages in decidestring/decidedragon sgf file. */
|
|
void sgf_trace(const char *func, int str, int move, int result,
|
|
const char *message);
|
|
/* Trace messages in decideconnection sgf file. */
|
|
void sgf_trace2(const char *func, int str1, int str2, int move,
|
|
const char *result, const char *message);
|
|
/* Trace messages in decidesemeai sgf file. */
|
|
void sgf_trace_semeai(const char *func, int str1, int str2, int move,
|
|
int result1, int result2, const char *message);
|
|
|
|
/* Macro to hide the call to sgf_trace(). Notice that a little black
|
|
* magic is going on here. Before using this macro, SETUP_TRACE_INFO
|
|
* must have been called to provide the variables read_function_name
|
|
* and q. These must of course not be used for anything else in
|
|
* the function.
|
|
*/
|
|
#define SGFTRACE(move, result, message) \
|
|
if (sgf_dumptree) \
|
|
sgf_trace(read_function_name, q, move, result, message)
|
|
|
|
/* Corresponding macro for use in connection or semeai reading, where
|
|
* two groups are involved.
|
|
*/
|
|
#define SGFTRACE2(move, result, message) \
|
|
if (sgf_dumptree) \
|
|
sgf_trace2(read_function_name, q1, q2, move, \
|
|
result_to_string(result), message)
|
|
|
|
#define SGFTRACE_SEMEAI(move, result1, result2, message) \
|
|
if (sgf_dumptree) \
|
|
sgf_trace_semeai(read_function_name, q1, q2, move, \
|
|
result1, result2, message)
|
|
|
|
|
|
/* ================================================================ */
|
|
|
|
/*
|
|
* These macros should be used in all the places where we want to
|
|
* return a result from a reading function and where we want to
|
|
* store the result in the hash table at the same time.
|
|
*/
|
|
|
|
#if !TRACE_READ_RESULTS
|
|
|
|
#define READ_RETURN0(routine, str, remaining_depth) \
|
|
do { \
|
|
tt_update(&ttable, routine, str, NO_MOVE, remaining_depth, NULL,\
|
|
0, 0, NO_MOVE);\
|
|
return 0; \
|
|
} while (0)
|
|
|
|
#define READ_RETURN(routine, str, remaining_depth, point, move, value) \
|
|
do { \
|
|
tt_update(&ttable, routine, str, NO_MOVE, remaining_depth, NULL,\
|
|
value, 0, move);\
|
|
if ((value) != 0 && (point) != 0) *(point) = (move); \
|
|
return (value); \
|
|
} while (0)
|
|
|
|
#define READ_RETURN_SEMEAI(routine, str1, str2, remaining_depth, point, move, value1, value2) \
|
|
do { \
|
|
tt_update(&ttable, routine, str1, str2, remaining_depth, NULL, \
|
|
value1, value2, move); \
|
|
if ((value1) != 0 && (point) != 0) *(point) = (move); \
|
|
return; \
|
|
} while (0)
|
|
|
|
#define READ_RETURN_CONN(routine, str1, str2, remaining_depth, point, move, value) \
|
|
do { \
|
|
tt_update(&ttable, routine, str1, str2, remaining_depth, NULL,\
|
|
value, 0, move);\
|
|
if ((value) != 0 && (point) != 0) *(point) = (move); \
|
|
return (value); \
|
|
} while (0)
|
|
|
|
#define READ_RETURN_HASH(routine, str, remaining_depth, hash, point, move, value) \
|
|
do { \
|
|
tt_update(&ttable, routine, str, NO_MOVE, remaining_depth, hash,\
|
|
value, 0, move);\
|
|
if ((value) != 0 && (point) != 0) *(point) = (move); \
|
|
return (value); \
|
|
} while (0)
|
|
|
|
#define READ_RETURN2(routine, str, remaining_depth, point, move, value1, value2) \
|
|
do { \
|
|
tt_update(&ttable, routine, str, NO_MOVE, remaining_depth, NULL,\
|
|
value1, value2, move);\
|
|
if ((value1) != 0 && (point) != 0) *(point) = (move); \
|
|
return (value1); \
|
|
} while (0)
|
|
|
|
#else /* !TRACE_READ_RESULTS */
|
|
|
|
#define READ_RETURN0(routine, str, remaining_depth) \
|
|
do { \
|
|
tt_update(&ttable, routine, str, NO_MOVE, remaining_depth, NULL,\
|
|
0, 0, NO_MOVE);\
|
|
gprintf("%o%s %1m %d 0 0 ", read_function_name, q, stackp); \
|
|
dump_stack(); \
|
|
return 0; \
|
|
} while (0)
|
|
|
|
#define READ_RETURN(routine, str, remaining_depth, point, move, value) \
|
|
do { \
|
|
tt_update(&ttable, routine, str, NO_MOVE, remaining_depth, NULL,\
|
|
value, 0, move);\
|
|
if ((value) != 0 && (point) != 0) *(point) = (move); \
|
|
gprintf("%o%s %1m %d %d %1m ", read_function_name, q, stackp, \
|
|
(value), (move)); \
|
|
dump_stack(); \
|
|
return (value); \
|
|
} while (0)
|
|
|
|
#define READ_RETURN_SEMEAI(routine, str1, str2, remaining_depth, point, move, value1, value2) \
|
|
do { \
|
|
tt_update(&ttable, routine, str1, str2, remaining_depth, NULL, \
|
|
value1, value2, move); \
|
|
if ((value1) != 0 && (point) != 0) *(point) = (move); \
|
|
gprintf("%o%s %1m %1m %d %d %d %1m ", read_function_name, q1, q2, stackp, \
|
|
(value1), (value2), (move)); \
|
|
dump_stack(); \
|
|
return; \
|
|
} while (0)
|
|
|
|
#define READ_RETURN_CONN(routine, str1, str2, remaining_depth, point, move, value) \
|
|
do { \
|
|
tt_update(&ttable, routine, str1, str2, remaining_depth, NULL,\
|
|
value, 0, move);\
|
|
if ((value) != 0 && (point) != 0) *(point) = (move); \
|
|
gprintf("%o%s %1m %1m %d %d %1m ", read_function_name, q1, q2, stackp, \
|
|
(value), (move)); \
|
|
dump_stack(); \
|
|
return (value); \
|
|
} while (0)
|
|
|
|
#define READ_RETURN_HASH(routine, str, remaining_depth, hash, point, move, value) \
|
|
do { \
|
|
tt_update(&ttable, routine, str, NO_MOVE, remaining_depth, hash,\
|
|
value, 0, move);\
|
|
if ((value) != 0 && (point) != 0) *(point) = (move); \
|
|
gprintf("%o%s %1m %d %d %1m ", read_function_name, q, stackp, \
|
|
(value), (move)); \
|
|
dump_stack(); \
|
|
return (value); \
|
|
} while (0)
|
|
|
|
#define READ_RETURN2(routine, str, remaining_depth, point, move, value1, value2) \
|
|
do { \
|
|
tt_update(&ttable, routine, str, NO_MOVE, remaining_depth, NULL,\
|
|
value1, value2, move);\
|
|
if ((value1) != 0 && (point) != 0) *(point) = (move); \
|
|
gprintf("%o%s %1m %d %d %1m ", read_function_name, q, stackp, \
|
|
(value1), (move)); \
|
|
dump_stack(); \
|
|
return (value1); \
|
|
} while (0)
|
|
|
|
#endif
|
|
|
|
|
|
/* ================================================================ */
|
|
/* This has actually nothing to do with caching, but is useful in
|
|
* the same places where the caching is.
|
|
*/
|
|
|
|
/* Macro to use when saving ko results while continuing to look for an
|
|
* unconditional result. It's assumed that we have tried the move at
|
|
* (move) and then called an attack or defense function giving the
|
|
* result passed in the code parameter.
|
|
*
|
|
* In general we prefer not to have to do the first ko threat. Thus a
|
|
* savecode KO_A is always better than a savecode KO_B. Also we always
|
|
* prefer to keep the old move if we get the same savecode once more,
|
|
* on the assumption that the moves have been ordered with the
|
|
* presumably best one first.
|
|
*
|
|
* Notice that the savecode may be either 0 (nothing found so far), KO_B
|
|
* or KO_A. Occasionally savecode WIN is also used, indicating an effective
|
|
* but not preferred move, typically because it's either a sacrifice
|
|
* or a backfilling move. If possible, we prefer making non-sacrifice
|
|
* and direct moves. Of course savecode WIN is better than KO_A or KO_B.
|
|
*/
|
|
|
|
#define UPDATE_SAVED_KO_RESULT(savecode, save, code, move) \
|
|
if (code != 0 && REVERSE_RESULT(code) > savecode) { \
|
|
save = move; \
|
|
savecode = REVERSE_RESULT(code); \
|
|
} \
|
|
|
|
/* Same as above, except this should be used when there's no
|
|
* intervening trymove(). Thus we shouldn't reverse the save code.
|
|
*/
|
|
#define UPDATE_SAVED_KO_RESULT_UNREVERSED(savecode, save, code, move) \
|
|
if (code != WIN && code > savecode) { \
|
|
save = move; \
|
|
savecode = code; \
|
|
}
|
|
|
|
|
|
/* This too isn't really related to caching but is convenient to have here.
|
|
* (Needs to be available in reading.c and persistent.c.)
|
|
*
|
|
* Minimum number of nodes for which DEBUG_READING_PERFORMANCE reports
|
|
* anything.
|
|
*/
|
|
#define MIN_READING_NODES_TO_REPORT 1000
|
|
|
|
|
|
#endif /* _CACHE_H_ */
|
|
|
|
|
|
/*
|
|
* Local Variables:
|
|
* tab-width: 8
|
|
* c-basic-offset: 2
|
|
* End:
|
|
*/
|