Merge branch 'tt'
This commit is contained in:
17
Makefile
17
Makefile
@@ -11,8 +11,8 @@
|
||||
#
|
||||
|
||||
SHELL := /bin/bash
|
||||
#CC := gcc
|
||||
CC := gcc-13
|
||||
CC := gcc
|
||||
#CC := gcc-13
|
||||
#CC := clang
|
||||
BEAR := bear
|
||||
TOUCH := touch
|
||||
@@ -69,8 +69,9 @@ CPPFLAGS += -DBUG_ON # brlib bug.h
|
||||
# fen.c
|
||||
#CPPFLAGS += -DDEBUG_FEN # FEN decoding
|
||||
|
||||
# hash.c
|
||||
#CPPFLAGS += -HASH_VERIFY # chk zobrist consistency
|
||||
# hash / TT
|
||||
#CPPFLAGS += -DZOBRIST_VERIFY # double chk zobrist
|
||||
#CPPFLAGS += -DPERFT_MOVE_HISTORY # perft, keep prev moves
|
||||
|
||||
# attack.c
|
||||
#CPPFLAGS += -DDEBUG_ATTACK_ATTACKERS # sq_attackers
|
||||
@@ -339,7 +340,7 @@ memcheck: targets
|
||||
.PHONY: testing 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
|
||||
FEN_OBJS := $(PIECE_OBJS) fen.o position.o bitboard.o board.o \
|
||||
@@ -349,6 +350,7 @@ MOVEGEN_OBJS := $(BB_OBJS) move.o move-gen.o
|
||||
ATTACK_OBJS := $(MOVEGEN_OBJS)
|
||||
MOVEDO_OBJS := $(ATTACK_OBJS) move-do.o misc.o
|
||||
PERFT_OBJS := $(MOVEDO_OBJS) search.o
|
||||
TT_OBJS := $(MOVEDO_OBJS)
|
||||
|
||||
TEST := $(addprefix $(BINDIR)/,$(TEST))
|
||||
|
||||
@@ -359,6 +361,7 @@ MOVEGEN_OBJS := $(addprefix $(OBJDIR)/,$(MOVEGEN_OBJS))
|
||||
ATTACK_OBJS := $(addprefix $(OBJDIR)/,$(ATTACK_OBJS))
|
||||
MOVEDO_OBJS := $(addprefix $(OBJDIR)/,$(MOVEDO_OBJS))
|
||||
PERFT_OBJS := $(addprefix $(OBJDIR)/,$(PERFT_OBJS))
|
||||
TT_OBJS := $(addprefix $(OBJDIR)/,$(TT_OBJS))
|
||||
|
||||
test:
|
||||
echo TEST=$(TEST)
|
||||
@@ -394,6 +397,10 @@ bin/perft-test: test/perft-test.c test/common-test.h $(PERFT_OBJS)
|
||||
@echo compiling $@ test executable.
|
||||
@$(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
|
||||
.PHONY: showflags wft
|
||||
|
||||
|
@@ -14,7 +14,8 @@
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "brlib.h"
|
||||
#include <brlib.h>
|
||||
|
||||
#include "board.h"
|
||||
#include "bitboard.h"
|
||||
|
||||
|
@@ -16,8 +16,8 @@
|
||||
|
||||
#include "brlib.h" /* brlib types */
|
||||
|
||||
#define ONE 1ull
|
||||
#define U64(const_u64) const_u64##ULL
|
||||
#define ONE 1ul
|
||||
#define U64(const_u64) const_u64##UL
|
||||
#define BIT(i) ( (u64) (ONE << (i)) )
|
||||
|
||||
#define BOARDSIZE (8*8)
|
||||
@@ -176,7 +176,7 @@ s64 clock_elapsed_μs(mclock_t *clock);
|
||||
s64 clock_elapsed_ms(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);
|
||||
u64 rand64(void);
|
||||
|
215
src/hash.c
215
src/hash.c
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <brlib.h>
|
||||
#include <bitops.h>
|
||||
@@ -99,10 +100,17 @@ hkey_t zobrist_calc(pos_t *pos)
|
||||
* @return: True if Zobrist key is OK.
|
||||
*/
|
||||
#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)
|
||||
{
|
||||
key_t diff, key = zobrist_calc(pos);
|
||||
|
||||
hkey_t diff, key = zobrist_calc(pos);
|
||||
if (pos->key == key)
|
||||
return true;
|
||||
|
||||
@@ -120,17 +128,19 @@ bool zobrist_verify(pos_t *pos)
|
||||
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]) {
|
||||
warn(true, "zobrist difference is castling:[%d]\n", c);
|
||||
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]) {
|
||||
warn(true, "zobrist difference is ep:[%d]\n", f);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
if (diff == zobrist_turn) {
|
||||
warn(true, "zobrist difference is turn\n");
|
||||
goto end;
|
||||
@@ -138,11 +148,16 @@ bool zobrist_verify(pos_t *pos)
|
||||
warn(true, "zobrist diff %lx is unknown\n", diff);
|
||||
end:
|
||||
bug_on(false);
|
||||
/* not reached */
|
||||
return true;
|
||||
}
|
||||
#pragma pop_macro("WARN_ON")
|
||||
#pragma pop_macro("BUG_ON")
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* hash_create() - hashtable creation.
|
||||
* tt_create() - create transposition table
|
||||
* @sizemb: s32 size of hash table in Mb
|
||||
*
|
||||
* Create a hash table of max @sizemb (or HASH_SIZE_MBif @sizemb <= 0) Mb size.
|
||||
@@ -164,7 +179,7 @@ end:
|
||||
* @return: hash table size in Mb. If memory allocation fails, the function does
|
||||
* not return.
|
||||
*/
|
||||
int hash_create(s32 sizemb)
|
||||
int tt_create(s32 sizemb)
|
||||
{
|
||||
size_t bytes, target_nbuckets;
|
||||
u32 nbits;
|
||||
@@ -185,12 +200,12 @@ int hash_create(s32 sizemb)
|
||||
|
||||
if (hash_tt.nbits != nbits) {
|
||||
if (hash_tt.nbits)
|
||||
hash_delete();
|
||||
tt_delete();
|
||||
|
||||
hash_tt.nbits = 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.mb = hash_tt.bytes / 1024 / 1024;
|
||||
@@ -207,34 +222,202 @@ int hash_create(s32 sizemb)
|
||||
// printf("unchanged (cleared)\n");
|
||||
//}
|
||||
/* attention - may fail ! */
|
||||
hash_clear();
|
||||
tt_clear();
|
||||
|
||||
return hash_tt.nbits;
|
||||
}
|
||||
|
||||
/**
|
||||
* hash_clear() - clear hashtable data.
|
||||
* tt_clear() - clear transposition table
|
||||
*
|
||||
* Reset hashtable entries (if available) and statistic information.
|
||||
*/
|
||||
void hash_clear()
|
||||
void tt_clear()
|
||||
{
|
||||
if (hash_tt.keys)
|
||||
memset(hash_tt.keys, 0, hash_tt.bytes);
|
||||
|
||||
hash_tt.used_buckets = 0;
|
||||
hash_tt.used_keys = 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.
|
||||
*/
|
||||
void hash_delete()
|
||||
void tt_delete()
|
||||
{
|
||||
if (hash_tt.keys)
|
||||
if (hash_tt.keys) {
|
||||
safe_free(hash_tt.keys);
|
||||
memset(&hash_tt, 0, sizeof(hash_tt));
|
||||
hash_tt.keys = NULL;
|
||||
}
|
||||
tt_clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 depth=%d bucket=%lu entry=%d!\n",
|
||||
* key, depth, bucket - hash_tt.keys, i);
|
||||
*/
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* printf("tt miss: key=%lx depth=%d ucket=%lu\n",
|
||||
* key, depth, 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;
|
||||
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 (!entry->key) {
|
||||
// replace = i;
|
||||
//hash_tt.used_keys++;
|
||||
// break;
|
||||
//}
|
||||
if (key == entry->key) {
|
||||
if (depth == HASH_PERFT_DEPTH(entry->data)) {
|
||||
printf("tt_store: dup key=%lx depth=%d, this should not happen!\n",
|
||||
key, depth);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
/* always keep higher nodes */
|
||||
if (HASH_PERFT_DEPTH(entry->data) < mindepth) {
|
||||
mindepth = HASH_PERFT_DEPTH(entry->data);
|
||||
replace = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (replace >= 0) {
|
||||
entry = bucket->entry + replace;
|
||||
|
||||
hash_tt.used_keys += entry->key == 0;
|
||||
hash_tt.collisions += entry->key && (key != entry->key);
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* tt_info() - print hash-table information.
|
||||
*/
|
||||
void tt_info()
|
||||
{
|
||||
if (hash_tt.keys) {
|
||||
printf("TT: Mb:%d buckets:%'lu (bits:%u mask:%#x) entries:%'lu\n",
|
||||
hash_tt.mb, hash_tt.nbuckets, hash_tt.nbits,
|
||||
hash_tt.mask, hash_tt.nkeys);
|
||||
} else {
|
||||
printf("TT: not set.\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tt_stats() - print hash-table usage.
|
||||
*/
|
||||
void tt_stats()
|
||||
{
|
||||
if (hash_tt.keys) {
|
||||
float percent = 100.0 * hash_tt.used_keys / hash_tt.nkeys;
|
||||
printf("hash: used:%'lu/%'lu (%.2f%%) hit:%'lu miss:%'lu coll:%'lu\n",
|
||||
hash_tt.used_keys, hash_tt.nkeys, percent,
|
||||
hash_tt.hits, hash_tt.misses,
|
||||
hash_tt.collisions);
|
||||
} else {
|
||||
printf("hash: not set.\n");
|
||||
}
|
||||
}
|
||||
|
52
src/hash.h
52
src/hash.h
@@ -14,16 +14,20 @@
|
||||
#ifndef HASH_H
|
||||
#define HASH_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <bug.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_MIN 4
|
||||
#define HASH_SIZE_MAX 32768 /* 32Gb */
|
||||
|
||||
#define TT_MISS NULL
|
||||
#define TT_DUP (void *) U64(0x01)
|
||||
#define TT_OK(p) ((p) > (void *)U64(0xF))
|
||||
|
||||
typedef u64 hkey_t; /* cannot use typedef for key_t */
|
||||
|
||||
/**
|
||||
@@ -33,7 +37,7 @@ typedef u64 hkey_t; /* cannot use typedef for key_
|
||||
* 16 bytes in future, it should be updated to be exactly 32 bytes.
|
||||
*/
|
||||
typedef struct {
|
||||
hkey_t key; /* zobrist */
|
||||
hkey_t key; /* zobrist */
|
||||
union {
|
||||
u64 data;
|
||||
struct {
|
||||
@@ -46,8 +50,18 @@ typedef struct {
|
||||
};
|
||||
} 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 {
|
||||
hentry_t buckets[NBUCKETS];
|
||||
hentry_t entry[ENTRIES_PER_BUCKET];
|
||||
} bucket_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -66,9 +80,11 @@ typedef struct {
|
||||
u32 mask; /* nbuckets - 1, key mask */
|
||||
|
||||
/* stats - unsure about usage */
|
||||
size_t used_buckets;
|
||||
//size_t used_buckets;
|
||||
size_t used_keys;
|
||||
u64 collisions;
|
||||
u64 hits;
|
||||
u64 misses;
|
||||
} hasht_t;
|
||||
|
||||
/* hack:
|
||||
@@ -79,7 +95,7 @@ typedef struct {
|
||||
* we use the formula:
|
||||
* idx = ( ( ep & SQUARE_NONE ) >> 3 ) | sq_file(ep);
|
||||
*/
|
||||
#define EP_ZOBRIST_IDX(ep) ( ( (ep) >> 3 ) | sq_file(ep) )
|
||||
#define EP_ZOBRIST_IDX(ep) ( ( ( ep & SQUARE_NONE ) >> 3 ) | sq_file(ep) )
|
||||
|
||||
extern hkey_t zobrist_pieces[16][64];
|
||||
extern hkey_t zobrist_castling[4 * 4 + 1];
|
||||
@@ -97,8 +113,26 @@ bool zobrist_verify(pos_t *pos);
|
||||
#define zobrist_verify(p) true
|
||||
#endif
|
||||
|
||||
int hash_create(int Mb);
|
||||
void hash_clear(void);
|
||||
void hash_delete(void);
|
||||
/**
|
||||
* tt_prefetch() - prefetch hash table entry
|
||||
* @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_info(void);
|
||||
void tt_stats(void);
|
||||
|
||||
#endif /* HASH_H */
|
||||
|
@@ -37,6 +37,6 @@ void init_all(void)
|
||||
|
||||
/* zobrist tables & default tt hashtable */
|
||||
zobrist_init();
|
||||
hash_create(HASH_SIZE_DEFAULT);
|
||||
tt_create(HASH_SIZE_DEFAULT);
|
||||
|
||||
}
|
||||
|
330
src/move-do.c
330
src/move-do.c
@@ -47,120 +47,8 @@
|
||||
*
|
||||
* @return: updated pos.
|
||||
*/
|
||||
pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state)
|
||||
pos_t *move_do(pos_t *pos, const move_t move, state_t *state)
|
||||
{
|
||||
//# ifdef DEBUG_MOVE_DO
|
||||
// move_print(move, M_PR_NL | M_PR_LONG);
|
||||
//# endif
|
||||
//*state = pos->state; /* save irreversible changes */
|
||||
|
||||
color_t us = pos->turn, them = OPPONENT(us);
|
||||
square_t from = move_from(move), to = move_to(move);
|
||||
piece_t piece = pos->board[from];
|
||||
piece_t captured = pos->board[to];
|
||||
piece_type_t ptype = PIECE(piece);
|
||||
piece_t new_piece = piece;
|
||||
int up = sq_up(us);
|
||||
hkey_t key = pos->key;
|
||||
|
||||
/* update key: switch turn, reset castling and ep */
|
||||
key ^= zobrist_turn;
|
||||
key ^= zobrist_castling[pos->castle];
|
||||
key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)];
|
||||
|
||||
++pos->clock_50;
|
||||
++pos->plycount;
|
||||
pos->en_passant = SQUARE_NONE;
|
||||
pos->turn = them;
|
||||
pos->captured = captured;
|
||||
|
||||
bug_on(COLOR(piece) != us);
|
||||
|
||||
if (is_promotion(move)) {
|
||||
bug_on(sq_rank(to) != sq_rel_rank(RANK_8, us));
|
||||
new_piece = MAKE_PIECE(move_promoted(move), us);
|
||||
}
|
||||
|
||||
if (captured != EMPTY) {
|
||||
pos->clock_50 = 0;
|
||||
//pos->captured = pos->board[to]; /* save capture info */
|
||||
bug_on(pos->board[to] == EMPTY || COLOR(pos->captured) != them);
|
||||
key ^= zobrist_pieces[captured][to];
|
||||
pos_clr_sq(pos, to); /* clear square */
|
||||
} else if (is_castle(move)) { /* handle rook move */
|
||||
square_t rookfrom, rookto;
|
||||
if (is_castle_K(move)) {
|
||||
rookfrom = sq_rel(H1, us);
|
||||
rookto = sq_rel(F1, us);
|
||||
} else {
|
||||
rookfrom = sq_rel(A1, us);
|
||||
rookto = sq_rel(D1, us);
|
||||
}
|
||||
key ^= zobrist_pieces[pos->board[rookfrom]][rookto] ^
|
||||
zobrist_pieces[pos->board[rookfrom]][rookfrom];
|
||||
pos_set_sq(pos, rookto, pos->board[rookfrom]);
|
||||
pos_clr_sq(pos, rookfrom);
|
||||
//pos->castle = clr_castle(pos->castle, us);
|
||||
} else if (ptype == PAWN) { /* pawn non capture or e.p. */
|
||||
pos->clock_50 = 0;
|
||||
if (is_dpush(move)) { /* if pawn double push, set e.p. */
|
||||
pos->en_passant = from + up;
|
||||
/* update ep key */
|
||||
key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)];
|
||||
} else if (is_enpassant(move)) { /* clear grabbed pawn */
|
||||
square_t grabbed = to - up;
|
||||
key ^= zobrist_pieces[pos->board[grabbed]][grabbed];
|
||||
pos_clr_sq(pos, grabbed);
|
||||
}
|
||||
}
|
||||
|
||||
key ^= zobrist_pieces[piece][from] ^ zobrist_pieces[new_piece][to];
|
||||
pos_clr_sq(pos, from); /* clear "from" and set "to" */
|
||||
pos_set_sq(pos, to, new_piece);
|
||||
|
||||
if (ptype == KING)
|
||||
pos->king[us] = to;
|
||||
|
||||
/* update castling flags
|
||||
* As we always consider flags are valid, we :
|
||||
* - adjust our flags if relative from is "E1", "A1", H1"
|
||||
* - adjust opp flags if relative to if "A8", H8"
|
||||
*/
|
||||
if (can_castle(pos->castle, us)) { /* do we save time with this test ? */
|
||||
square_t rel_e1 = sq_rel(E1, us);
|
||||
square_t rel_a1 = sq_rel(A1, us);
|
||||
square_t rel_h1 = sq_rel(H1, us);
|
||||
if (from == rel_e1)
|
||||
pos->castle = clr_castle(pos->castle, us);
|
||||
else if (from == rel_a1)
|
||||
pos->castle = clr_ooo(pos->castle, us);
|
||||
else if (from == rel_h1)
|
||||
pos->castle = clr_oo(pos->castle, us);
|
||||
}
|
||||
if (can_castle(pos->castle, them)) {
|
||||
square_t rel_a8 = sq_rel(A8, us);
|
||||
square_t rel_h8 = sq_rel(H8, us);
|
||||
if (to == rel_a8)
|
||||
pos->castle = clr_ooo(pos->castle, them);
|
||||
else if (to == rel_h8)
|
||||
pos->castle = clr_oo(pos->castle, them);
|
||||
}
|
||||
|
||||
/* update castling rights key */
|
||||
key ^= zobrist_castling[pos->castle];
|
||||
|
||||
pos->key = key;
|
||||
|
||||
bug_on(zobrist_verify(pos) == false);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
pos_t *move_do2(pos_t *pos, const move_t move, state_t *state)
|
||||
{
|
||||
//# ifdef DEBUG_MOVE_DO
|
||||
// move_print(move, M_PR_NL | M_PR_LONG);
|
||||
//# endif
|
||||
color_t us = pos->turn, them = OPPONENT(us);
|
||||
square_t from = move_from(move), to = move_to(move);
|
||||
piece_t piece = pos->board[from];
|
||||
@@ -205,26 +93,28 @@ pos_t *move_do2(pos_t *pos, const move_t move, state_t *state)
|
||||
rookfrom = sq_rel(A1, us);
|
||||
rookto = sq_rel(D1, us);
|
||||
}
|
||||
key ^= zobrist_pieces[pos->board[rookfrom]][rookto];
|
||||
key ^= zobrist_pieces[pos->board[rookfrom]][rookfrom];
|
||||
key ^= zobrist_pieces[pos->board[rookfrom]][rookto] ^
|
||||
zobrist_pieces[pos->board[rookfrom]][rookfrom];
|
||||
pos_set_sq(pos, rookto, pos->board[rookfrom]);
|
||||
pos_clr_sq(pos, rookfrom);
|
||||
pos->castle = clr_castle(pos->castle, us);
|
||||
} else if (ptype == PAWN) { /* pawn non capture or e.p. */
|
||||
pos->clock_50 = 0;
|
||||
if (is_dpush(move)) { /* if pawn double push, set e.p. */
|
||||
pos->en_passant = from + up;
|
||||
/* update key */
|
||||
key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)];
|
||||
if (from + up + up == to) { /* if pawn double push, set e.p. */
|
||||
square_t ep = from + up;
|
||||
if (bb_pawn_attacks[us][ep] & pos->bb[them][PAWN]) {
|
||||
pos->en_passant = ep;
|
||||
key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)];
|
||||
}
|
||||
} else if (is_enpassant(move)) { /* clear grabbed pawn */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
key ^= zobrist_pieces[piece][from];
|
||||
key ^= zobrist_pieces[new_piece][to];
|
||||
key ^= zobrist_pieces[piece][from] ^ zobrist_pieces[new_piece][to];
|
||||
pos_clr_sq(pos, from); /* clear "from" and set "to" */
|
||||
pos_set_sq(pos, to, new_piece);
|
||||
|
||||
@@ -256,12 +146,12 @@ pos_t *move_do2(pos_t *pos, const move_t move, state_t *state)
|
||||
pos->castle = clr_oo(pos->castle, them);
|
||||
}
|
||||
|
||||
/* update castle key */
|
||||
/* update castling rights key */
|
||||
key ^= zobrist_castling[pos->castle];
|
||||
|
||||
pos->key = key;
|
||||
|
||||
bug_on(zobrist_verify(pos) == false);
|
||||
zobrist_verify(pos);
|
||||
|
||||
return pos;
|
||||
}
|
||||
@@ -283,46 +173,158 @@ pos_t *move_do2(pos_t *pos, const move_t move, state_t *state)
|
||||
*
|
||||
* @return: pos.
|
||||
*/
|
||||
pos_t *move_undo(pos_t *pos, const move_t move)
|
||||
{
|
||||
color_t them = pos->turn, us = OPPONENT(them);
|
||||
square_t from = move_from(move), to = move_to(move);
|
||||
piece_t piece = pos->board[to];
|
||||
int up = sq_up(them);
|
||||
|
||||
if (is_promotion(move))
|
||||
piece = MAKE_PIECE(PAWN, us);
|
||||
|
||||
pos_clr_sq(pos, to); /* always clear "to" ... */
|
||||
pos_set_sq(pos, from, piece); /* ... and set "from" */
|
||||
|
||||
if (PIECE(piece) == KING)
|
||||
pos->king[us] = from;
|
||||
|
||||
if (pos->captured != EMPTY) {
|
||||
pos_set_sq(pos, to, pos->captured); /* restore captured piece */
|
||||
} else if (is_castle(move)) { /* make reverse rook move */
|
||||
square_t rookfrom, rookto;
|
||||
if (is_castle_K(move)) {
|
||||
rookfrom = sq_rel(F1, us);
|
||||
rookto = sq_rel(H1, us);
|
||||
} else {
|
||||
rookfrom = sq_rel(D1, us);
|
||||
rookto = sq_rel(A1, us);
|
||||
}
|
||||
pos_set_sq(pos, rookto, pos->board[rookfrom]);
|
||||
pos_clr_sq(pos, rookfrom);
|
||||
} else if (is_enpassant(move)) { /* restore grabbed pawn */
|
||||
square_t grabbed = to + up;
|
||||
pos_set_sq(pos, grabbed, MAKE_PIECE(PAWN, them));
|
||||
}
|
||||
|
||||
//pos->state = *state; /* restore irreversible changes */
|
||||
pos->turn = us;
|
||||
return pos;
|
||||
}
|
||||
|
||||
pos_t *move_undo2(pos_t *pos, const move_t move, const state_t *state)
|
||||
pos_t *move_undo(pos_t *pos, const move_t move, const state_t *state)
|
||||
{
|
||||
color_t them = pos->turn, us = OPPONENT(them);
|
||||
square_t from = move_from(move), to = move_to(move);
|
||||
piece_t piece = pos->board[to];
|
||||
int up = sq_up(them);
|
||||
|
||||
if (is_promotion(move))
|
||||
piece = MAKE_PIECE(PAWN, us);
|
||||
|
||||
pos_clr_sq(pos, to); /* always clear "to" ... */
|
||||
pos_set_sq(pos, from, piece); /* ... and set "from" */
|
||||
|
||||
if (PIECE(piece) == KING)
|
||||
pos->king[us] = from;
|
||||
|
||||
if (pos->captured != EMPTY) {
|
||||
pos_set_sq(pos, to, pos->captured); /* restore captured piece */
|
||||
} else if (is_castle(move)) { /* make reverse rook move */
|
||||
square_t rookfrom, rookto;
|
||||
if (is_castle_K(move)) {
|
||||
rookfrom = sq_rel(F1, us);
|
||||
rookto = sq_rel(H1, us);
|
||||
} else {
|
||||
rookfrom = sq_rel(D1, us);
|
||||
rookto = sq_rel(A1, us);
|
||||
}
|
||||
pos_set_sq(pos, rookto, pos->board[rookfrom]);
|
||||
pos_clr_sq(pos, rookfrom);
|
||||
} else if (is_enpassant(move)) { /* restore grabbed pawn */
|
||||
square_t grabbed = to + up;
|
||||
pos_set_sq(pos, grabbed, MAKE_PIECE(PAWN, them));
|
||||
}
|
||||
|
||||
pos->state = *state; /* restore irreversible changes */
|
||||
pos->turn = us;
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* move_{do,undo}_alt - alternative move_do/move_undo (to experiment)
|
||||
*/
|
||||
pos_t *move_do_alt(pos_t *pos, const move_t move, state_t *state)
|
||||
{
|
||||
color_t us = pos->turn, them = OPPONENT(us);
|
||||
square_t from = move_from(move), to = move_to(move);
|
||||
piece_t piece = pos->board[from];
|
||||
piece_t captured = pos->board[to];
|
||||
piece_type_t ptype = PIECE(piece);
|
||||
piece_t new_piece = piece;
|
||||
int up = sq_up(us);
|
||||
hkey_t key = pos->key;
|
||||
|
||||
*state = pos->state; /* save irreversible changes */
|
||||
|
||||
/* update key: switch turn, reset castling and ep */
|
||||
key ^= zobrist_turn;
|
||||
key ^= zobrist_castling[pos->castle];
|
||||
key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)];
|
||||
|
||||
++pos->clock_50;
|
||||
++pos->plycount;
|
||||
pos->en_passant = SQUARE_NONE;
|
||||
pos->turn = them;
|
||||
pos->captured = captured;
|
||||
|
||||
bug_on(COLOR(piece) != us);
|
||||
|
||||
if (is_promotion(move)) {
|
||||
bug_on(sq_rank(to) != sq_rel_rank(RANK_8, us));
|
||||
new_piece = MAKE_PIECE(move_promoted(move), us);
|
||||
}
|
||||
|
||||
if (captured != EMPTY) {
|
||||
pos->clock_50 = 0;
|
||||
//pos->captured = pos->board[to]; /* save capture info */
|
||||
bug_on(pos->board[to] == EMPTY || COLOR(pos->captured) != them);
|
||||
key ^= zobrist_pieces[captured][to];
|
||||
pos_clr_sq(pos, to); /* clear square */
|
||||
} else if (is_castle(move)) { /* handle rook move */
|
||||
square_t rookfrom, rookto;
|
||||
if (is_castle_K(move)) {
|
||||
rookfrom = sq_rel(H1, us);
|
||||
rookto = sq_rel(F1, us);
|
||||
} else {
|
||||
rookfrom = sq_rel(A1, us);
|
||||
rookto = sq_rel(D1, us);
|
||||
}
|
||||
key ^= zobrist_pieces[pos->board[rookfrom]][rookto] ^
|
||||
zobrist_pieces[pos->board[rookfrom]][rookfrom];
|
||||
pos_set_sq(pos, rookto, pos->board[rookfrom]);
|
||||
pos_clr_sq(pos, rookfrom);
|
||||
pos->castle = clr_castle(pos->castle, us);
|
||||
} else if (ptype == PAWN) { /* pawn non capture or e.p. */
|
||||
pos->clock_50 = 0;
|
||||
if (from + up + up == to) { /* if pawn double push, set e.p. */
|
||||
square_t ep = from + up;
|
||||
if (bb_pawn_attacks[us][ep] & pos->bb[them][PAWN]) {
|
||||
pos->en_passant = ep;
|
||||
key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)];
|
||||
}
|
||||
} else if (is_enpassant(move)) { /* clear grabbed pawn */
|
||||
square_t grabbed = to - up;
|
||||
piece_t pc = pos->board[grabbed];
|
||||
key ^= zobrist_pieces[pc][grabbed];
|
||||
pos_clr_sq(pos, grabbed);
|
||||
}
|
||||
}
|
||||
|
||||
key ^= zobrist_pieces[piece][from] ^ zobrist_pieces[new_piece][to];
|
||||
pos_clr_sq(pos, from); /* clear "from" and set "to" */
|
||||
pos_set_sq(pos, to, new_piece);
|
||||
|
||||
if (ptype == KING)
|
||||
pos->king[us] = to;
|
||||
|
||||
/* update castling flags
|
||||
* As we always consider flags are valid, we :
|
||||
* - adjust our flags if relative from is "E1", "A1", H1"
|
||||
* - adjust opp flags if relative to if "A8", H8"
|
||||
*/
|
||||
if (can_castle(pos->castle, us)) { /* do we save time with this test ? */
|
||||
square_t rel_e1 = sq_rel(E1, us);
|
||||
square_t rel_a1 = sq_rel(A1, us);
|
||||
square_t rel_h1 = sq_rel(H1, us);
|
||||
if (from == rel_e1)
|
||||
pos->castle = clr_castle(pos->castle, us);
|
||||
else if (from == rel_a1)
|
||||
pos->castle = clr_ooo(pos->castle, us);
|
||||
else if (from == rel_h1)
|
||||
pos->castle = clr_oo(pos->castle, us);
|
||||
}
|
||||
if (can_castle(pos->castle, them)) {
|
||||
square_t rel_a8 = sq_rel(A8, us);
|
||||
square_t rel_h8 = sq_rel(H8, us);
|
||||
if (to == rel_a8)
|
||||
pos->castle = clr_ooo(pos->castle, them);
|
||||
else if (to == rel_h8)
|
||||
pos->castle = clr_oo(pos->castle, them);
|
||||
}
|
||||
|
||||
/* update castling rights key */
|
||||
key ^= zobrist_castling[pos->castle];
|
||||
|
||||
pos->key = key;
|
||||
|
||||
zobrist_verify(pos);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
pos_t *move_undo_alt(pos_t *pos, const move_t move, const state_t *state)
|
||||
{
|
||||
color_t them = pos->turn, us = OPPONENT(them);
|
||||
square_t from = move_from(move), to = move_to(move);
|
||||
|
@@ -16,10 +16,11 @@
|
||||
|
||||
#include "position.h"
|
||||
|
||||
pos_t *move_do(pos_t *pos, const move_t move);//, state_t *state);
|
||||
pos_t *move_undo(pos_t *pos, const move_t move);//, const state_t *state);
|
||||
pos_t *move_do(pos_t *pos, const move_t move, state_t *state);
|
||||
pos_t *move_undo(pos_t *pos, const move_t move, const state_t *state);
|
||||
|
||||
pos_t *move_do2(pos_t *pos, const move_t move, state_t *state);
|
||||
pos_t *move_undo2(pos_t *pos, const move_t move, const state_t *state);
|
||||
/* new version testing */
|
||||
pos_t *move_do_alt(pos_t *pos, const move_t move, state_t *state);
|
||||
pos_t *move_undo_alt(pos_t *pos, const move_t move, const state_t *state);
|
||||
|
||||
#endif /* MOVE_DO_H */
|
||||
|
@@ -353,19 +353,12 @@ movelist_t *pos_gen_pseudo(pos_t *pos, movelist_t *movelist)
|
||||
bitboard_t from_bb, to_bb;
|
||||
bitboard_t tmp_bb;
|
||||
move_t *moves = movelist->move;
|
||||
//int *nmoves = &movelist->nmoves;
|
||||
square_t from, to;
|
||||
square_t king = pos->king[us];
|
||||
|
||||
//*nmoves = 0;
|
||||
|
||||
/* king - MUST BE FIRST */
|
||||
to_bb = bb_king_moves(dest_squares, king);
|
||||
moves = moves_gen(moves, king, to_bb);
|
||||
//while(to_bb) {
|
||||
// to = bb_next(&to_bb);
|
||||
// *moves++ = move_make(king, to);
|
||||
//}
|
||||
|
||||
if (bb_multiple(pos->checkers)) /* double check, we stop here */
|
||||
goto finish;
|
||||
@@ -382,8 +375,6 @@ movelist_t *pos_gen_pseudo(pos_t *pos, movelist_t *movelist)
|
||||
* Attention ! Castling flags are assumed correct
|
||||
*/
|
||||
bitboard_t rel_rank1 = bb_rel_rank(RANK_1, us);
|
||||
//square_t from_square[2] = { E1, E8 }; /* verify king is on E1/E8 */
|
||||
//bug_on(can_castle(pos->castle, us) && from != from_square[us]);
|
||||
/* For castle, we check the opponent attacks on squares between from and to.
|
||||
* To square attack check will be done in gen_is_legal.
|
||||
*/
|
||||
@@ -400,26 +391,19 @@ movelist_t *pos_gen_pseudo(pos_t *pos, movelist_t *movelist)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* sliding pieces */
|
||||
from_bb = pos->bb[us][BISHOP] | pos->bb[us][QUEEN];
|
||||
while (from_bb) {
|
||||
from = bb_next(&from_bb);
|
||||
to_bb = hyperbola_bishop_moves(occ, from) & dest_squares;
|
||||
moves = moves_gen(moves, from, to_bb);
|
||||
//while(to_bb) {
|
||||
// to = bb_next(&to_bb);
|
||||
// *moves++ = move_make(from, to);
|
||||
//}
|
||||
}
|
||||
from_bb = pos->bb[us][ROOK] | pos->bb[us][QUEEN];
|
||||
while (from_bb) {
|
||||
from = bb_next(&from_bb);
|
||||
to_bb = hyperbola_rook_moves(occ, from) & dest_squares;
|
||||
moves = moves_gen(moves, from, to_bb);
|
||||
//while(to_bb) {
|
||||
// to = bb_next(&to_bb);
|
||||
// *moves++ = move_make(from, to);
|
||||
//}
|
||||
}
|
||||
|
||||
/* knight */
|
||||
@@ -428,10 +412,6 @@ movelist_t *pos_gen_pseudo(pos_t *pos, movelist_t *movelist)
|
||||
from = bb_next(&from_bb);
|
||||
to_bb = bb_knight_moves(dest_squares, from);
|
||||
moves = moves_gen(moves, from, to_bb);
|
||||
//while(to_bb) {
|
||||
// to = bb_next(&to_bb);
|
||||
// *moves++ = move_make(from, to);
|
||||
//}
|
||||
}
|
||||
|
||||
/* pawn: relative rank and files */
|
||||
@@ -460,12 +440,11 @@ movelist_t *pos_gen_pseudo(pos_t *pos, movelist_t *movelist)
|
||||
while(to_bb) {
|
||||
to = bb_next(&to_bb);
|
||||
from = to - shift - shift;
|
||||
*moves++ = move_make_flags(from, to, M_DPUSH);
|
||||
*moves++ = move_make(from, to);
|
||||
}
|
||||
|
||||
/* pawn: captures */
|
||||
tmp_bb = bb_pawns_attacks(pos->bb[us][PAWN], shift) & enemy_pieces;
|
||||
//bb_print("FAIL", tmp_bb);
|
||||
to_bb = tmp_bb & ~rel_rank8;
|
||||
while (to_bb) {
|
||||
to = bb_next(&to_bb);
|
||||
|
@@ -89,7 +89,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* move_str() - get a move string
|
||||
* move_to_str() - get a move string
|
||||
* @dst: destination memory
|
||||
* @move: move
|
||||
* @flags: moves selection and display options.
|
||||
@@ -102,7 +102,7 @@
|
||||
* M_PR_NL: print a newline after move
|
||||
* M_PR_EVAL: print move eval
|
||||
*/
|
||||
char *move_str(char *dst, const move_t move, __unused const int flags)
|
||||
char *move_to_str(char *dst, const move_t move, __unused const int flags)
|
||||
{
|
||||
square_t from = move_from(move);
|
||||
square_t to = move_to(move);
|
||||
@@ -116,7 +116,6 @@ char *move_str(char *dst, const move_t move, __unused const int flags)
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* moves_print() - print movelist moves.
|
||||
* @moves: &movelist_t moves list
|
||||
@@ -135,7 +134,7 @@ void moves_print(movelist_t *moves, __unused int flags)
|
||||
char str[16];
|
||||
//printf("%2d:", moves->nmoves);
|
||||
for (int m = 0; m < moves->nmoves; ++m)
|
||||
printf("%s ", move_str(str, moves->move[m], flags));
|
||||
printf("%s ", move_to_str(str, moves->move[m], flags));
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
68
src/move.h
68
src/move.h
@@ -42,30 +42,30 @@ enum {
|
||||
M_OFF_FROM = 0,
|
||||
M_OFF_TO = 6,
|
||||
M_OFF_PROMOTED = 12,
|
||||
M_OFF_CAPTURED = 15,
|
||||
M_OFF_FLAGS = 18
|
||||
// M_OFF_CAPTURED = 15,
|
||||
M_OFF_FLAGS = 15
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
M_CAPTURE = BIT(M_OFF_FLAGS + 0),
|
||||
M_ENPASSANT = BIT(M_OFF_FLAGS + 1),
|
||||
M_PROMOTION = BIT(M_OFF_FLAGS + 2),
|
||||
M_CASTLE_K = BIT(M_OFF_FLAGS + 3), /* maybe only one ? */
|
||||
M_CASTLE_Q = BIT(M_OFF_FLAGS + 5), /* maybe only one ? */
|
||||
M_CHECK = BIT(M_OFF_FLAGS + 6), /* maybe unknown/useless ? */
|
||||
M_DPUSH = BIT(M_OFF_FLAGS + 7) /* pawn double push */
|
||||
M_PROMOTION = 070000,
|
||||
// M_CAPTURE = BIT(M_OFF_FLAGS + 0),
|
||||
M_ENPASSANT = BIT(M_OFF_FLAGS + 0),
|
||||
M_CASTLE_K = BIT(M_OFF_FLAGS + 1), /* maybe only one ? */
|
||||
M_CASTLE_Q = BIT(M_OFF_FLAGS + 2), /* maybe only one ? */
|
||||
M_CHECK = BIT(M_OFF_FLAGS + 3), /* maybe unknown/useless ? */
|
||||
// M_DPUSH = BIT(M_OFF_FLAGS + 7) /* pawn double push */
|
||||
} move_flags_t;
|
||||
|
||||
#define move_set_flags(move, flags) ((move) | (flags))
|
||||
|
||||
#define is_capture(m) ((m) & M_CAPTURE)
|
||||
//#define is_capture(m) ((m) & M_CAPTURE)
|
||||
#define is_enpassant(m) ((m) & M_ENPASSANT)
|
||||
#define is_promotion(m) ((m) & M_PROMOTION)
|
||||
#define is_castle(m) ((m) & (M_CASTLE_K | M_CASTLE_Q))
|
||||
#define is_castle_K(m) ((m) & M_CASTLE_K)
|
||||
#define is_castle_Q(m) ((m) & M_CASTLE_Q)
|
||||
#define is_check(m) ((m) & M_CHECK)
|
||||
#define is_dpush(m) ((m) & M_DPUSH)
|
||||
//#define is_dpush(m) ((m) & M_DPUSH)
|
||||
|
||||
#define MOVES_MAX 256
|
||||
|
||||
@@ -89,10 +89,12 @@ static inline piece_type_t move_promoted(move_t move)
|
||||
return (move >> M_OFF_PROMOTED) & 07;
|
||||
}
|
||||
|
||||
static inline piece_type_t move_captured(move_t move)
|
||||
{
|
||||
return (move >> M_OFF_CAPTURED) & 07;
|
||||
}
|
||||
/*
|
||||
* static inline piece_type_t move_captured(move_t move)
|
||||
* {
|
||||
* return (move >> M_OFF_CAPTURED) & 07;
|
||||
* }
|
||||
*/
|
||||
|
||||
static inline move_t move_make(square_t from, square_t to)
|
||||
{
|
||||
@@ -105,10 +107,12 @@ static inline move_t move_make_flags(square_t from, square_t to, move_flags_t fl
|
||||
//move_set_flags(move_make(from, to), flags);
|
||||
}
|
||||
|
||||
static inline move_t move_make_capture(square_t from, square_t to)
|
||||
{
|
||||
return move_make_flags(from, to, M_CAPTURE);
|
||||
}
|
||||
/*
|
||||
* static inline move_t move_make_capture(square_t from, square_t to)
|
||||
* {
|
||||
* return move_make_flags(from, to, M_CAPTURE);
|
||||
* }
|
||||
*/
|
||||
|
||||
static inline move_t move_make_enpassant(square_t from, square_t to)
|
||||
{
|
||||
@@ -118,19 +122,23 @@ static inline move_t move_make_enpassant(square_t from, square_t to)
|
||||
static inline move_t move_make_promote(square_t from, square_t to,
|
||||
piece_type_t promoted)
|
||||
{
|
||||
return move_make_flags(from, to, M_PROMOTION) | (promoted << M_OFF_PROMOTED);
|
||||
return move_make(from, to) | (promoted << M_OFF_PROMOTED);
|
||||
}
|
||||
|
||||
static inline move_t move_make_promote_capture(square_t from, square_t to,
|
||||
piece_type_t promoted)
|
||||
{
|
||||
return move_make_promote(from, to, promoted) | M_CAPTURE;
|
||||
}
|
||||
/*
|
||||
* static inline move_t move_make_promote_capture(square_t from, square_t to,
|
||||
* piece_type_t promoted)
|
||||
* {
|
||||
* return move_make_promote(from, to, promoted) | M_CAPTURE;
|
||||
* }
|
||||
*/
|
||||
|
||||
static inline move_t move_set_captured(move_t move, piece_type_t captured)
|
||||
{
|
||||
return move | (captured << M_OFF_CAPTURED);
|
||||
}
|
||||
/*
|
||||
* static inline move_t move_set_captured(move_t move, piece_type_t captured)
|
||||
* {
|
||||
* return move | (captured << M_OFF_CAPTURED);
|
||||
* }
|
||||
*/
|
||||
|
||||
/* moves_print flags
|
||||
*/
|
||||
@@ -144,7 +152,7 @@ static inline move_t move_set_captured(move_t move, piece_type_t captured)
|
||||
#define M_PR_LONG 0x80
|
||||
|
||||
//int move_print(int movenum, move_t *move, move_flags_t flags);
|
||||
char *move_str(char *dst, const move_t move, __unused const int flags);
|
||||
char *move_to_str(char *dst, const move_t move, __unused const int flags);
|
||||
void moves_print(movelist_t *moves, int flags);
|
||||
void move_sort_by_sq(movelist_t *moves);
|
||||
|
||||
|
@@ -78,8 +78,12 @@ char *piece_to_name(piece_t p)
|
||||
|
||||
piece_type_t piece_t_from_char(char c)
|
||||
{
|
||||
char *p = strchr(pieces_str, c);
|
||||
return p? (p - pieces_str) % 6 + 1: NO_PIECE_TYPE;
|
||||
char *p;
|
||||
piece_type_t pt = NO_PIECE_TYPE;
|
||||
if (c && (p = strchr(pieces_str, c))) {
|
||||
pt = (p - pieces_str) % 6 + 1;
|
||||
}
|
||||
return pt;
|
||||
}
|
||||
|
||||
//piece_type_t piece_from_promotion(char c, color_t color)
|
||||
|
@@ -429,9 +429,10 @@ void pos_print(const pos_t *pos)
|
||||
char str[128];
|
||||
|
||||
board_print(pos->board);
|
||||
printf("fen %s\n", pos2fen(pos, str));
|
||||
printf("checkers: %s\n", pos_checkers2str(pos, str, sizeof(str)));
|
||||
printf("pinners : %s\n", pos_pinners2str(pos, str, sizeof(str)));
|
||||
printf("key:%lx ", pos->key);
|
||||
printf("fen: %s\n", pos2fen(pos, 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)));
|
||||
}
|
||||
|
||||
|
61
src/search.c
61
src/search.c
@@ -23,9 +23,10 @@
|
||||
|
||||
/**
|
||||
* perft() - Perform perft on position
|
||||
* @pos: &position to search
|
||||
* @depth: Wanted depth.
|
||||
* @ply: perft depth level.
|
||||
* @pos: &position to search
|
||||
* @depth: Wanted depth.
|
||||
* @ply: current perft depth level (root = 1)
|
||||
* @output: output total for 1st level moves.
|
||||
*
|
||||
* Run perft on a position. This function displays the available moves at @depth
|
||||
* level for each possible first move, and the total of moves.
|
||||
@@ -43,15 +44,17 @@
|
||||
*/
|
||||
u64 perft(pos_t *pos, int depth, int ply, bool output)
|
||||
{
|
||||
int subnodes;
|
||||
u64 nodes = 0;
|
||||
u64 subnodes, nodes = 0;
|
||||
movelist_t movelist;
|
||||
move_t *move, *last;
|
||||
state_t state;
|
||||
# ifdef PERFT_MOVE_HISTORY
|
||||
static movelist_t stack;
|
||||
if (ply == 1)
|
||||
stack.nmoves = 0;
|
||||
# endif
|
||||
|
||||
movelist.nmoves = 0;
|
||||
pos_set_checkers_pinners_blockers(pos);
|
||||
state = pos->state;
|
||||
|
||||
pos_legal(pos, pos_gen_pseudo(pos, &movelist));
|
||||
last = movelist.move + movelist.nmoves;
|
||||
@@ -59,21 +62,34 @@ u64 perft(pos_t *pos, int depth, int ply, bool output)
|
||||
if (depth == 1) {
|
||||
nodes++;
|
||||
} else {
|
||||
move_do(pos, *move);
|
||||
move_do(pos, *move, &state);
|
||||
# ifdef PERFT_MOVE_HISTORY
|
||||
stack.move[stack.nmoves++] = *move;
|
||||
# endif
|
||||
if (depth == 2) {
|
||||
movelist_t movelist2;
|
||||
pos_set_checkers_pinners_blockers(pos);
|
||||
subnodes = pos_legal(pos, pos_gen_pseudo(pos, &movelist2))->nmoves;
|
||||
} else if (ply >= 4) {
|
||||
hentry_t *entry = tt_probe_perft(pos->key, depth);
|
||||
if (entry != TT_MISS) {
|
||||
subnodes = HASH_PERFT_VAL(entry->data);
|
||||
} else {
|
||||
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) {
|
||||
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;
|
||||
move_undo(pos, *move);
|
||||
pos->state = state;
|
||||
move_undo(pos, *move, &state);
|
||||
# ifdef PERFT_MOVE_HISTORY
|
||||
stack.nmoves--;
|
||||
# endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,25 +99,24 @@ u64 perft(pos_t *pos, int depth, int ply, bool output)
|
||||
}
|
||||
|
||||
/**
|
||||
* perft_test() - Perform perft on position, experiment version.
|
||||
* @pos: &position to search
|
||||
* @depth: Wanted depth.
|
||||
* @ply: perft depth level.
|
||||
* perft_alt() - Perform perft on position, experimental version.
|
||||
* @pos: &position to search
|
||||
* @depth: Wanted depth.
|
||||
* @ply: current perft depth level (root = 1)
|
||||
* @output: output total for 1st level moves.
|
||||
*
|
||||
* Run perft on a position. This function displays the available moves at @depth
|
||||
* level for each possible first move, and the total of moves.
|
||||
*
|
||||
* @return: total moves found at @depth level.
|
||||
*/
|
||||
u64 perft_test(pos_t *pos, int depth, int ply, bool output)
|
||||
u64 perft_alt(pos_t *pos, int depth, int ply, bool output)
|
||||
{
|
||||
int subnodes;
|
||||
u64 nodes = 0;
|
||||
u64 subnodes, nodes = 0;
|
||||
movelist_t movelist;
|
||||
move_t *move, *last;
|
||||
state_t state;
|
||||
|
||||
movelist.nmoves = 0;
|
||||
pos_set_checkers_pinners_blockers(pos);
|
||||
|
||||
pos_legal(pos, pos_gen_pseudo(pos, &movelist));
|
||||
@@ -110,20 +125,20 @@ u64 perft_test(pos_t *pos, int depth, int ply, bool output)
|
||||
if (depth == 1) {
|
||||
nodes++;
|
||||
} else {
|
||||
move_do2(pos, *move, &state);
|
||||
move_do_alt(pos, *move, &state);
|
||||
if (depth == 2) {
|
||||
movelist_t movelist2;
|
||||
pos_set_checkers_pinners_blockers(pos);
|
||||
subnodes = pos_legal(pos, pos_gen_pseudo(pos, &movelist2))->nmoves;
|
||||
} else {
|
||||
subnodes = perft_test(pos, depth - 1, ply + 1, output);
|
||||
subnodes = perft_alt(pos, depth - 1, ply + 1, output);
|
||||
}
|
||||
if (output && ply == 1) {
|
||||
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;
|
||||
move_undo2(pos, *move, &state);
|
||||
move_undo_alt(pos, *move, &state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -20,6 +20,6 @@
|
||||
//eval_t pvs(pos_t *pos, int depth, int alpha, int beta, int color);
|
||||
|
||||
u64 perft(pos_t *pos, int depth, int ply, bool output);
|
||||
u64 perft_test(pos_t *pos, int depth, int ply, bool output);
|
||||
u64 perft_alt(pos_t *pos, int depth, int ply, bool output);
|
||||
|
||||
#endif /* SEARCH_H */
|
||||
|
@@ -28,14 +28,26 @@ struct fentest {
|
||||
char *comment;
|
||||
char *fen;
|
||||
} fentest[] = {
|
||||
/*
|
||||
{ __LINE__, 1,
|
||||
"",
|
||||
""
|
||||
},
|
||||
*/
|
||||
/************************************************************
|
||||
* TEMP TESTS BELOW - only run them (till sentinel below) *
|
||||
************************************************************/
|
||||
|
||||
/* ***************** TEMP TESTS ABOVE ************************** */
|
||||
/*
|
||||
* { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
|
||||
* "startpos + 1.e4 e5 2.Nf3 Nc6 3.Bb5 a6 4.Ba4",
|
||||
* "r1bqkbnr/1ppp1ppp/p1n5/4p3/B3P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 1 4"
|
||||
* },
|
||||
* { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
|
||||
* "",
|
||||
* "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1"
|
||||
* },
|
||||
*/
|
||||
|
||||
/******************************************************************
|
||||
* DO NOT DELETE NEXT LINE - sentinel entry for temp tests above. *
|
||||
* ignored if first array entry. *
|
||||
******************************************************************/
|
||||
{ __LINE__, 0, NULL, NULL },
|
||||
|
||||
{ __LINE__, MOVEGEN | MOVEDO | PERFT,
|
||||
"illegal white e.p.",
|
||||
@@ -138,40 +150,45 @@ struct fentest {
|
||||
"checker: h4",
|
||||
"4k3/8/8/8/7b/8/8/4K3 w - - 0 1"
|
||||
},
|
||||
// First game moves
|
||||
/*
|
||||
* { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
|
||||
* "1.e3 - perft bug",
|
||||
* "rnbqkbnr/pppppppp/8/8/8/4P3/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
|
||||
* },
|
||||
* { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT | PERFT,
|
||||
* "1.e3 Nc6 - perft bug",
|
||||
* "r1bqkbnr/pppppppp/2n5/8/8/4P3/PPPP1PPP/RNBQKBNR w KQkq - 1 2"
|
||||
* },
|
||||
* { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT | PERFT,
|
||||
* "1.e3 Nc6 2.Ke2 - perft bug",
|
||||
* "r1bqkbnr/pppppppp/2n5/8/8/4P3/PPPPKPPP/RNBQ1BNR b kq - 2 2"
|
||||
* },
|
||||
* { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
|
||||
* "1.e3 Nc6 2.Ke2 Nd4+ - perft bug",
|
||||
* "r1bqkbnr/pppppppp/8/8/3n4/4P3/PPPPKPPP/RNBQ1BNR w kq - 3 3"
|
||||
* },
|
||||
*/
|
||||
// First game moves
|
||||
{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
|
||||
"startpos",
|
||||
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
|
||||
},
|
||||
//{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
|
||||
// "1.e3 - perft bug",
|
||||
// "rnbqkbnr/pppppppp/8/8/8/4P3/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
|
||||
//},
|
||||
//{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT | PERFT,
|
||||
// "1.e3 Nc6 - perft bug",
|
||||
// "r1bqkbnr/pppppppp/2n5/8/8/4P3/PPPP1PPP/RNBQKBNR w KQkq - 1 2"
|
||||
//},
|
||||
//{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT | PERFT,
|
||||
// "1.e3 Nc6 2.Ke2 - perft bug",
|
||||
// "r1bqkbnr/pppppppp/2n5/8/8/4P3/PPPPKPPP/RNBQ1BNR b kq - 2 2"
|
||||
//},
|
||||
{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
|
||||
"1.e3 Nc6 2.Ke2 Nd4+ - perft bug",
|
||||
"r1bqkbnr/pppppppp/8/8/3n4/4P3/PPPPKPPP/RNBQ1BNR w kq - 3 3"
|
||||
},
|
||||
{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
|
||||
"1.e4",
|
||||
"startpos + 1.e4",
|
||||
"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
|
||||
},
|
||||
{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
|
||||
"1.Nh3",
|
||||
"rnbqkbnr/pppppppp/8/8/8/7N/PPPPPPPP/RNBQKB1R b KQkq - 1 1"
|
||||
"startpos + 1.e4 e5 2.Nf3",
|
||||
"rnbqkbnr/pppp1ppp/8/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2"
|
||||
},
|
||||
{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
|
||||
"1.e4 e5 2.Nf3 Nc6",
|
||||
"r1bqkbnr/pp1ppppp/2n5/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 0 1"
|
||||
"startpos + 1.e4 e5 2.Nf3 Nc6",
|
||||
"r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3"
|
||||
},
|
||||
{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
|
||||
"startpos + 1.e4 e5 2.Nf3 Nc6 3.Bb5 a6 4.Ba4",
|
||||
"r1bqkbnr/1ppp1ppp/p1n5/4p3/B3P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 1 4"
|
||||
},
|
||||
|
||||
// castling test
|
||||
// both can castle queen only
|
||||
{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
|
||||
@@ -425,6 +442,10 @@ static int fentest_cur = -1;
|
||||
static char *next_fen(uint module)
|
||||
{
|
||||
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))
|
||||
fentest_cur++;
|
||||
return fentest[fentest_cur].fen;
|
||||
|
@@ -29,8 +29,8 @@ int main(int __unused ac, __unused char**av)
|
||||
int i = 0, test_line;
|
||||
char *fen, movebuf[8];;
|
||||
pos_t *pos, *savepos;
|
||||
movelist_t pseudo;
|
||||
move_t move;
|
||||
movelist_t movelist;
|
||||
move_t *move, *last;
|
||||
|
||||
init_all();
|
||||
|
||||
@@ -41,39 +41,39 @@ int main(int __unused ac, __unused char**av)
|
||||
continue;
|
||||
}
|
||||
|
||||
pos->checkers = pos_checkers(pos, pos->turn);
|
||||
pos_set_pinners_blockers(pos);
|
||||
|
||||
pos_gen_pseudo(pos, &pseudo);
|
||||
movelist.nmoves = 0;
|
||||
pos_set_checkers_pinners_blockers(pos);
|
||||
pos_legal(pos, pos_gen_pseudo(pos, &movelist));
|
||||
last = movelist.move + movelist.nmoves;
|
||||
savepos = pos_dup(pos);
|
||||
|
||||
state_t state = pos->state;
|
||||
int tmp = 0, j = 0;
|
||||
while ((move = pos_next_legal(pos, &pseudo, &tmp)) != MOVE_NONE) {
|
||||
int j = 0;
|
||||
for (move = movelist.move; move < last; ++move) {
|
||||
//pos_print(pos);
|
||||
//printf("i=%d j=%d turn=%d move=[%s]\n", i, j, pos->turn,
|
||||
// move_str(movebuf, move, 0));
|
||||
//move_p
|
||||
move_do(pos, move);
|
||||
move_do(pos, *move, &state);
|
||||
//pos_print(pos);
|
||||
//fflush(stdout);
|
||||
if (!pos_ok(pos, false)) {
|
||||
printf("*** fen %d move %d [%s] invalid position after move_do\n",
|
||||
test_line, j, movebuf);
|
||||
printf("*** fen %d [%s] move %d [%s] invalid position after move_do\n",
|
||||
test_line, fen, j, move_to_str(movebuf, *move, 0));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
//printf("%d/%d move_do check ok\n", i, j);
|
||||
move_undo(pos, move);
|
||||
move_undo(pos, *move, &state);
|
||||
pos->state = state;
|
||||
if (!pos_ok(pos, false)) {
|
||||
printf("*** fen %d move %d [%s] invalid position after move_undo\n",
|
||||
test_line, j, movebuf);
|
||||
printf("*** fen %d [%s] move %d [%s] invalid position after move_undo\n",
|
||||
test_line, fen, j, movebuf);
|
||||
exit(0);
|
||||
}
|
||||
if (pos_cmp(pos, savepos) != true) {
|
||||
printf("*** fen %d move %d [%s] position differ after move_{do,undo}\n",
|
||||
test_line, j, movebuf);
|
||||
printf("*** fen %d [%s] move %d [%s] position differ after move_{do,undo}\n",
|
||||
test_line, fen, j, movebuf);
|
||||
exit(0);
|
||||
}
|
||||
//fflush(stdout);
|
||||
|
@@ -255,20 +255,29 @@ static __unused void compare_moves(movelist_t *fish, movelist_t *me)
|
||||
|
||||
static int usage(char *prg)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [-d depth] [-p pertf-modules] [-n][-v]\n", prg);
|
||||
fprintf(stderr, "\t-d: depth, -p: 1-3, -n: no SF res check, -v: output moves\n");
|
||||
fprintf(stderr, "Usage: %s [-cms][-d depth] [-p version] [-t size:\n", prg);
|
||||
fprintf(stderr, "\t-c: do *not* print FEN comments\n");
|
||||
fprintf(stderr, "\t-d depth: perft depth (default: 6)");
|
||||
fprintf(stderr, "\t-m: print moves details\n");
|
||||
fprintf(stderr, "\t-s: use Stockfish to validate perft result\n");
|
||||
fprintf(stderr, "\t-t size: Transposition Table size (Mb). Default: 32\n");
|
||||
fprintf(stderr,
|
||||
"\t-p flavor: perft flavor, 1:perft, 2:perft_alt 3:both, default:1\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int ac, char**av)
|
||||
{
|
||||
int test_line;
|
||||
int curtest = 0;
|
||||
u64 sf_count = 0, my_count;
|
||||
bool comment = true, sf_run = false, moves_output = false;
|
||||
char *fen;
|
||||
pos_t *pos = NULL, *fenpos;
|
||||
pos_t *fishpos = pos_new();
|
||||
movelist_t fishmoves;
|
||||
FILE *outfd = NULL;
|
||||
s64 ms, lps;
|
||||
int opt, depth = 6, run = 3, tt = 32, newtt = 32;
|
||||
struct {
|
||||
s64 count, ms;
|
||||
s64 minlps, maxlps;
|
||||
@@ -278,48 +287,73 @@ int main(int ac, char**av)
|
||||
{ .minlps=LONG_MAX },
|
||||
{ .minlps=LONG_MAX },
|
||||
};
|
||||
s64 ms, lps;
|
||||
|
||||
int opt, depth = 6, run = 3;
|
||||
bool sf_run = true, perft_output = false;
|
||||
|
||||
while ((opt = getopt(ac, av, "vnd:p:")) != -1) {
|
||||
while ((opt = getopt(ac, av, "cd:mp:st:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'c':
|
||||
comment = false;
|
||||
break;
|
||||
case 'd':
|
||||
depth = atoi(optarg);
|
||||
if (depth <= 0)
|
||||
depth = 6;
|
||||
break;
|
||||
case 'p': /* 1 or 2 or 3 for both */
|
||||
case 'm':
|
||||
moves_output = false;
|
||||
break;
|
||||
case 'p':
|
||||
run = atoi(optarg);
|
||||
break;
|
||||
case 'n':
|
||||
sf_run = false;
|
||||
case 's':
|
||||
sf_run = true;
|
||||
break;
|
||||
case 'v':
|
||||
perft_output = true;
|
||||
case 't':
|
||||
newtt = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
return usage(*av);
|
||||
}
|
||||
}
|
||||
|
||||
printf("perft: depth = %d run = %x stockfish = %s\n",
|
||||
depth, run, sf_run? "true": "false");
|
||||
if (!run) {
|
||||
printf("Nothing to do, exiting\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
init_all();
|
||||
if (newtt != 32 && newtt > 1) {
|
||||
printf("changing TT size from %d to %d\n", tt, newtt);
|
||||
tt_create(newtt);
|
||||
tt = newtt;
|
||||
}
|
||||
printf("%s: depth:%d tt_size:%d run:%x SF:%s\n",
|
||||
*av,
|
||||
depth, newtt, run,
|
||||
sf_run? "yes": "no");
|
||||
|
||||
if (!run)
|
||||
exit(0);
|
||||
tt_info();
|
||||
printf("\n");
|
||||
|
||||
printf("move_t size:%lu\n", sizeof(move_t));
|
||||
|
||||
if (sf_run)
|
||||
outfd = open_stockfish();
|
||||
|
||||
CLOCK_DEFINE(clock, CLOCK_MONOTONIC);
|
||||
while ((fen = next_fen(PERFT | MOVEDO))) {
|
||||
test_line = cur_line();
|
||||
if (!(fenpos = fen2pos(pos, fen))) {
|
||||
printf("wrong fen line = %d: [%s]\n", test_line, fen);
|
||||
printf("wrong fen line:%d fen:%s\n\n", cur_line(), fen);
|
||||
continue;
|
||||
}
|
||||
curtest++;
|
||||
printf("test:%d line:%d", curtest, cur_line());
|
||||
if (comment)
|
||||
printf(" comment:%s\n",
|
||||
*cur_comment()? cur_comment(): "no test desc");
|
||||
printf("\t%s\n", fen);
|
||||
|
||||
tt_clear();
|
||||
|
||||
pos = fenpos;
|
||||
if (sf_run) {
|
||||
stockfish_fen(outfd, fen);
|
||||
@@ -338,15 +372,13 @@ int main(int ac, char**av)
|
||||
if (lps < res[2].minlps)
|
||||
res[2].minlps = lps;
|
||||
}
|
||||
printf("SF : line=%3d perft=%'lu %'ldms lps=%'lu \"%s\"\n",
|
||||
test_line, sf_count, ms,
|
||||
lps,
|
||||
fen);
|
||||
printf("Stockfish : perft:%'lu ms:%'ld lps:%'lu\n",
|
||||
sf_count, ms, lps);
|
||||
}
|
||||
|
||||
if (run & 1) {
|
||||
clock_start(&clock);
|
||||
my_count = perft(pos, depth, 1, perft_output);
|
||||
my_count = perft(pos, depth, 1, moves_output);
|
||||
ms = clock_elapsed_ms(&clock);
|
||||
if (!ms) {
|
||||
res[0].skipped++;
|
||||
@@ -362,19 +394,17 @@ int main(int ac, char**av)
|
||||
}
|
||||
|
||||
if (!sf_run || sf_count == my_count) {
|
||||
printf("pt1 OK : line=%3d perft=%'lu %'ldms lps=%'lu \"%s\"\n",
|
||||
test_line, my_count, ms,
|
||||
lps,
|
||||
fen);
|
||||
printf("perft : perft:%'lu ms:%'ld lps:%'lu ",
|
||||
my_count, ms, lps);
|
||||
tt_stats();
|
||||
} else {
|
||||
printf("pt1 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n",
|
||||
test_line, sf_count, my_count, fen);
|
||||
printf("perft : perft:%'lu ***ERROR***\n", my_count);
|
||||
}
|
||||
}
|
||||
|
||||
if (run & 2) {
|
||||
clock_start(&clock);
|
||||
my_count = perft_test(pos, depth, 1, perft_output);
|
||||
my_count = perft_alt(pos, depth, 1, moves_output);
|
||||
ms = clock_elapsed_ms(&clock);
|
||||
if (!ms) {
|
||||
res[1].skipped++;
|
||||
@@ -390,13 +420,10 @@ int main(int ac, char**av)
|
||||
}
|
||||
|
||||
if (!sf_run || sf_count == my_count) {
|
||||
printf("pt2 OK : line=%3d perft=%'lu %'ldms lps=%'lu \"%s\"\n",
|
||||
test_line, my_count, ms,
|
||||
lps,
|
||||
fen);
|
||||
printf("perft_alt : perft:%'lu ms:%'ld lps:%'lu\n",
|
||||
my_count, ms, lps);
|
||||
} else {
|
||||
printf("pt2 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n",
|
||||
test_line, sf_count, my_count, fen);
|
||||
printf("perft_alt : perft:%'lu ***ERROR***\n", my_count);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
@@ -405,29 +432,32 @@ int main(int ac, char**av)
|
||||
if (sf_run) {
|
||||
if (!res[2].ms)
|
||||
res[2].ms = 1;
|
||||
printf("total SF %'lums %'lums lps=%'lu min=%'lu max=%'lu (skipped %d)\n",
|
||||
printf("total Stockfish : perft:%'lums ms:%'lums lps:%'lu min:%'lu max:%'lu "
|
||||
"(skipped %d/%d)\n",
|
||||
res[2].count, res[2].ms,
|
||||
res[2].count * 1000l / res[2].ms,
|
||||
res[2].minlps, res[2].maxlps,
|
||||
res[2].skipped);
|
||||
res[0].skipped, curtest);
|
||||
}
|
||||
if (run & 1) {
|
||||
if (!res[0].ms)
|
||||
res[0].ms = 1;
|
||||
printf("total perft %'lums %'lums lps=%'lu min=%'lu max=%'lu (skipped %d)\n",
|
||||
printf("total perft : perft:%'lums ms:%'lums lps:%'lu min:%'lu max:%'lu "
|
||||
"(skipped %d/%d)\n",
|
||||
res[0].count, res[0].ms,
|
||||
res[0].count * 1000l / res[0].ms,
|
||||
res[0].minlps, res[0].maxlps,
|
||||
res[0].skipped);
|
||||
res[0].skipped, curtest);
|
||||
}
|
||||
if (run & 2) {
|
||||
if (!res[1].ms)
|
||||
res[1].ms = 1;
|
||||
printf("total perft2 %'lums %'lums lps=%'lu min=%'lu max=%'lu (skipped %d)\n",
|
||||
printf("total perft_alt : perft:%'lums ms:%'lums lps:%'lu min:%'lu max:%'lu "
|
||||
"(skipped %d/%d)\n",
|
||||
res[1].count, res[1].ms,
|
||||
res[1].count * 1000l / res[1].ms,
|
||||
res[1].minlps, res[1].maxlps,
|
||||
res[1].skipped);
|
||||
res[0].skipped, curtest);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
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