11 Commits

15 changed files with 488 additions and 116 deletions

View File

@@ -89,10 +89,10 @@ CFLAGS := -std=gnu11
### dev OR release ### dev OR release
# dev # dev
CFLAGS += -O1 #CFLAGS += -O1
#CFLAGS += -g #CFLAGS += -g
# release # release
#CFLAGS += -Ofast CFLAGS += -Ofast
CFLAGS += -march=native CFLAGS += -march=native
CFLAGS += -flto CFLAGS += -flto
@@ -100,7 +100,7 @@ CFLAGS += -Wall
CFLAGS += -Wextra CFLAGS += -Wextra
CFLAGS += -Wmissing-declarations CFLAGS += -Wmissing-declarations
# for gprof # for gprof
CFLAGS += -pg #CFLAGS += -pg
# Next one may be useful for valgrind (when invalid instructions) # Next one may be useful for valgrind (when invalid instructions)
# CFLAGS += -mno-tbm # CFLAGS += -mno-tbm
@@ -114,7 +114,8 @@ CFLAGS := $(strip $(CFLAGS))
#REL_CFLAGS := -Ofast #REL_CFLAGS := -Ofast
##################################### linker flags ##################################### linker flags
LDFLAGS := -L$(BRLIBDIR) LDFLAGS := --static
LDFLAGS += -L$(BRLIBDIR)
LDFLAGS += -flto LDFLAGS += -flto
LDFLAGS := $(strip $(LDFLAGS)) LDFLAGS := $(strip $(LDFLAGS))
@@ -256,11 +257,11 @@ cleanasmcpp:
@$(call rmfiles,$(ASMFILES) $(CPPFILES),asm and pre-processed) @$(call rmfiles,$(ASMFILES) $(CPPFILES),asm and pre-processed)
%.i: %.c %.i: %.c
@echo generating $@ (cpp processed). @echo "generating $@ (cpp processed)."
@$(CC) -E $(CPPFLAGS) $(CFLAGS) $< -o $@ @$(CC) -E $(CPPFLAGS) $(CFLAGS) $< -o $@
%.s: %.c %.s: %.c
@echo generating $@ (asm). @echo "generating $@ (asm)."
@$(CC) -S -fverbose-asm $(CPPFLAGS) $(CFLAGS) $< -o $@ @$(CC) -S -fverbose-asm $(CPPFLAGS) $(CFLAGS) $< -o $@
##################################### LSP (ccls) ##################################### LSP (ccls)
@@ -309,7 +310,7 @@ BB_OBJS := $(FEN_OBJS)
MOVEGEN_OBJS := $(BB_OBJS) move.o move-gen.o MOVEGEN_OBJS := $(BB_OBJS) move.o move-gen.o
ATTACK_OBJS := $(MOVEGEN_OBJS) ATTACK_OBJS := $(MOVEGEN_OBJS)
MOVEDO_OBJS := $(ATTACK_OBJS) move-do.o MOVEDO_OBJS := $(ATTACK_OBJS) move-do.o
PERFT_OBJS := $(MOVEDO_OBJS) search.o PERFT_OBJS := $(MOVEDO_OBJS) search.o misc.o
TEST := $(addprefix $(BINDIR)/,$(TEST)) TEST := $(addprefix $(BINDIR)/,$(TEST))

View File

