Compare commits
2 Commits
ec2d2291d4
...
148fef20ea
Author | SHA1 | Date | |
---|---|---|---|
148fef20ea | |||
8be03c6230 |
10
Makefile
10
Makefile
@@ -70,7 +70,7 @@ CPPFLAGS += -DBUG_ON # brlib bug.h
|
|||||||
#CPPFLAGS += -DDEBUG_FEN # FEN decoding
|
#CPPFLAGS += -DDEBUG_FEN # FEN decoding
|
||||||
|
|
||||||
# hash.c
|
# hash.c
|
||||||
#CPPFLAGS += -HASH_VERIFY # chk zobrist consistency
|
CPPFLAGS += -DZOBRIST_VERIFY # chk zobrist consistency
|
||||||
|
|
||||||
# attack.c
|
# attack.c
|
||||||
#CPPFLAGS += -DDEBUG_ATTACK_ATTACKERS # sq_attackers
|
#CPPFLAGS += -DDEBUG_ATTACK_ATTACKERS # sq_attackers
|
||||||
@@ -339,7 +339,7 @@ memcheck: targets
|
|||||||
.PHONY: testing test
|
.PHONY: testing test
|
||||||
|
|
||||||
TEST := piece-test fen-test bitboard-test movegen-test attack-test
|
TEST := piece-test fen-test bitboard-test movegen-test attack-test
|
||||||
TEST += movedo-test perft-test
|
TEST += movedo-test perft-test tt-test
|
||||||
|
|
||||||
PIECE_OBJS := piece.o
|
PIECE_OBJS := piece.o
|
||||||
FEN_OBJS := $(PIECE_OBJS) fen.o position.o bitboard.o board.o \
|
FEN_OBJS := $(PIECE_OBJS) fen.o position.o bitboard.o board.o \
|
||||||
@@ -349,6 +349,7 @@ MOVEGEN_OBJS := $(BB_OBJS) move.o move-gen.o
|
|||||||
ATTACK_OBJS := $(MOVEGEN_OBJS)
|
ATTACK_OBJS := $(MOVEGEN_OBJS)
|
||||||
MOVEDO_OBJS := $(ATTACK_OBJS) move-do.o misc.o
|
MOVEDO_OBJS := $(ATTACK_OBJS) move-do.o misc.o
|
||||||
PERFT_OBJS := $(MOVEDO_OBJS) search.o
|
PERFT_OBJS := $(MOVEDO_OBJS) search.o
|
||||||
|
TT_OBJS := $(MOVEDO_OBJS)
|
||||||
|
|
||||||
TEST := $(addprefix $(BINDIR)/,$(TEST))
|
TEST := $(addprefix $(BINDIR)/,$(TEST))
|
||||||
|
|
||||||
@@ -359,6 +360,7 @@ MOVEGEN_OBJS := $(addprefix $(OBJDIR)/,$(MOVEGEN_OBJS))
|
|||||||
ATTACK_OBJS := $(addprefix $(OBJDIR)/,$(ATTACK_OBJS))
|
ATTACK_OBJS := $(addprefix $(OBJDIR)/,$(ATTACK_OBJS))
|
||||||
MOVEDO_OBJS := $(addprefix $(OBJDIR)/,$(MOVEDO_OBJS))
|
MOVEDO_OBJS := $(addprefix $(OBJDIR)/,$(MOVEDO_OBJS))
|
||||||
PERFT_OBJS := $(addprefix $(OBJDIR)/,$(PERFT_OBJS))
|
PERFT_OBJS := $(addprefix $(OBJDIR)/,$(PERFT_OBJS))
|
||||||
|
TT_OBJS := $(addprefix $(OBJDIR)/,$(TT_OBJS))
|
||||||
|
|
||||||
test:
|
test:
|
||||||
echo TEST=$(TEST)
|
echo TEST=$(TEST)
|
||||||
@@ -394,6 +396,10 @@ bin/perft-test: test/perft-test.c test/common-test.h $(PERFT_OBJS)
|
|||||||
@echo compiling $@ test executable.
|
@echo compiling $@ test executable.
|
||||||
@$(CC) $(ALL_CFLAGS) $< $(PERFT_OBJS) $(ALL_LDFLAGS) -o $@
|
@$(CC) $(ALL_CFLAGS) $< $(PERFT_OBJS) $(ALL_LDFLAGS) -o $@
|
||||||
|
|
||||||
|
bin/tt-test: test/tt-test.c test/common-test.h $(TT_OBJS)
|
||||||
|
@echo compiling $@ test executable.
|
||||||
|
@$(CC) $(ALL_CFLAGS) $< $(TT_OBJS) $(ALL_LDFLAGS) -o $@
|
||||||
|
|
||||||
##################################### Makefile debug
|
##################################### Makefile debug
|
||||||
.PHONY: showflags wft
|
.PHONY: showflags wft
|
||||||
|
|
||||||
|
@@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
#include "brlib.h" /* brlib types */
|
#include "brlib.h" /* brlib types */
|
||||||
|
|
||||||
#define ONE 1ull
|
#define ONE 1ul
|
||||||
#define U64(const_u64) const_u64##ULL
|
#define U64(const_u64) const_u64##UL
|
||||||
#define BIT(i) ( (u64) (ONE << (i)) )
|
#define BIT(i) ( (u64) (ONE << (i)) )
|
||||||
|
|
||||||
#define BOARDSIZE (8*8)
|
#define BOARDSIZE (8*8)
|
||||||
@@ -176,7 +176,7 @@ s64 clock_elapsed_μs(mclock_t *clock);
|
|||||||
s64 clock_elapsed_ms(mclock_t *clock);
|
s64 clock_elapsed_ms(mclock_t *clock);
|
||||||
double clock_elapsed_sec(mclock_t *clock);
|
double clock_elapsed_sec(mclock_t *clock);
|
||||||
|
|
||||||
#define RAND_SEED_DEFAULT U64(1)
|
#define RAND_SEED_DEFAULT U64(0xb0d1ccea)
|
||||||
|
|
||||||
void rand_init(u64 seed);
|
void rand_init(u64 seed);
|
||||||
u64 rand64(void);
|
u64 rand64(void);
|
||||||
|
181
src/hash.c
181
src/hash.c
@@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include <brlib.h>
|
#include <brlib.h>
|
||||||
#include <bitops.h>
|
#include <bitops.h>
|
||||||
@@ -99,9 +100,17 @@ hkey_t zobrist_calc(pos_t *pos)
|
|||||||
* @return: True if Zobrist key is OK.
|
* @return: True if Zobrist key is OK.
|
||||||
*/
|
*/
|
||||||
#ifdef ZOBRIST_VERIFY
|
#ifdef ZOBRIST_VERIFY
|
||||||
|
|
||||||
|
#pragma push_macro("BUG_ON") /* force BUG_ON and WARN_ON */
|
||||||
|
#pragma push_macro("WARN_ON")
|
||||||
|
#undef BUG_ON
|
||||||
|
#define BUG_ON
|
||||||
|
#undef WARN_ON
|
||||||
|
#define WARN_ON
|
||||||
|
|
||||||
bool zobrist_verify(pos_t *pos)
|
bool zobrist_verify(pos_t *pos)
|
||||||
{
|
{
|
||||||
key_t diff, key = zobrist_calc(pos);
|
hkey_t diff, key = zobrist_calc(pos);
|
||||||
|
|
||||||
if (pos->key == key)
|
if (pos->key == key)
|
||||||
return true;
|
return true;
|
||||||
@@ -120,17 +129,19 @@ bool zobrist_verify(pos_t *pos)
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (castle_rights_t c = CASTLE_NONE; c <= CASTLE_ALL; ++c)
|
for (castle_rights_t c = CASTLE_NONE; c <= CASTLE_ALL; ++c) {
|
||||||
if (diff == zobrist_castling[c]) {
|
if (diff == zobrist_castling[c]) {
|
||||||
warn(true, "zobrist difference is castling:[%d]\n", c);
|
warn(true, "zobrist difference is castling:[%d]\n", c);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (file_t f = FILE_A; f <= FILE_H; ++f)
|
for (file_t f = FILE_A; f <= FILE_H; ++f) {
|
||||||
if (diff == zobrist_ep[f]) {
|
if (diff == zobrist_ep[f]) {
|
||||||
warn(true, "zobrist difference is ep:[%d]\n", f);
|
warn(true, "zobrist difference is ep:[%d]\n", f);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (diff == zobrist_turn) {
|
if (diff == zobrist_turn) {
|
||||||
warn(true, "zobrist difference is turn\n");
|
warn(true, "zobrist difference is turn\n");
|
||||||
goto end;
|
goto end;
|
||||||
@@ -138,11 +149,16 @@ bool zobrist_verify(pos_t *pos)
|
|||||||
warn(true, "zobrist diff %lx is unknown\n", diff);
|
warn(true, "zobrist diff %lx is unknown\n", diff);
|
||||||
end:
|
end:
|
||||||
bug_on(false);
|
bug_on(false);
|
||||||
|
/* not reached */
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
#pragma pop_macro("WARN_ON")
|
||||||
|
#pragma pop_macro("BUG_ON")
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hash_create() - hashtable creation.
|
* tt_create() - create transposition table
|
||||||
* @sizemb: s32 size of hash table in Mb
|
* @sizemb: s32 size of hash table in Mb
|
||||||
*
|
*
|
||||||
* Create a hash table of max @sizemb (or HASH_SIZE_MBif @sizemb <= 0) Mb size.
|
* Create a hash table of max @sizemb (or HASH_SIZE_MBif @sizemb <= 0) Mb size.
|
||||||
@@ -164,7 +180,7 @@ end:
|
|||||||
* @return: hash table size in Mb. If memory allocation fails, the function does
|
* @return: hash table size in Mb. If memory allocation fails, the function does
|
||||||
* not return.
|
* not return.
|
||||||
*/
|
*/
|
||||||
int hash_create(s32 sizemb)
|
int tt_create(s32 sizemb)
|
||||||
{
|
{
|
||||||
size_t bytes, target_nbuckets;
|
size_t bytes, target_nbuckets;
|
||||||
u32 nbits;
|
u32 nbits;
|
||||||
@@ -185,12 +201,12 @@ int hash_create(s32 sizemb)
|
|||||||
|
|
||||||
if (hash_tt.nbits != nbits) {
|
if (hash_tt.nbits != nbits) {
|
||||||
if (hash_tt.nbits)
|
if (hash_tt.nbits)
|
||||||
hash_delete();
|
tt_delete();
|
||||||
|
|
||||||
hash_tt.nbits = nbits;
|
hash_tt.nbits = nbits;
|
||||||
|
|
||||||
hash_tt.nbuckets = BIT(hash_tt.nbits);
|
hash_tt.nbuckets = BIT(hash_tt.nbits);
|
||||||
hash_tt.nkeys = hash_tt.nbuckets * NBUCKETS;
|
hash_tt.nkeys = hash_tt.nbuckets * ENTRIES_PER_BUCKET;
|
||||||
|
|
||||||
hash_tt.bytes = hash_tt.nbuckets * sizeof(bucket_t);
|
hash_tt.bytes = hash_tt.nbuckets * sizeof(bucket_t);
|
||||||
hash_tt.mb = hash_tt.bytes / 1024 / 1024;
|
hash_tt.mb = hash_tt.bytes / 1024 / 1024;
|
||||||
@@ -207,34 +223,173 @@ int hash_create(s32 sizemb)
|
|||||||
// printf("unchanged (cleared)\n");
|
// printf("unchanged (cleared)\n");
|
||||||
//}
|
//}
|
||||||
/* attention - may fail ! */
|
/* attention - may fail ! */
|
||||||
hash_clear();
|
tt_clear();
|
||||||
|
|
||||||
return hash_tt.nbits;
|
return hash_tt.nbits;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hash_clear() - clear hashtable data.
|
* tt_clear() - clear transposition table
|
||||||
*
|
*
|
||||||
* Reset hashtable entries (if available) and statistic information.
|
* Reset hashtable entries (if available) and statistic information.
|
||||||
*/
|
*/
|
||||||
void hash_clear()
|
void tt_clear()
|
||||||
{
|
{
|
||||||
if (hash_tt.keys)
|
if (hash_tt.keys)
|
||||||
memset(hash_tt.keys, 0, hash_tt.bytes);
|
memset(hash_tt.keys, 0, hash_tt.bytes);
|
||||||
|
|
||||||
hash_tt.used_buckets = 0;
|
|
||||||
hash_tt.used_keys = 0;
|
hash_tt.used_keys = 0;
|
||||||
hash_tt.collisions = 0;
|
hash_tt.collisions = 0;
|
||||||
|
hash_tt.hits = 0;
|
||||||
|
hash_tt.misses = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hash_delete() - delete hashtable data.
|
* tt_delete() - delete transposition table
|
||||||
*
|
*
|
||||||
* free hashtable data.
|
* free hashtable data.
|
||||||
*/
|
*/
|
||||||
void hash_delete()
|
void tt_delete()
|
||||||
{
|
{
|
||||||
if (hash_tt.keys)
|
if (hash_tt.keys)
|
||||||
safe_free(hash_tt.keys);
|
safe_free(hash_tt.keys);
|
||||||
memset(&hash_tt, 0, sizeof(hash_tt));
|
memset(&hash_tt, 0, sizeof(hash_tt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tt_probe() - probe tt for an entry
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
hentry_t *tt_probe(hkey_t key)
|
||||||
|
{
|
||||||
|
bucket_t *bucket;
|
||||||
|
hentry_t *entry;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
bug_on(!hash_tt.keys);
|
||||||
|
bucket = hash_tt.keys + (key & hash_tt.mask);
|
||||||
|
|
||||||
|
/* find key in buckets */
|
||||||
|
for (i = 0; i < ENTRIES_PER_BUCKET; ++i) {
|
||||||
|
entry = bucket->entry + i;
|
||||||
|
if (key == entry->key)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i < ENTRIES_PER_BUCKET)
|
||||||
|
return entry;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tt_probe_perft() - probe tt for an entry (perft version)
|
||||||
|
* @key: Zobrist (hkey_t) key
|
||||||
|
* @depth: depth from search root
|
||||||
|
*
|
||||||
|
* Search transposition for @key entry with @depth depth.
|
||||||
|
*
|
||||||
|
* @return: @hentry_t address is found, TT_MISS otherwise.
|
||||||
|
*/
|
||||||
|
hentry_t *tt_probe_perft(const hkey_t key, const u16 depth)
|
||||||
|
{
|
||||||
|
bucket_t *bucket;
|
||||||
|
hentry_t *entry;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
bug_on(!hash_tt.keys);
|
||||||
|
bucket = hash_tt.keys + (key & hash_tt.mask);
|
||||||
|
|
||||||
|
/* find key in buckets */
|
||||||
|
for (i = 0; i < ENTRIES_PER_BUCKET; ++i) {
|
||||||
|
entry = bucket->entry + i;
|
||||||
|
if (key == entry->key && HASH_PERFT_DEPTH(entry->data) == depth) {
|
||||||
|
hash_tt.hits++;
|
||||||
|
//printf("tt hit: key=%lx bucket=%lu entry=%d!\n",
|
||||||
|
// key, bucket - hash_tt.keys, i);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//printf("tt miss: key=%lx bucket=%lu\n",
|
||||||
|
// key, bucket - hash_tt.keys);
|
||||||
|
hash_tt.misses++;
|
||||||
|
return TT_MISS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tt_store_perft() - store a transposition table entry (perft version)
|
||||||
|
* @key: Zobrist (hkey_t) key
|
||||||
|
* @depth: depth from search root
|
||||||
|
* @nodes: value to store
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
hentry_t *tt_store_perft(const hkey_t key, const u16 depth, const u64 nodes)
|
||||||
|
{
|
||||||
|
bucket_t *bucket;
|
||||||
|
hentry_t *entry;
|
||||||
|
int replace = -1, newkey = 0;
|
||||||
|
uint mindepth = 1024;
|
||||||
|
u64 data = HASH_PERFT(depth, nodes);
|
||||||
|
|
||||||
|
//printf("tt_store: key=%lx data=%lx depth=%d=%d nodes=%lu=%lu\n",
|
||||||
|
// key, data, depth, HASH_PERFT_DEPTH(data), nodes, HASH_PERFT_VAL(data));
|
||||||
|
printf("tt_store: key=%lx depth=%d nodes=%lu ",
|
||||||
|
key, depth, nodes);
|
||||||
|
bug_on(!hash_tt.keys);
|
||||||
|
bucket = hash_tt.keys + (key & hash_tt.mask);
|
||||||
|
|
||||||
|
/* find key in buckets */
|
||||||
|
for (int i = 0; i < ENTRIES_PER_BUCKET; ++i) {
|
||||||
|
entry = bucket->entry + i;
|
||||||
|
if (key == entry->key && HASH_PERFT_DEPTH(entry->data)) {
|
||||||
|
printf("tt_store: sup key/depth, this should not happen!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!entry->key) {
|
||||||
|
replace = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* we replace hash if we are higher in tree */
|
||||||
|
if (HASH_PERFT_DEPTH(entry->data) < mindepth) {
|
||||||
|
mindepth = HASH_PERFT_DEPTH(entry->data);
|
||||||
|
replace = i;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* else {
|
||||||
|
*
|
||||||
|
* if (key == entry->key && HASH_PERFT_DEPTH(entry->data) > mindepth) {
|
||||||
|
* mindepth = HASH_PERFT_DEPTH(entry->data);
|
||||||
|
* replace = i;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
if (replace >= 0) {
|
||||||
|
entry = bucket->entry + replace;
|
||||||
|
if (HASH_PERFT_VAL(entry->data)) {
|
||||||
|
printf("REPL entry=%lu[%d] key=%lx->%lx val=%lu->%lu\n",
|
||||||
|
bucket - hash_tt.keys, replace,
|
||||||
|
entry->key, key,
|
||||||
|
HASH_PERFT_VAL(entry->data), nodes);
|
||||||
|
} else {
|
||||||
|
printf("NEW entry=%lu[%d] key=%lx val=%lu\n",
|
||||||
|
bucket - hash_tt.keys, replace,
|
||||||
|
entry->key, nodes);
|
||||||
|
}
|
||||||
|
entry->key = key;
|
||||||
|
entry->data = data;
|
||||||
|
return entry;
|
||||||
|
} else {
|
||||||
|
printf("TT full, skip\n");
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tt_stats()
|
||||||
|
{
|
||||||
|
printf("TT: sz=%u bits=%u bcks=%'lu entries=%'lu mask=%10x"
|
||||||
|
"used=%lu hits/miss=%'lu/%'lu\n",
|
||||||
|
hash_tt.mb, hash_tt.nbits, hash_tt.nbuckets, hash_tt.nkeys, hash_tt.mask,
|
||||||
|
hash_tt.used_keys, hash_tt.hits, hash_tt.misses);
|
||||||
|
//printf("\tused=%lu hits/miss=%lu/%lu\n",
|
||||||
|
// hash_tt.used_keys, hash_tt.hits, hash_tt.misses);
|
||||||
|
}
|
||||||
|
45
src/hash.h
45
src/hash.h
@@ -14,16 +14,18 @@
|
|||||||
#ifndef HASH_H
|
#ifndef HASH_H
|
||||||
#define HASH_H
|
#define HASH_H
|
||||||
|
|
||||||
#include <assert.h>
|
#include <bug.h>
|
||||||
|
|
||||||
#include "chessdefs.h"
|
#include "chessdefs.h"
|
||||||
|
|
||||||
#define NBUCKETS 4 /* buckets per hash table entry */
|
#define ENTRIES_PER_BUCKET 4 /* buckets per hash table entry */
|
||||||
|
|
||||||
#define HASH_SIZE_DEFAULT 32 /* default: 32Mb */
|
#define HASH_SIZE_DEFAULT 32 /* default: 32Mb */
|
||||||
#define HASH_SIZE_MIN 4
|
#define HASH_SIZE_MIN 4
|
||||||
#define HASH_SIZE_MAX 32768 /* 32Gb */
|
#define HASH_SIZE_MAX 32768 /* 32Gb */
|
||||||
|
|
||||||
|
#define TT_MISS NULL
|
||||||
|
|
||||||
typedef u64 hkey_t; /* cannot use typedef for key_t */
|
typedef u64 hkey_t; /* cannot use typedef for key_t */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,8 +48,18 @@ typedef struct {
|
|||||||
};
|
};
|
||||||
} hentry_t;
|
} hentry_t;
|
||||||
|
|
||||||
|
/* hentry perft data:
|
||||||
|
* 0-47: perft value
|
||||||
|
* 48-63: depth
|
||||||
|
*/
|
||||||
|
#define HASH_PERFT_MASK U64(0xffffffffffff)
|
||||||
|
#define HASH_PERFT(depth, val) ((((u64) depth) << 48) | ((val) & HASH_PERFT_MASK))
|
||||||
|
#define HASH_PERFT_VAL(data) ((data) & HASH_PERFT_MASK)
|
||||||
|
#define HASH_PERFT_DEPTH(data) ((u16)((data) >> 48))
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
hentry_t buckets[NBUCKETS];
|
hentry_t entry[ENTRIES_PER_BUCKET];
|
||||||
} bucket_t;
|
} bucket_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -66,9 +78,11 @@ typedef struct {
|
|||||||
u32 mask; /* nbuckets - 1, key mask */
|
u32 mask; /* nbuckets - 1, key mask */
|
||||||
|
|
||||||
/* stats - unsure about usage */
|
/* stats - unsure about usage */
|
||||||
size_t used_buckets;
|
//size_t used_buckets;
|
||||||
size_t used_keys;
|
size_t used_keys;
|
||||||
u64 collisions;
|
u64 collisions;
|
||||||
|
u64 hits;
|
||||||
|
u64 misses;
|
||||||
} hasht_t;
|
} hasht_t;
|
||||||
|
|
||||||
/* hack:
|
/* hack:
|
||||||
@@ -97,8 +111,25 @@ bool zobrist_verify(pos_t *pos);
|
|||||||
#define zobrist_verify(p) true
|
#define zobrist_verify(p) true
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int hash_create(int Mb);
|
/**
|
||||||
void hash_clear(void);
|
* tt_prefetch() - prefetch hash table entry
|
||||||
void hash_delete(void);
|
* @hash: u64 key
|
||||||
|
*
|
||||||
|
* Prefetch memory for @key.
|
||||||
|
*/
|
||||||
|
static inline void tt_prefetch(hkey_t key)
|
||||||
|
{
|
||||||
|
bug_on(!hash_tt.keys);
|
||||||
|
__builtin_prefetch(hash_tt.keys + (key & hash_tt.mask));
|
||||||
|
}
|
||||||
|
|
||||||
|
int tt_create(int Mb);
|
||||||
|
void tt_clear(void);
|
||||||
|
void tt_delete(void);
|
||||||
|
|
||||||
|
hentry_t *tt_probe(hkey_t key);
|
||||||
|
hentry_t *tt_probe_perft(const hkey_t key, const u16 depth);
|
||||||
|
hentry_t *tt_store_perft(const hkey_t key, const u16 depth, const u64 nodes);
|
||||||
|
void tt_stats(void);
|
||||||
|
|
||||||
#endif /* HASH_H */
|
#endif /* HASH_H */
|
||||||
|
@@ -37,6 +37,6 @@ void init_all(void)
|
|||||||
|
|
||||||
/* zobrist tables & default tt hashtable */
|
/* zobrist tables & default tt hashtable */
|
||||||
zobrist_init();
|
zobrist_init();
|
||||||
hash_create(HASH_SIZE_DEFAULT);
|
tt_create(HASH_SIZE_DEFAULT);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -108,7 +108,8 @@ pos_t *move_do(pos_t *pos, const move_t move, state_t *state)
|
|||||||
}
|
}
|
||||||
} else if (is_enpassant(move)) { /* clear grabbed pawn */
|
} else if (is_enpassant(move)) { /* clear grabbed pawn */
|
||||||
square_t grabbed = to - up;
|
square_t grabbed = to - up;
|
||||||
key ^= zobrist_pieces[pos->board[grabbed]][grabbed];
|
piece_t pc = pos->board[grabbed];
|
||||||
|
key ^= zobrist_pieces[pc][grabbed];
|
||||||
pos_clr_sq(pos, grabbed);
|
pos_clr_sq(pos, grabbed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -429,9 +429,10 @@ void pos_print(const pos_t *pos)
|
|||||||
char str[128];
|
char str[128];
|
||||||
|
|
||||||
board_print(pos->board);
|
board_print(pos->board);
|
||||||
printf("fen %s\n", pos2fen(pos, str));
|
printf("key:%lx ", pos->key);
|
||||||
printf("checkers: %s\n", pos_checkers2str(pos, str, sizeof(str)));
|
printf("fen: %s\n", pos2fen(pos, str));
|
||||||
printf("pinners : %s\n", pos_pinners2str(pos, str, sizeof(str)));
|
printf("checkers:%s ", pos_checkers2str(pos, str, sizeof(str)));
|
||||||
|
printf("pinners: %s ", pos_pinners2str(pos, str, sizeof(str)));
|
||||||
printf("blockers: %s\n", pos_blockers2str(pos, str, sizeof(str)));
|
printf("blockers: %s\n", pos_blockers2str(pos, str, sizeof(str)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
42
src/search.c
42
src/search.c
@@ -43,13 +43,16 @@
|
|||||||
*/
|
*/
|
||||||
u64 perft(pos_t *pos, int depth, int ply, bool output)
|
u64 perft(pos_t *pos, int depth, int ply, bool output)
|
||||||
{
|
{
|
||||||
int subnodes;
|
static movelist_t stack;
|
||||||
u64 nodes = 0;
|
//int subnodes;
|
||||||
|
u64 subnodes, nodes = 0;
|
||||||
movelist_t movelist;
|
movelist_t movelist;
|
||||||
move_t *move, *last;
|
move_t *move, *last;
|
||||||
state_t state;
|
state_t state;
|
||||||
|
|
||||||
movelist.nmoves = 0;
|
if (ply == 1)
|
||||||
|
stack.nmoves = 0;
|
||||||
|
|
||||||
pos_set_checkers_pinners_blockers(pos);
|
pos_set_checkers_pinners_blockers(pos);
|
||||||
|
|
||||||
pos_legal(pos, pos_gen_pseudo(pos, &movelist));
|
pos_legal(pos, pos_gen_pseudo(pos, &movelist));
|
||||||
@@ -59,19 +62,46 @@ u64 perft(pos_t *pos, int depth, int ply, bool output)
|
|||||||
nodes++;
|
nodes++;
|
||||||
} else {
|
} else {
|
||||||
move_do(pos, *move, &state);
|
move_do(pos, *move, &state);
|
||||||
|
stack.move[stack.nmoves++] = *move;
|
||||||
|
if (ply == 2 &&
|
||||||
|
//move_from(*move) == F7 &&
|
||||||
|
//move_to(*move) == F5 &&
|
||||||
|
move_from(stack.move[stack.nmoves-2]) == B2 &&
|
||||||
|
move_to(stack.move[stack.nmoves-2]) == B4 &&
|
||||||
|
move_from(stack.move[stack.nmoves-1]) == F7 &&
|
||||||
|
move_to(stack.move[stack.nmoves-1]) == F5
|
||||||
|
) {
|
||||||
|
//&& pos->board[F5] == B_PAWN) {
|
||||||
|
moves_print(&stack, 0);
|
||||||
|
pos_print(pos);
|
||||||
|
}
|
||||||
if (depth == 2) {
|
if (depth == 2) {
|
||||||
movelist_t movelist2;
|
movelist_t movelist2;
|
||||||
pos_set_checkers_pinners_blockers(pos);
|
pos_set_checkers_pinners_blockers(pos);
|
||||||
subnodes = pos_legal(pos, pos_gen_pseudo(pos, &movelist2))->nmoves;
|
subnodes = pos_legal(pos, pos_gen_pseudo(pos, &movelist2))->nmoves;
|
||||||
|
} else {
|
||||||
|
hentry_t *entry;
|
||||||
|
//if (ply >= 4 && ply <= 8) {
|
||||||
|
if (ply == 4) {
|
||||||
|
if ((entry = tt_probe_perft(pos->key, depth))) {
|
||||||
|
subnodes = HASH_PERFT_VAL(entry->data);
|
||||||
|
printf("tt hit key=%lx ply=%d depth=%d nodes=%lu\n",
|
||||||
|
pos->key, ply, depth, subnodes);
|
||||||
} else {
|
} else {
|
||||||
subnodes = perft(pos, depth - 1, ply + 1, output);
|
subnodes = perft(pos, depth - 1, ply + 1, output);
|
||||||
|
tt_store_perft(pos->key, depth, subnodes);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
subnodes = perft(pos, depth - 1, ply + 1, output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (output && ply == 1) {
|
if (output && ply == 1) {
|
||||||
char movestr[8];
|
char movestr[8];
|
||||||
printf("%s: %d\n", move_str(movestr, *move, 0), subnodes);
|
printf("%s: %lu\n", move_to_str(movestr, *move, 0), subnodes);
|
||||||
}
|
}
|
||||||
nodes += subnodes;
|
nodes += subnodes;
|
||||||
move_undo(pos, *move, &state);
|
move_undo(pos, *move, &state);
|
||||||
|
stack.nmoves--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +129,7 @@ u64 perft_alt(pos_t *pos, int depth, int ply, bool output)
|
|||||||
move_t *move, *last;
|
move_t *move, *last;
|
||||||
state_t state;
|
state_t state;
|
||||||
|
|
||||||
movelist.nmoves = 0;
|
//movelist.nmoves = 0;
|
||||||
pos_set_checkers_pinners_blockers(pos);
|
pos_set_checkers_pinners_blockers(pos);
|
||||||
state = pos->state;
|
state = pos->state;
|
||||||
|
|
||||||
@@ -119,7 +149,7 @@ u64 perft_alt(pos_t *pos, int depth, int ply, bool output)
|
|||||||
}
|
}
|
||||||
if (output && ply == 1) {
|
if (output && ply == 1) {
|
||||||
char movestr[8];
|
char movestr[8];
|
||||||
printf("%s: %d\n", move_str(movestr, *move, 0), subnodes);
|
printf("%s: %d\n", move_to_str(movestr, *move, 0), subnodes);
|
||||||
}
|
}
|
||||||
nodes += subnodes;
|
nodes += subnodes;
|
||||||
move_undo_alt(pos, *move);
|
move_undo_alt(pos, *move);
|
||||||
|
@@ -28,14 +28,30 @@ struct fentest {
|
|||||||
char *comment;
|
char *comment;
|
||||||
char *fen;
|
char *fen;
|
||||||
} fentest[] = {
|
} fentest[] = {
|
||||||
|
/******************* TEMP TESTS BELOW *******************/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
{ __LINE__, 1,
|
* { __LINE__, MOVEGEN | MOVEDO | PERFT,
|
||||||
"",
|
* "bug perft TT après 1.b4 f5",
|
||||||
""
|
* "1nbqkbn1/ppp1p1pp/8/r1rpPpK1/1P6/8/P1PP1PPP/RNBQ1BNR w - f6 0 2"
|
||||||
},
|
* },
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* ***************** TEMP TESTS ABOVE ************************** */
|
/*
|
||||||
|
* { __LINE__, MOVEGEN | MOVEDO | PERFT,
|
||||||
|
* "bug perft TT après 1.b4",
|
||||||
|
* "1nbqkbn1/ppp1pppp/8/r1rpP1K1/1P6/8/P1PP1PPP/RNBQ1BNR b - - 0 1",
|
||||||
|
* },
|
||||||
|
*/
|
||||||
|
|
||||||
|
{ __LINE__, MOVEGEN | MOVEDO | PERFT,
|
||||||
|
"bug perft TT",
|
||||||
|
"1nbqkbn1/ppp1pppp/8/r1rpP1K1/8/8/PPPP1PPP/RNBQ1BNR w - d6 0 1",
|
||||||
|
},
|
||||||
|
|
||||||
|
/* ***************** END of TEMP TESTS ******************/
|
||||||
|
/* below line ignored if first test */
|
||||||
|
{ __LINE__, 0, NULL, NULL },
|
||||||
|
|
||||||
{ __LINE__, MOVEGEN | MOVEDO | PERFT,
|
{ __LINE__, MOVEGEN | MOVEDO | PERFT,
|
||||||
"illegal white e.p.",
|
"illegal white e.p.",
|
||||||
@@ -425,6 +441,10 @@ static int fentest_cur = -1;
|
|||||||
static char *next_fen(uint module)
|
static char *next_fen(uint module)
|
||||||
{
|
{
|
||||||
fentest_cur++;
|
fentest_cur++;
|
||||||
|
|
||||||
|
/* skip first entry if NULL - for special testing, see */
|
||||||
|
if (fentest_cur == 0 && fentest[fentest_cur].fen == NULL)
|
||||||
|
fentest_cur++;
|
||||||
while (fentest[fentest_cur].fen && !(fentest[fentest_cur].modules & module))
|
while (fentest[fentest_cur].fen && !(fentest[fentest_cur].modules & module))
|
||||||
fentest_cur++;
|
fentest_cur++;
|
||||||
return fentest[fentest_cur].fen;
|
return fentest[fentest_cur].fen;
|
||||||
|
@@ -59,7 +59,7 @@ int main(int __unused ac, __unused char**av)
|
|||||||
//fflush(stdout);
|
//fflush(stdout);
|
||||||
if (!pos_ok(pos, false)) {
|
if (!pos_ok(pos, false)) {
|
||||||
printf("*** fen %d [%s] move %d [%s] invalid position after move_do\n",
|
printf("*** fen %d [%s] move %d [%s] invalid position after move_do\n",
|
||||||
test_line, fen, j, move_str(movebuf, *move, 0));
|
test_line, fen, j, move_to_str(movebuf, *move, 0));
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -255,8 +255,8 @@ static __unused void compare_moves(movelist_t *fish, movelist_t *me)
|
|||||||
|
|
||||||
static int usage(char *prg)
|
static int usage(char *prg)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Usage: %s [-d depth] [-p pertf-modules] [-n][-v]\n", prg);
|
fprintf(stderr, "Usage: %s [-cmv][-d depth] [-p perft-version] \n", prg);
|
||||||
fprintf(stderr, "\t-d: depth, -p: 1-3, -n: no SF res check, -v: output moves\n");
|
fprintf(stderr, "\t-c/m: print comments/moves, -n: no SF check, -d: depth, -p: 1-3, \n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,6 +264,7 @@ int main(int ac, char**av)
|
|||||||
{
|
{
|
||||||
int test_line;
|
int test_line;
|
||||||
u64 sf_count = 0, my_count;
|
u64 sf_count = 0, my_count;
|
||||||
|
bool comment = false;
|
||||||
char *fen;
|
char *fen;
|
||||||
pos_t *pos = NULL, *fenpos;
|
pos_t *pos = NULL, *fenpos;
|
||||||
pos_t *fishpos = pos_new();
|
pos_t *fishpos = pos_new();
|
||||||
@@ -283,8 +284,11 @@ int main(int ac, char**av)
|
|||||||
int opt, depth = 6, run = 3;
|
int opt, depth = 6, run = 3;
|
||||||
bool sf_run = true, perft_output = false;
|
bool sf_run = true, perft_output = false;
|
||||||
|
|
||||||
while ((opt = getopt(ac, av, "vnd:p:")) != -1) {
|
while ((opt = getopt(ac, av, "cmnd:p:")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
|
case 'c':
|
||||||
|
comment = true;
|
||||||
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
depth = atoi(optarg);
|
depth = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
@@ -294,7 +298,7 @@ int main(int ac, char**av)
|
|||||||
case 'n':
|
case 'n':
|
||||||
sf_run = false;
|
sf_run = false;
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'm':
|
||||||
perft_output = true;
|
perft_output = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -315,7 +319,10 @@ int main(int ac, char**av)
|
|||||||
|
|
||||||
CLOCK_DEFINE(clock, CLOCK_MONOTONIC);
|
CLOCK_DEFINE(clock, CLOCK_MONOTONIC);
|
||||||
while ((fen = next_fen(PERFT | MOVEDO))) {
|
while ((fen = next_fen(PERFT | MOVEDO))) {
|
||||||
|
if (comment)
|
||||||
|
printf("%s\n", *cur_comment()? cur_comment(): "<FIXME>");
|
||||||
test_line = cur_line();
|
test_line = cur_line();
|
||||||
|
tt_clear();
|
||||||
if (!(fenpos = fen2pos(pos, fen))) {
|
if (!(fenpos = fen2pos(pos, fen))) {
|
||||||
printf("wrong fen line = %d: [%s]\n", test_line, fen);
|
printf("wrong fen line = %d: [%s]\n", test_line, fen);
|
||||||
continue;
|
continue;
|
||||||
@@ -370,6 +377,7 @@ int main(int ac, char**av)
|
|||||||
printf("pt1 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n",
|
printf("pt1 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n",
|
||||||
test_line, sf_count, my_count, fen);
|
test_line, sf_count, my_count, fen);
|
||||||
}
|
}
|
||||||
|
tt_stats();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (run & 2) {
|
if (run & 2) {
|
||||||
|
159
test/tt-test.c
Normal file
159
test/tt-test.c
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
/* tt-test.c - transposition table test.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Bruno Raoult ("br")
|
||||||
|
* Licensed under the GNU General Public License v3.0 or later.
|
||||||
|
* Some rights reserved. See COPYING.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with this
|
||||||
|
* program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <brlib.h>
|
||||||
|
|
||||||
|
#include "chessdefs.h"
|
||||||
|
#include "fen.h"
|
||||||
|
#include "position.h"
|
||||||
|
#include "move.h"
|
||||||
|
#include "move-do.h"
|
||||||
|
#include "move-gen.h"
|
||||||
|
#include "search.h"
|
||||||
|
|
||||||
|
// #include "common-test.h"
|
||||||
|
|
||||||
|
static move_t move_in_movelist(movelist_t *ml, square_t from, square_t to, piece_type_t pt)
|
||||||
|
{
|
||||||
|
const int nmoves = ml->nmoves;
|
||||||
|
const move_t *moves = ml->move;
|
||||||
|
int movenum = 0;
|
||||||
|
move_t move;
|
||||||
|
for (movenum = 0; movenum < nmoves; ++movenum) {
|
||||||
|
move = moves[movenum];
|
||||||
|
printf("compare %s%s to %s%s pt=%d ",
|
||||||
|
sq_to_string(from), sq_to_string(to),
|
||||||
|
sq_to_string(move_from(move)),
|
||||||
|
sq_to_string(move_to(move)),
|
||||||
|
pt
|
||||||
|
);
|
||||||
|
if (move_from(move) == from && move_to(move) == to) {
|
||||||
|
printf("HIT!\n");
|
||||||
|
if (pt != NO_PIECE_TYPE && move_promoted(move) != pt)
|
||||||
|
continue;
|
||||||
|
printf("move_in_movelist(%s%s) found from=%s to=%s\n",
|
||||||
|
sq_to_string(from), sq_to_string(to),
|
||||||
|
sq_to_string(move_from(move)),
|
||||||
|
sq_to_string(move_to(move)));
|
||||||
|
return move;
|
||||||
|
} else
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
return MOVE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static move_t move_from_str(pos_t *pos, const char *move)
|
||||||
|
{
|
||||||
|
movelist_t movelist;
|
||||||
|
square_t from = sq_from_string(move);
|
||||||
|
square_t to = sq_from_string(move + 2);
|
||||||
|
piece_type_t promoted = piece_t_from_char(*(move + 4));
|
||||||
|
printf("from=%o to=%o promoted=%d\n", from, to, promoted);
|
||||||
|
|
||||||
|
pos_set_checkers_pinners_blockers(pos);
|
||||||
|
pos_legal(pos, pos_gen_pseudo(pos, &movelist));
|
||||||
|
return move_in_movelist(&movelist, from, to, promoted);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pr_entry(hentry_t *entry)
|
||||||
|
{
|
||||||
|
if (!entry)
|
||||||
|
printf("entry: NULL\n");
|
||||||
|
else {
|
||||||
|
printf("entry: key=%lx depth=%d n=%lu\n",
|
||||||
|
entry->key, HASH_PERFT_DEPTH(entry->data),
|
||||||
|
HASH_PERFT_VAL(entry->data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
pos_t *pos = NULL;
|
||||||
|
char *token, *str, buf[128];
|
||||||
|
hentry_t *entry;
|
||||||
|
move_t move;
|
||||||
|
state_t state;
|
||||||
|
//movelist_t movelist;
|
||||||
|
|
||||||
|
const char *moves_array[] = {
|
||||||
|
"e2e4 e7e5 g1f3 b8c6",
|
||||||
|
"e2e4 b8c6 g1f3 e7e5"
|
||||||
|
};
|
||||||
|
|
||||||
|
init_all();
|
||||||
|
|
||||||
|
for (uint i = 0; i < ARRAY_SIZE(moves_array); ++i) {
|
||||||
|
int depth = 0;
|
||||||
|
str = strdup(moves_array[i]);
|
||||||
|
printf("%2d: ", i + 1);
|
||||||
|
|
||||||
|
pos = startpos(pos);
|
||||||
|
entry = tt_store_perft(pos->key, 0, 123 + depth);
|
||||||
|
pr_entry(entry);
|
||||||
|
token = strtok(str, " \t");
|
||||||
|
while (token) {
|
||||||
|
depth++;
|
||||||
|
printf("%s ", token);
|
||||||
|
|
||||||
|
//pos_set_checkers_pinners_blockers(pos);
|
||||||
|
//pos_legal(pos, pos_gen_pseudo(pos, &movelist));
|
||||||
|
move = move_from_str(pos, token);
|
||||||
|
printf("move: %s\n", move_to_str(buf, move, 0));
|
||||||
|
move_do(pos, move, &state);
|
||||||
|
if ((entry = tt_probe_perft(pos->key, depth))) {
|
||||||
|
printf("tt hit: depth=%d val=%lu",
|
||||||
|
HASH_PERFT_DEPTH(entry->data),
|
||||||
|
HASH_PERFT_VAL(entry->data));
|
||||||
|
} else {
|
||||||
|
tt_store_perft(pos->key, i + 1, depth);
|
||||||
|
printf("tt store: depth=%d val=%lu", depth, (u64)i * 123);
|
||||||
|
};
|
||||||
|
|
||||||
|
token = strtok(NULL, " \t");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
free(str);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ccls bug report: https://github.com/emacs-lsp/emacs-ccls/issues/126
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* int called(int), caller();
|
||||||
|
*
|
||||||
|
* /\**
|
||||||
|
* * called() - test ccls.
|
||||||
|
* * @x: int, the test value
|
||||||
|
* *
|
||||||
|
* * @called() description.
|
||||||
|
* *
|
||||||
|
* * @return: int, a very interesting value.
|
||||||
|
* *\/
|
||||||
|
* int called(int x) { return x; }
|
||||||
|
*
|
||||||
|
* int caller()
|
||||||
|
* {
|
||||||
|
* int i = 0;
|
||||||
|
* called(int x)
|
||||||
|
* return i;
|
||||||
|
* }
|
||||||
|
*/
|
Reference in New Issue
Block a user