@@ -14,8 +14,8 @@
#ifndef _BITBOARD_H #ifndef _BITBOARD_H
#define _BITBOARD_H #define _BITBOARD_H
#include "brlib.h" #include <brlib.h>
#include "bitops.h" #include <bitops.h>
#include "chessdefs.h" #include "chessdefs.h"
#include "board.h" #include "board.h"
@@ -183,14 +183,51 @@ static __always_inline bitboard_t bb_file(int file)
} }
*/ */
/**
* bb_first_bb() - return bitboard of first square of a bitboard.
* @bb: bitboard
*
* bb must be non-zero.
*
* @return: bitboard of first square (lsb) of @bb.
*/
static __always_inline square_t bb_first_bb(bitboard_t bb)
{
return bb & -bb;
}
/**
* bb_next() - clear and return next (lsb) square of a bitboard.
* @bb: &bitboard
*
* The bitboard addressed by @bb must be non-zero.
*
* @return: first bit (lsb) of @bb.
*/
static __always_inline square_t bb_next(bitboard_t *bb)
{
square_t sq = ctz64(*bb);
*bb &= *bb - 1;
return sq;
}
/**
* bb_multiple() - test if a bitboard has multiple bits.
* @bb: bitboard
*
* @return: true if @bb has more than 1 bit, false otherwise.
*/
static __always_inline bool bb_multiple(bitboard_t bb)
{
return !!(bb & (bb - 1));
}
#define bb_rank(r) ((u64) RANK_1bb << ((r) * 8)) #define bb_rank(r) ((u64) RANK_1bb << ((r) * 8))
#define BB_FILE(f) ((u64) FILE_Abb << (f)) #define bb_file(f) ((u64) FILE_Abb << (f))
#define bb_rel_rank(r, c) bb_rank(sq_rel_rank(r, c)) #define bb_rel_rank(r, c) bb_rank(sq_rel_rank(r, c))
//#define BB_REL_RANK(r, c) (RANK_1bb << (SQ_REL_RANK(r, c) * 8))
//#define BB_REL_FILE(f, c) (FILE_Abb << (SQ_REL_RANK((f), (c))))
/** /**
* bb_sq_aligned() - check if two squares are aligned (same file or rank). * bb_sq_aligned() - check if two squares are aligned (same file or rank).
* @sq1, @sq2: the two squares. * @sq1, @sq2: the two squares.
@@ -219,7 +256,7 @@ static __always_inline bool bb_sq_aligned3(square_t sq1, square_t sq2, square_t
* @sq1: square 1 * @sq1: square 1
* @sq2: square 2 * @sq2: square 2
* *
* @return: bitboard of @betw if between @sq1 and @sq2. * @return: bitboard of @sq if between @sq1 and @sq2.
*/ */
static __always_inline bitboard_t bb_sq_between(square_t sq, square_t sq1, square_t sq2) static __always_inline bitboard_t bb_sq_between(square_t sq, square_t sq1, square_t sq2)
{ {
@@ -260,7 +297,6 @@ static __always_inline bitboard_t shift_nw(const bitboard_t bb)
return (bb & ~FILE_Abb) << NORTH_WEST; return (bb & ~FILE_Abb) << NORTH_WEST;
} }
#define pawn_up_value(c) ((c) == WHITE ? 8: -8)
/* pawn moves/attacks (for bitboards) */ /* pawn moves/attacks (for bitboards) */
#define pawn_shift_up(bb, c) ((c) == WHITE ? shift_n(bb): shift_s(bb)) #define pawn_shift_up(bb, c) ((c) == WHITE ? shift_n(bb): shift_s(bb))
#define pawn_shift_upleft(bb, c) ((c) == WHITE ? shift_nw(bb): shift_se(bb)) #define pawn_shift_upleft(bb, c) ((c) == WHITE ? shift_nw(bb): shift_se(bb))

View File

@@ -134,5 +134,40 @@ typedef enum {
NORTH_WEST = (NORTH + WEST), NORTH_WEST = (NORTH + WEST),
} dir_t; } dir_t;
/* define diff for relative squares */
#define sq_up(c) ((c) == WHITE ? NORTH: SOUTH)
#define sq_upleft(c) ((c) == WHITE ? NORTH_WEST: SOUTH_EAST)
#define sq_upright(c) ((c) == WHITE ? NORTH_EAST: SOUTH_WEST)
#include <time.h>
typedef struct mclock {
clockid_t clocktype;
ulong elapsed_l;
double elapsed_f;
struct timespec start;
} mclock_t;
#define CLOCK_WALL CLOCK_REALTIME
#define CLOCK_SYSTEM CLOCK_MONOTONIC_RAW
#define CLOCK_PROCESS CLOCK_PROCESS_CPUTIME_ID
#define CLOCK_THREAD CLOCK_THREAD_CPUTIME_ID
/**
* CLOCK_DEFINE - define a clock type.
* @name: clock name
* @type: clock type
*
* This macro is equivalent to:
* mclock_t name;
* clock_init(&name, type);
*/
#define CLOCK_DEFINE(name, type) struct mclock name = { .clocktype = type }
void clock_init(mclock_t *clock, clockid_t type);
void clock_start(mclock_t *clock);
s64 clock_elapsed_μs(mclock_t *clock);
s64 clock_elapsed_ms(mclock_t *clock);
double clock_elapsed_sec(mclock_t *clock);
#endif /* _CHESSDEFS_H */ #endif /* _CHESSDEFS_H */

112
src/misc.c Normal file
View File

@@ -0,0 +1,112 @@
/* misc.c - generic/catchall functions.
*
* 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 <time.h>
#include "chessdefs.h"
/*
* 1 sec = 1000 millisec
* 1 millisec = 1000 microsec
* 1 microsec = 1000 nanosec
* milli = sec * 1000 + nanosec / 1000000
*
*/
/* We use microsec for all intermediate calcluation */
#define NANO_IN_MICRO 1000ll /* nanosecond in millisecond */
#define MICRO_IN_SEC 1000000ll /* millisecond in second */
#define MILLI_IN_SEC 1000ll /* millisecond in second */
#define MICRO_IN_MILLI 1000ll
//#define NANO_IN_MILLI 1000000ll /* nanosecond in millisecond */
//#define NANO_IN_SEC (NANOS_IN_MS * MS_IN_SEC)
/**
* clock_start - start or restart a clock.
* @clock: &mclock_t clock
*
* Save current time according to @clock type.
*/
void clock_start(mclock_t *clock)
{
clock_gettime(clock->clocktype, &clock->start);
}
/**
* clock_init - initializes a clock type.
* @clock: &mclock_t clock
* @type: clock type
*
* See the clock_gettime(2) for details.
* CLOCK_WALL (a.k.a CLOCK_REALTIME): Wall clock.
* CLOCK_SYSTEM (a.k.a CLOCK_MONOTONIC_RAW): System clock.
* CLOCK_PROCESS (a.k.a CLOCK_PROCESS_CPUTIME_ID): Process CPU clock (incl. threads).
* CLOCK_THREAD (a.k.a CLOCK_THREAD_CPUTIME_ID): Thread CPU clock.
*/
void clock_init(mclock_t *clock, clockid_t type)
{
clock->clocktype = type;
clock_start(clock);
}
/**
* clock_elapsed_μs - return a mclock_t elapsed time in microseconds.
* @clock: &mclock_t clock
*
* The elapsed time is calculated between current time and last clock_start(@clock)
* call time.
*
* @return: microseconds elapsed since last clock_start().
*/
s64 clock_elapsed_μs(mclock_t *clock)
{
struct timespec current;
s64 μs;
clock_gettime(clock->clocktype, &current);
μs = ((s64)current.tv_sec - (s64)clock->start.tv_sec) * MICRO_IN_SEC +
((s64)current.tv_nsec - (s64)clock->start.tv_nsec) / NANO_IN_MICRO ;
return μs;
}
/**
* clock_elapsed_ms - return a mclock_t elapsed time in milliseconds.
* @clock: &mclock_t clock
*
* The elapsed time is calculated between current time and last clock_start(@clock)
* call time.
*
* @return: milliseconds elapsed since last clock_start().
*/
s64 clock_elapsed_ms(mclock_t *clock)
{
return clock_elapsed_μs(clock) / MICRO_IN_MILLI;
}
/**
* clock_elapsed_sec - return a mclock_t elapsed time in seconds.
* @clock: &mclock_t clock
*
* The elapsed time is calculated between current time and last clock_start(@clock)
* call time.
*
* @return: seconds elapsed since last clock_start().
*/
double clock_elapsed_sec(mclock_t *clock)
{
return (double) clock_elapsed_μs(clock) / (double) MICRO_IN_SEC;
}

View File

@@ -1,6 +1,6 @@
/* move-do.h - move do/undo. /* move-do.h - move do/undo.
* *
* Copyright (C) 2021 Bruno Raoult ("br") * Copyright (C) 2021-2024 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later. * Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING. * Some rights reserved. See COPYING.
* *

View File

@@ -36,18 +36,18 @@
*/ */
bool pseudo_is_legal(const pos_t *pos, const move_t move) bool pseudo_is_legal(const pos_t *pos, const move_t move)
{ {
color_t us = pos->turn, them = OPPONENT(us); color_t us = pos->turn, them = OPPONENT(us);
square_t from = move_from(move), to = move_to(move), king = pos->king[us], sq; square_t from = move_from(move), to = move_to(move);
piece_type_t piece = PIECE(pos->board[from]); square_t king = pos->king[us];
bitboard_t kingbb = pos->bb[us][KING], tmp; bitboard_t kingbb = pos->bb[us][KING];
u64 pinned = mask(from) & pos->blockers & pos->bb[us][ALL_PIECES]; u64 pinned = mask(from) & pos->blockers;
/* (1) - King /* (1) - King
* For castling, we already tested intermediate squares attacks * For castling, we already tested intermediate squares attacks
* in pseudo move generation, so we only care destination square here. * in pseudo move generation, so we only care destination square here.
* Attention: We need to exclude king from occupation bitboard ! * Attention: We need to exclude king from occupation bitboard !
*/ */
if (piece == KING) { if (from == king) {
bitboard_t occ = pos_occ(pos) ^ kingbb; bitboard_t occ = pos_occ(pos) ^ kingbb;
return !sq_attackers(pos, occ, to, them); return !sq_attackers(pos, occ, to, them);
} }
@@ -62,17 +62,16 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move)
* pinned piece: always illegal * pinned piece: always illegal
*/ */
if (pos->checkers) { if (pos->checkers) {
if (popcount64(pos->checkers) == 1) { /* one checker */ if (pinned)
square_t checker = ctz64(pos->checkers); return false;
if (pinned) if (bb_multiple(pos->checkers))
return false; return false;
if (is_enpassant(move)) { square_t checker = ctz64(pos->checkers);
return pawn_push_up(pos->en_passant, them) == checker; if (is_enpassant(move)) {
} return pawn_push_up(pos->en_passant, them) == checker;
bitboard_t between = bb_between[king][checker] | pos->checkers;
return mask(to) & between;
} }
return false; /* double check */ bitboard_t between = bb_between[king][checker] | pos->checkers;
return mask(to) & between;
} }
/* (3) - pinned pieces /* (3) - pinned pieces
@@ -85,27 +84,24 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move)
} }
/* (4) - En-passant /* (4) - En-passant
* We only care the situation where our King and enemy R/Q are on * pinned pieces are already handled.
* 5th relative rank. To do so, we create an occupation bb without * One case is left: when the two "disappearing" pawns would discover
* the 2 pawns. * a R/Q check.
*/ */
if (is_enpassant(move)) { if (is_enpassant(move)) {
/* from rank bitboard */ bitboard_t rank5 = us == WHITE? RANK_5bb: RANK_4bb;
bitboard_t rank5 = bb_sqrank[from];
/* enemy rooks/queens on from rank */
bitboard_t rooks = (pos->bb[them][ROOK] | pos->bb[them][QUEEN]) & rank5;
if ((kingbb & rank5) && rooks) { /* K and enemy R/Q on rank */ if ((pos->bb[us][KING] & rank5)) {
/* captured pawn square (beside from square) */ bitboard_t exclude = mask(pos->en_passant + sq_up(them)) | mask(from);
square_t captured = sq_make(sq_file(pos->en_passant), sq_rank(from)); bitboard_t rooks = (pos->bb[them][ROOK] | pos->bb[them][QUEEN]) & rank5;
/* occupation bitboard without the two "disappearing" pawns */
bitboard_t occ = pos_occ(pos) ^ mask(from) ^ mask(captured);
bit_for_each64(sq, tmp, rooks) /* check all rooks/queens */ bitboard_t occ = pos_occ(pos) ^ exclude;
if (hyperbola_rank_moves(occ, sq) & kingbb) while (rooks) {
square_t rook = bb_next(&rooks);
if (hyperbola_rank_moves(occ, rook) & kingbb)
return false; return false;
}
} }
return true;
} }
return true; return true;
} }
@@ -156,6 +152,31 @@ movelist_t *pos_all_legal(const pos_t *pos, movelist_t *movelist, movelist_t *de
return dest; return dest;
} }
/**
* pos_gen_check_pseudomoves() - generate position pseudo-legal moves when in check
* @pos: position
* @movelist: &movelist_t array to store pseudo-moves
*
* Generate all @pos pseudo moves for player-to-move.
* @movelist is filled with the moves.
*
* Only a few validity checks are done here (i.e. moves are not generated):
* - castling, if king is in check
* - castling, if king passes an enemy-controlled square (not final square).
* When immediately known, a few move flags are also applied in these cases:
* - castling: M_CASTLE_{K,Q}
* - capture (excl. en-passant): M_CAPTURE
* - en-passant: M_EN_PASSANT
* - pawn double push: M_DPUSH
* - promotion: M_PROMOTION
* - promotion and capture
*
* TODO: move code to specific functions (especially castling, pawn push/capture)
*
* @Return: The total number of moves.
*/
/** /**
* pos_gen_pseudomoves() - generate position pseudo-legal moves * pos_gen_pseudomoves() - generate position pseudo-legal moves
* @pos: position * @pos: position

View File

@@ -1,6 +1,6 @@
/* move.c - move management. /* move.c - move management.
* *
* Copyright (C) 2021 Bruno Raoult ("br") * Copyright (C) 2021-2024 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later. * Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING. * Some rights reserved. See COPYING.
* *

View File

@@ -1,6 +1,6 @@
/* move.h - move management. /* move.h - move management.
* *
* Copyright (C) 2021 Bruno Raoult ("br") * Copyright (C) 2021-2024 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later. * Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING. * Some rights reserved. See COPYING.
* *

View File

@@ -24,6 +24,7 @@
#include "chessdefs.h" #include "chessdefs.h"
#include "position.h" #include "position.h"
#include "bitboard.h" #include "bitboard.h"
#include "hyperbola-quintessence.h"
#include "fen.h" #include "fen.h"
#include "piece.h" #include "piece.h"
#include "util.h" #include "util.h"
@@ -194,17 +195,83 @@ bitboard_t pos_checkers(const pos_t *pos, const color_t color)
* It should be faster than @pos_checkers + @pos_set_pinners_blockers, as * It should be faster than @pos_checkers + @pos_set_pinners_blockers, as
* some calculation will be done once. * some calculation will be done once.
*/ */
/* void pos_set_checkers_pinners_blockers(pos_t *pos)
* void pos_set_checkers_pinners_blockers(pos_t *pos) {
* { int us = pos->turn, them = OPPONENT(us);
* bitboard_t b_bb = pos->bb[WHITE][BISHOP] | pos->bb[BLACK][BISHOP]; bitboard_t occ = pos_occ(pos);
* bitboard_t r_bb = pos->bb[WHITE][ROOK] | pos->bb[BLACK][ROOK]; bitboard_t attackers;
* bitboard_t q_bb = pos->bb[WHITE][QUEEN] | pos->bb[BLACK][QUEEN]; bitboard_t checkers = 0, blockers = 0, pinners = 0;
* bitboard_t targets, tmpcheckers, tmpblockers, tmppinners, tmpbb;
* /\* find out first piece on every diagonal *\/ square_t king = pos->king[us];
* bitboard_t king_bb = mask(king);
* } int pinner;
*/
/* bishop type - we attack with a bishop from king position */
attackers = pos->bb[them][BISHOP] | pos->bb[them][QUEEN];
/* targets is all "target" pieces if K was a bishop */
targets = hyperbola_bishop_moves(occ, king) & occ;
/* checkers = only opponent B/Q */
tmpcheckers = targets & attackers;
checkers |= tmpcheckers;
/* maybe blockers = not checkers */
tmpblockers = targets & ~tmpcheckers;
/* we find second targets, by removing only first ones (excl. checkers) */
targets = hyperbola_bishop_moves(occ ^ tmpblockers, king) ^ tmpcheckers;
/* pinners = only B/Q */
tmppinners = targets & attackers;
pinners |= tmppinners;
//tmpblockers = 0;
/* blockers = we find occupied squares between pinner and king */
bit_for_each64(pinner, tmpbb, tmppinners)
blockers |= bb_between[pinner][king] & tmpblockers;
//blockers |= tmpblockers;
/* same for rook type */
attackers = pos->bb[them][ROOK] | pos->bb[them][QUEEN];
/* targets is all "target" pieces if K was a bishop */
targets = hyperbola_rook_moves(occ, king) & occ;
/* checkers = only opponent B/Q */
tmpcheckers = targets & attackers;
checkers |= tmpcheckers;
/* maybe blockers = not checkers */
tmpblockers = targets & ~tmpcheckers;
/* we find second targets, by removing only first ones (excl. checkers) */
targets = hyperbola_rook_moves(occ ^ tmpblockers, king) ^ tmpcheckers;
/* pinners = only B/Q */
tmppinners = targets & attackers;
pinners |= tmppinners;
//tmpblockers = 0;
/* blockers = we find occupied squares between pinner and king */
bit_for_each64(pinner, tmpbb, tmppinners)
blockers |= bb_between[pinner][king] & tmpblockers;
//blockers |= tmpblockers;
/* pawns */
attackers = pos->bb[them][PAWN];
targets = pawn_shift_upleft(king_bb, us) | pawn_shift_upright(king_bb, us);
checkers |= targets & attackers;
/* knight */
attackers = pos->bb[them][KNIGHT];
targets = bb_knight_moves(attackers, king);
checkers |= targets;
pos->checkers = checkers;
pos->pinners = pinners;
pos->blockers = blockers;
}
/** /**
* pos_set_pinners_blockers() - set position pinners and blockers. * pos_set_pinners_blockers() - set position pinners and blockers.

View File

@@ -159,7 +159,7 @@ void pos_del(pos_t *pos);
pos_t *pos_clear(pos_t *pos); pos_t *pos_clear(pos_t *pos);
bool pos_cmp(const pos_t *pos1, const pos_t *pos2); bool pos_cmp(const pos_t *pos1, const pos_t *pos2);
//bitboard_t set_king_pinners_blockers(pos_t *pos); void pos_set_checkers_pinners_blockers(pos_t *pos);
void pos_set_pinners_blockers(pos_t *pos); void pos_set_pinners_blockers(pos_t *pos);
bitboard_t pos_checkers(const pos_t *pos, const color_t color); bitboard_t pos_checkers(const pos_t *pos, const color_t color);
bitboard_t pos_king_pinners(const pos_t *pos, const color_t color); bitboard_t pos_king_pinners(const pos_t *pos, const color_t color);

View File

@@ -1,4 +1,4 @@
/* search.c - search good moves. /* search.c - perft + search.
* *
* Copyright (C) 2023-2024 Bruno Raoult ("br") * Copyright (C) 2023-2024 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later. * Licensed under the GNU General Public License v3.0 or later.
@@ -13,7 +13,7 @@
#include <stdio.h> #include <stdio.h>
#include "brlib.h" #include <brlib.h>
#include "position.h" #include "position.h"
#include "move-gen.h" #include "move-gen.h"
@@ -21,19 +21,25 @@
#include "search.h" #include "search.h"
#include "attack.h" #include "attack.h"
//#include "move.h"
//#include "eval.h"
/** /**
* perft() - Perform perft on position * perft() - Perform perft on position
* @pos: &position to search * @pos: &position to search
* @depth: Wanted depth. * @depth: Wanted depth.
* @ply: Depth level where perft is consolidated. * @ply: perft depth level.
* *
* Print perftCalculate the negamax value of @pos. This is an extensive search, with * Run perft on a position. This function displays the available moves at @depth
* absolutely no cutoff. * level for each possible first move, and the total of moves.
* *
* @return: The @pos negamax evaluation. * This version uses the algorithm:
* if last depth
* return 1;
* gen pseudo-legal moves
* loop for each legal move
* do-move
* perft (depth -1)
* undo-move
*
* @return: total moves found at @depth level.
*/ */
u64 perft(pos_t *pos, int depth, int ply) u64 perft(pos_t *pos, int depth, int ply)
{ {
@@ -63,10 +69,29 @@ u64 perft(pos_t *pos, int depth, int ply)
} }
if (ply == 1) if (ply == 1)
printf("\nTotal: %lu\n", nodes); printf("Total: %lu\n", nodes);
return nodes; return nodes;
} }
/**
* perft2() - Perform perft on position
* @pos: &position to search
* @depth: Wanted depth.
* @ply: perft depth level.
*
* Run perft on a position. This function displays the available moves at @depth
* level for each possible first move, and the total of moves.
*
* This version uses the algorithm:
* if (king in check)
* finish;
* if last depth
* return 1;
* gen pseudo-legal moves
* foreach pseudo-legal move...
*
* @return: total moves found at @depth level.
*/
u64 perft2(pos_t *pos, int depth, int ply) u64 perft2(pos_t *pos, int depth, int ply)
{ {
int subnodes, nmove = 0; int subnodes, nmove = 0;
@@ -75,8 +100,6 @@ u64 perft2(pos_t *pos, int depth, int ply)
move_t move; move_t move;
state_t state; state_t state;
if (is_in_check(pos, OPPONENT(pos->turn)))
return 0;
if (depth == 0) if (depth == 0)
return 1; return 1;
pos->checkers = pos_checkers(pos, pos->turn); pos->checkers = pos_checkers(pos, pos->turn);
@@ -88,19 +111,50 @@ u64 perft2(pos_t *pos, int depth, int ply)
for (nmove = 0; nmove < pseudo.nmoves; ++nmove ) { for (nmove = 0; nmove < pseudo.nmoves; ++nmove ) {
move = pseudo.move[nmove]; move = pseudo.move[nmove];
move_do(pos, move); move_do(pos, move);
//if (!is_in_check(pos, OPPONENT(pos->turn))) { if (!is_in_check(pos, OPPONENT(pos->turn))) {
subnodes = perft2(pos, depth - 1, ply + 1); subnodes = perft2(pos, depth - 1, ply + 1);
nodes += subnodes; nodes += subnodes;
if (ply == 1) { if (ply == 1) {
char movestr[8]; char movestr[8];
printf("%s: %d\n", move_str(movestr, move, 0), subnodes); printf("%s: %d\n", move_str(movestr, move, 0), subnodes);
}
} }
//}
move_undo(pos, move); move_undo(pos, move);
pos->state = state; pos->state = state;
} }
if (ply == 1) if (ply == 1)
printf("\nTotal: %lu\n", nodes); printf("Total: %lu\n", nodes);
return nodes;
}
u64 perft_new_pinners(pos_t *pos, int depth, int ply)
{
int subnodes, movetmp = 0;
u64 nodes = 0;
movelist_t pseudo = { .nmoves = 0 };
move_t move;
state_t state;
if (depth == 0)
return 1;
pos_set_checkers_pinners_blockers(pos);
state = pos->state;
pos_gen_pseudomoves(pos, &pseudo);
while ((move = pos_next_legal(pos, &pseudo, &movetmp)) != MOVE_NONE) {
move_do(pos, move);
subnodes = perft(pos, depth - 1, ply + 1);
if (ply == 1) {
char movestr[8];
printf("%s: %d\n", move_str(movestr, move, 0), subnodes);
}
nodes += subnodes;
move_undo(pos, move);
pos->state = state;
}
if (ply == 1)
printf("Total: %lu\n", nodes);
return nodes; return nodes;
} }

View File

@@ -21,5 +21,6 @@
u64 perft(pos_t *pos, int depth, int ply); u64 perft(pos_t *pos, int depth, int ply);
u64 perft2(pos_t *pos, int depth, int ply); u64 perft2(pos_t *pos, int depth, int ply);
u64 perft_new_pinners(pos_t *pos, int depth, int ply);
#endif /* SEARCH_H */ #endif /* SEARCH_H */

View File

@@ -28,6 +28,7 @@ int main(int __unused ac, __unused char**av)
int i = 0; int i = 0;
char *fen; char *fen;
pos_t *pos;//, *fishpos = pos_new(); pos_t *pos;//, *fishpos = pos_new();
bitboard_t checkers, pinners, blockers;
setlinebuf(stdout); /* line-buffered stdout */ setlinebuf(stdout); /* line-buffered stdout */
@@ -41,8 +42,22 @@ int main(int __unused ac, __unused char**av)
printf("wrong fen %d: [%s]\n", i, fen); printf("wrong fen %d: [%s]\n", i, fen);
continue; continue;
} }
pos->checkers = pos_checkers(pos, pos->turn);
pos_set_pinners_blockers(pos);
pos_print(pos); pos_print(pos);
checkers = pos->checkers;
pinners = pos->pinners;
blockers = pos->blockers;
pos_set_checkers_pinners_blockers(pos);
printf("******* %s\n", cur_comment());
bb_print_multi("checkers", 2, checkers, pos->checkers);
bb_print_multi("pinners", 2, pinners, pos->pinners);
bb_print_multi("blockers", 2, blockers, pos->blockers);
pos_del(pos); pos_del(pos);
i++; i++;
} }

View File

@@ -237,6 +237,8 @@ int main(int __unused ac, __unused char**av)
} }
/* print movelists */ /* print movelists */
send_stockfish_fen(outfd, fishpos, &fishmoves, fen); send_stockfish_fen(outfd, fishpos, &fishmoves, fen);
pos_set_checkers_pinners_blockers(pos);
pos_gen_pseudomoves(pos, &pseudo); pos_gen_pseudomoves(pos, &pseudo);
//moves_print(&pseudo, 0); //moves_print(&pseudo, 0);
pos_all_legal(pos, &pseudo, &legal); pos_all_legal(pos, &pseudo, &legal);

View File

@@ -233,11 +233,19 @@ int main(int __unused ac, __unused char**av)
//move_t move; //move_t move;
FILE *outfd; FILE *outfd;
int depth = 6; int depth = 6;
s64 ms1 = 0, ms1_total = 0;
s64 ms2 = 0, ms2_total = 0;
s64 ms3 = 0, ms3_total = 0;
int run = 7;
if (ac > 1) if (ac > 1)
depth = atoi(av[1]); depth = atoi(av[1]);
printf("depth = %d\n", depth); if (ac > 2)
run = atoi(av[2]);
printf("depth = %d run=%d\n", depth, run);
if (!run)
exit(0);
setlocale(LC_NUMERIC, ""); setlocale(LC_NUMERIC, "");
setlinebuf(stdout); /* line-buffered stdout */ setlinebuf(stdout); /* line-buffered stdout */
outfd = open_stockfish(); outfd = open_stockfish();
@@ -245,6 +253,8 @@ int main(int __unused ac, __unused char**av)
bitboard_init(); bitboard_init();
hyperbola_init(); hyperbola_init();
CLOCK_DEFINE(clock, CLOCK_PROCESS);
while ((fen = next_fen(PERFT | MOVEDO))) { while ((fen = next_fen(PERFT | MOVEDO))) {
test_line = cur_line(); test_line = cur_line();
if (!(pos = fen2pos(NULL, fen))) { if (!(pos = fen2pos(NULL, fen))) {
@@ -256,48 +266,66 @@ int main(int __unused ac, __unused char**av)
savepos = pos_dup(pos); savepos = pos_dup(pos);
//int j = 0; //int j = 0;
#define NANOSEC 1000000000 /* nano sec in sec */ if (run & 1) {
#define MILLISEC 1000000 /* milli sec in sec */ clock_start(&clock);
my_count = perft(pos, depth, 1);
ms1 = clock_elapsed_ms(&clock);
ms1_total += ms1;
// 1 sec = 1000 millisec if (sf_count == my_count) {
// 1 millisec = 1000 microsec printf("pt1 OK : line=%3d perft=%lu %'ldms lps=%'lu \"%s\"\n",
// 1 microsec = 1000 nanosec test_line, my_count, ms1,
// milli = sec * 1000 + nanosec / 1000000 ms1? my_count*1000l/ms1: 0,
struct timespec ts1, ts2; fen);
s64 microsecs; } else {
printf("pt1 ERR: line=%3d sf=%lu me=%lu \"%s\"\n",
clock_gettime(CLOCK_MONOTONIC_RAW, &ts1); test_line, sf_count, my_count, fen);
my_count = perft(pos, depth, 1); }
clock_gettime(CLOCK_MONOTONIC_RAW, &ts2);
microsecs = ((s64)ts2.tv_sec - (s64)ts1.tv_sec) * 1000000l
+ ((s64)ts2.tv_nsec - (s64)ts1.tv_nsec) / 1000l ;
if (sf_count == my_count) {
printf("pt1 OK : line=%03d perft=%lu ms=%'ldms lps=%'lu \"%s\"\n",
test_line, my_count, microsecs/1000l,
my_count*1000000l/microsecs, fen);
} else {
printf("pt1 ERR: line=%03d sf=%lu me=%lu \"%s\"\n",
test_line, sf_count, my_count, fen);
} }
clock_gettime(CLOCK_MONOTONIC_RAW, &ts1); if (run & 2) {
my_count = perft2(pos, depth, 1); clock_start(&clock);
clock_gettime(CLOCK_MONOTONIC_RAW, &ts2); my_count = perft2(pos, depth, 1);
ms2 = clock_elapsed_ms(&clock);
ms2_total += ms2;
microsecs = ((s64)ts2.tv_sec - (s64)ts1.tv_sec) * 1000000l if (sf_count == my_count) {
+ ((s64)ts2.tv_nsec - (s64)ts1.tv_nsec) / 1000l ; printf("pt2 OK : line=%3d perft=%lu %'ldms lps=%'lu \"%s\"\n",
if (sf_count == my_count) { test_line, my_count, ms2,
printf("pt2 OK : line=%03d perft=%lu ms=%'ldms lps=%'lu \"%s\"\n\n", ms2? my_count*1000l/ms2: 0,
test_line, my_count, microsecs/1000l, fen);
my_count*1000000l/microsecs, fen); } else {
} else { printf("pt2 ERR: line=%3d sf=%lu me=%lu \"%s\"\n",
printf("pt2 ERR: line=%03d sf=%lu me=%lu \"%s\"\n\n", test_line, sf_count, my_count, fen);
test_line, sf_count, my_count, fen); }
} }
if (run & 4) {
clock_start(&clock);
my_count = perft_new_pinners(pos, depth, 1);
ms3 = clock_elapsed_ms(&clock);
ms3_total += ms3;
if (sf_count == my_count) {
printf("pt3 OK : line=%3d perft=%lu %'ldms lps=%'lu \"%s\"\n",
test_line, my_count, ms3,
ms3? my_count*1000l/ms3: 0,
fen);
} else {
printf("pt3 ERR: line=%3d sf=%lu me=%lu \"%s\"\n",
test_line, sf_count, my_count, fen);
}
}
printf("\n");
pos_del(savepos); pos_del(savepos);
pos_del(pos); pos_del(pos);
i++; i++;
} }
if (run & 1)
printf("total perft %'ldms\n", ms1_total);
if (run & 2)
printf("total perft2 %'ldms\n", ms2_total);
if (run & 4)
printf("total perft3 %'ldms\n", ms3_total);
return 0; return 0;
} }