From 403e625cbe85cc9089b014e78e2e74b661914761 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Thu, 15 Feb 2024 10:15:13 +0100 Subject: [PATCH] add hyperbola-quintessence.[ch]}, rank move gen, + file/rook/queen --- src/bitboard.c | 127 ++++++++++++++++++----------------- src/bitboard.h | 50 +++++++++++--- src/board.h | 73 ++++++-------------- src/brchess.c | 2 +- src/hyperbola-quintessence.c | 126 ++++++++++++++++++++++++++++++++++ src/hyperbola-quintessence.h | 19 ++++++ test/bitboard-test.c | 37 +++++----- test/fen-test.c | 2 +- 8 files changed, 297 insertions(+), 139 deletions(-) create mode 100644 src/hyperbola-quintessence.c create mode 100644 src/hyperbola-quintessence.h diff --git a/src/bitboard.c b/src/bitboard.c index 4003729..75f482a 100644 --- a/src/bitboard.c +++ b/src/bitboard.c @@ -17,6 +17,7 @@ #include "brlib.h" #include "chessdefs.h" +#include "piece.h" #include "board.h" #include "bitboard.h" @@ -31,90 +32,78 @@ static int king_vector[8] = { NORTH, NORTH_EAST, EAST, SOUTH_EAST, SOUTH, SOUTH_WEST, WEST, NORTH_WEST }; -static int bishop_vector[4] = { - NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST -}; -static int rook_vector[4] = { - NORTH, EAST, SOUTH, WEST -}; -bitboard_t sq_bb[SQUARE_MAX]; -bitboard_t sq_bbrank[64], sq_bbfile[64], sq_bbdiag[64], sq_bbanti[64]; +bitboard_t bb_sq[SQUARE_MAX]; +bitboard_t bb_rank[64], bb_file[64], bb_diagonal[64], bb_antidiagonal[64]; -bitboard_t knight_attacks[64], king_attacks[64]; -bitboard_t pawn_attacks[2]; - -/* we will create only dest squares for A1-D4 square, then flip - */ +bitboard_t bb_knight[64], bb_king[64]; +bitboard_t bb_pawn_push[2][64], bb_bpawn_attack[2][64], bb_pawn_ep[2][64]; /** - * raw_bitboard_print() - print simple bitboard representation - * @bb: the bitboard - * @tit: a string or NULL + * bitboard_init() - initialize general bitboards + * + * Generate the following bitboards : + * bb_sq[64]: square to bitboard + * bb_rank[64]: square to rank + * bb_file[64]: square to file + * bb_diagonal[64]: square to diagonal + * bb_antidiagonal[64]: square to antidiagonal + * + * And the following pseudo move masks: + * bb_knight[64]: knight moves + * bb_king[64]: king moves + * bb_pawn[2][64]: white pawn moves (not attacks) + * bb_pawn_att[2][64]: white pawn attacks */ void bitboard_init(void) { - struct { - union { - struct { - bitboard_t diag; - bitboard_t anti; - bitboard_t vert; - bitboard_t hori; - }; - bitboard_t all[4]; - }; - } tmpmasks[64] = {0}; - struct { - int df, dr; - } dirs[4] = { + /* for each square, the 4 masks: file, rank, diagonal, antidiagonal */ + struct { int df, dr; } dirs[4] = { { 0, 1 }, /* vertical/file */ { 1, 0 }, /* horizontal/rank */ { 1, 1 }, /* diagonal */ { 1, -1 }, /* antidiagonal */ } ; + bitboard_t tmpbb[64][4] = { 0 }; - square_t sq; - int dst, dst_f, dst_r; + /* 1) square to bitboard */ + for (square_t sq = A1; sq <= H8; ++sq) + bb_sq[sq] = mask(sq); - /* square to bitboard */ - for (sq = A1; sq <= H8; ++sq) - sq_bb[sq] = mask(sq); - - /* square to rank/file/diagonal/antidiagonal */ - for (sq = 0; sq < 64; ++sq) { + /* 2) square to rank/file/diagonal/antidiagonal */ + for (square_t sq = 0; sq < 64; ++sq) { int r = sq_rank(sq), f = sq_file(sq); for (int mult = -7; mult < 8; ++mult) { for (int dir = 0; dir < 4; ++dir) { - dst_f = f + mult * dirs[dir].df; - dst_r = r + mult * dirs[dir].dr; + int dst_f = f + mult * dirs[dir].df; + int dst_r = r + mult * dirs[dir].dr; if (sq_coord_ok(dst_f) && sq_coord_ok(dst_r)) { - dst = sq_make(dst_f, dst_r); - tmpmasks[sq].all[dir] |= mask(dst); + int dst = sq_make(dst_f, dst_r); + tmpbb[sq][dir] |= mask(dst); } } } } - for (sq = 0; sq < 64; ++sq) { - sq_bbfile[sq] = tmpmasks[sq].all[0]; - sq_bbrank[sq] = tmpmasks[sq].all[1]; - sq_bbdiag[sq] = tmpmasks[sq].all[2]; - sq_bbanti[sq] = tmpmasks[sq].all[3]; + for (square_t sq = 0; sq < 64; ++sq) { + bb_file[sq] = tmpbb[sq][0]; + bb_rank[sq] = tmpbb[sq][1]; + bb_diagonal[sq] = tmpbb[sq][2]; + bb_antidiagonal[sq] = tmpbb[sq][3]; } - /* knight and king attacks */ - for (sq = A1; sq <= H8; ++sq) { + /* 3) knight and king moves */ + for (square_t sq = A1; sq <= H8; ++sq) { for (int vec = 0; vec < 8; ++vec) { - dst = sq + knight_vector[vec]; + int dst = sq + knight_vector[vec]; if (sq_ok(dst)) { if (sq_dist(dst, sq) == 2) { - knight_attacks[sq] |= sq_bb[dst]; + bb_knight[sq] |= bb_sq[dst]; } } dst = sq + king_vector[vec]; if (sq_ok(dst)) { if (sq_dist(dst, sq) == 1) { - king_attacks[sq] |= sq_bb[dst]; + bb_king[sq] |= bb_sq[dst]; } } } @@ -134,7 +123,7 @@ void bitboard_print(const char *title, const bitboard_t bitboard) for (rank_t r = RANK_8; r >= RANK_1; --r) { printf("%d ", r + 1); for (file_t f = FILE_A; f <= FILE_H; ++f) { - printf(" %c", bitboard & sq_bb[sq_make(f, r)] ? 'X': '.'); + printf(" %c", bitboard & bb_sq[sq_make(f, r)] ? 'X': '.'); } printf("\n"); } @@ -150,35 +139,51 @@ void bitboard_print(const char *title, const bitboard_t bitboard) * * @n is the number of bitboards to print. If @n -s > 8, it is reduced to 8; */ -void bitboard_print_multi(const char *title, const int n, ...) +void bitboard_print_multi(const char *title, int n, ...) { - bitboard_t bb[16]; - int i; + bitboard_t bb[8]; va_list ap; + n = min(n, 8); + va_start(ap, n); - for (i = 0; i < n; ++i) { + for (int i = 0; i < n; ++i) { /* save all bitboards */ bb[i] = va_arg(ap, bitboard_t); } va_end(ap); - //char c = p? p: 'X'; + if (title) printf("%s\n", title); for (rank_t r = RANK_8; r >= RANK_1; --r) { - for (i = 0; i < n; ++i) { + for (int i = 0; i < n; ++i) { printf("%d ", r + 1); for (file_t f = FILE_A; f <= FILE_H; ++f) { - printf(" %c", bb[i] & sq_bb[sq_make(f, r)] ? 'X': '.'); + printf(" %c", bb[i] & bb_sq[sq_make(f, r)] ? 'X': '.'); } printf(" "); } printf("\n"); } - for (i = 0; i < n; ++i) { + for (int i = 0; i < n; ++i) { printf(" a b c d e f g h"); printf(" "); } printf("\n"); return; } + +/** + * bitboard_rank_sprint() - print an u8 rank binary representation + * @str: the destination string + * @bb8: the uchar to print + */ +char *bitboard_rank_sprint(char *str, const uchar bb8) +{ + for (file_t f = FILE_A; f <= FILE_H; ++f) { + *(str+f) = bb8 & mask(f) ? '1': '.'; + } + //printf(" 0 1 2 3 4 5 6 7\n"); + //printf("\n"); + return str; +} diff --git a/src/bitboard.h b/src/bitboard.h index 346129d..d6d0bd3 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -23,16 +23,15 @@ typedef u64 bitboard_t; /* mapping square -> bitboard */ -extern bitboard_t sq_bb[64]; +extern bitboard_t bb_sq[64]; /* mapping square -> rank/file/diagonal/antidiagonal */ -extern bitboard_t sq_bbrank[64], sq_bbfile[64], sq_bbdiag[64], sq_bbanti[64]; - -extern bitboard_t knight_attacks[64], king_attacks[64]; +extern bitboard_t bb_rank[64], bb_file[64], bb_diagonal[64], bb_antidiagonal[64]; +extern bitboard_t bb_knight[64], bb_king[64]; #define mask(i) ( 1ULL << (i) ) -typedef enum { +enum { FILE_Abb = 0x0101010101010101ULL, FILE_Bbb = 0x0202020202020202ULL, FILE_Cbb = 0x0404040404040404ULL, @@ -41,9 +40,9 @@ typedef enum { FILE_Fbb = 0x2020202020202020ULL, FILE_Gbb = 0x4040404040404040ULL, FILE_Hbb = 0x8080808080808080ULL, -} file_bb; +}; -typedef enum { +enum { RANK_1bb = 0x00000000000000ffULL, RANK_2bb = 0x000000000000ff00ULL, RANK_3bb = 0x0000000000ff0000ULL, @@ -52,7 +51,7 @@ typedef enum { RANK_6bb = 0x0000ff0000000000ULL, RANK_7bb = 0x00ff000000000000ULL, RANK_8bb = 0xff00000000000000ULL -} rank_bb; +}; /* https://www.chessprogramming.org/Flipping_Mirroring_and_Rotating#Rotation */ @@ -61,8 +60,43 @@ static __always_inline bitboard_t bb_rotate_90(bitboard_t b) return b; } +/* TODO: when OK, replace with macros */ +static __always_inline bitboard_t shift_n(const bitboard_t bb) +{ + return bb << NORTH; +} +static __always_inline bitboard_t shift_ne(const bitboard_t bb) +{ + return (bb & ~FILE_Hbb) << NORTH_EAST; +} +static __always_inline bitboard_t shift_e(const bitboard_t bb) +{ + return (bb & ~FILE_Hbb) << EAST; +} +static __always_inline bitboard_t shift_se(const bitboard_t bb) +{ + return (bb & ~FILE_Hbb) >> -SOUTH_EAST; +} +static __always_inline bitboard_t shift_s(const bitboard_t bb) +{ + return bb >> -SOUTH; +} +static __always_inline bitboard_t shift_sw(const bitboard_t bb) +{ + return (bb & ~FILE_Abb) >> -SOUTH_WEST; +} +static __always_inline bitboard_t shift_w(const bitboard_t bb) +{ + return (bb & ~FILE_Abb) >> -WEST; +} +static __always_inline bitboard_t shift_nw(const bitboard_t bb) +{ + return (bb & ~FILE_Abb) << NORTH_WEST; +} + extern void bitboard_init(void); extern void bitboard_print(const char *title, const bitboard_t bitboard); extern void bitboard_print_multi(const char *title, const int n, ...); +extern char *bitboard8_sprint(char *str, const uchar bb8); #endif /* _BITBOARD_H */ diff --git a/src/board.h b/src/board.h index 745ef5d..16fd506 100644 --- a/src/board.h +++ b/src/board.h @@ -18,16 +18,22 @@ #include "chessdefs.h" +/* a square is defined as + * rrrfff + */ +#define SQ_FILEMASK (007) /* warning, octal */ +#define SQ_RANKMASK (070) + typedef enum { _SSQUARE_ = -1, /* force signed enum */ A1 = 0, B1, C1, D1, E1, F1, G1, H1, - A2, B2, C2, D2, E2, F2, G2, H2, - A3, B3, C3, D3, E3, F3, G3, H3, - A4, B4, C4, D4, E4, F4, G4, H4, - A5, B5, C5, D5, E5, F5, G5, H5, - A6, B6, C6, D6, E6, F6, G6, H6, - A7, B7, C7, D7, E7, F7, G7, H7, - A8, B8, C8, D8, E8, F8, G8, H8, + A2, B2, C2, D2, E2, F2, G2, H2, + A3, B3, C3, D3, E3, F3, G3, H3, + A4, B4, C4, D4, E4, F4, G4, H4, + A5, B5, C5, D5, E5, F5, G5, H5, + A6, B6, C6, D6, E6, F6, G6, H6, + A7, B7, C7, D7, E7, F7, G7, H7, + A8, B8, C8, D8, E8, F8, G8, H8, SQUARE_MAX = 64, SQUARE_NONE = 64 } square_t; @@ -71,65 +77,26 @@ static __always_inline square_t sq_make(file_t file, rank_t rank) } static __always_inline file_t sq_file(square_t square) { - return square & 7; + return square & SQ_FILEMASK; } static __always_inline rank_t sq_rank(square_t square) { return square >> 3; } - #define sq_ok(sq) ((sq) >= A1 && (sq) <= H8) #define sq_coord_ok(c) ((c) >= 0 && (c) < 8) -/* Chebyshev distance: https://www.chessprogramming.org/Distance */ +/* Chebyshev distance: max( |r2 - r1|, |f2 - f1| ) + * See: https://www.chessprogramming.org/Distance + */ #define sq_dist(sq1, sq2) (max(abs(sq_file(sq2) - sq_file(sq1)), \ abs(sq_rank(sq2) - sq_rank(sq1)))) -/* Manhattan distance */ +/* Manhattan distance: |r2 - r1| + |f2 - f1| + */ #define sq_manh(sq1, sq2) (abs(sq_file(sq2) - sq_file(sq1)) + \ abs(sq_rank(sq2) - sq_rank(sq1))) -// while the orthogonal Manhattan-Distance is the sum of both absolute rank- and file-distance distances. - -//Dtaxi = |r2 - r1| + |f2 - f1| - -// Chebyshev Distance - -//#include "piece.h" - -/* definitions for 0x88 representation - */ -//#define SQ88(f, r) (((r) << 4) | (f)) /* from rank,file to sq88 */ -//#define F88(s) ((s) & 0x0f) /* from sq88 to file */ -//#define R88(s) ((s) >> 4) /* from sq88 to rank */ - -//#define SETF88(s, r) ((s) &= 0xf0, (s) |= (r)) -//#define SETR88(s, f) ((s) &= 0x0f, (s) |= (f)<<4) - -//#define SQ88_NOK(s) ((s) & 0x88) /* invalid square */ -//#define SQ88_OK(s) (!SQ88_NOK(s)) - -/* definitions for bitboard representation - */ -//#define BB(f, r) (1ULL << (8 * (r) + (f))) /* from rank,file to bitboard */ - - -//#define SQ88_2_BB(s) (BB(F88(s), R88(s))) /* from sq88 to bitboard */ -//#define FILEBB(b) ((b) % 8) /* from sq88 to file */ -//#define RANKBB(b) ((b) / 8) /* from sq88 to rank */ - -//#define SQ88_NOK(s) ((s) & 0x88) /* invalid square */ -//#define SQ88_OK(s) (!SQ88_NOK(s)) - -/* from human to machine - */ -/* - * #define C2FILE(c) (tolower(c) - 'a') - * #define C2RANK(c) (tolower(c) - '1') - * /\* from machine to human - * *\/ - * #define FILE2C(f) ((f) + 'a') - * #define RANK2C(r) ((r) + '1') - */ +extern const char *sq_string(const square_t sq); #endif /* BOARD_H */ diff --git a/src/brchess.c b/src/brchess.c index 2661fdf..9af49e7 100644 --- a/src/brchess.c +++ b/src/brchess.c @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include diff --git a/src/hyperbola-quintessence.c b/src/hyperbola-quintessence.c new file mode 100644 index 0000000..c14b385 --- /dev/null +++ b/src/hyperbola-quintessence.c @@ -0,0 +1,126 @@ +/* hyperbola-quintessence.c - hyperbola quintessence 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include +#include + +#include "brlib.h" + +#include "chessdefs.h" +#include "board.h" +#include "bitboard.h" +#include "hyperbola-quintessence.h" + + +uchar bb_rank_attacks[64 * 8]; + +/** + * hyperbola_init() - init hyperbola quintessence attack bitboards + * + * See: https://www.chessprogramming.org/Kindergarten_Bitboards + * and https://www.chessprogramming.org/Hyperbola_Quintessence + * + * Generate the rank attacks bitboard: + * bb_rank_hyperbola[64 * 8], where: + * - 64 = 2^6 = the occupation of inner 6 bits on rank + * - 8 = file of sliding piece + * + */ +void hyperbola_init() +{ + /* generate rank attacks + */ + for (int occ = 0; occ < 64; ++occ) { + for (int file = 0; file < 8; ++file) { + uchar attacks = 0; + + /* set f left attacks */ + for (int slide = file - 1; slide >= 0; --slide) { + int b = bb_sq[slide]; /* bit to consider */ + attacks |= b; /* add to attack mask */ + if ((occ << 1) & b) /* piece on b, we stop */ + break; + } + /* set f right attacks */ + for (int slide = file + 1; slide < 8; ++slide) { + int b = bb_sq[slide]; + attacks |= b; + if ((occ << 1) & b) + break; + } + bb_rank_attacks[occ * 8 + file] = attacks; + } + } +} + +/** + * hyperbola_moves() - generate hyperbola moves mask for a given sliding piece + * @pieces: occupation bitboard + * @sq: piece square + * @mask: mask considered + * + * See https://www.chessprogramming.org/Hyperbola_Quintessence + * + * @Return: The moves mask for piece + */ +static bitboard_t hyperbola_moves(const bitboard_t pieces, const square_t sq, + const bitboard_t mask) +{ + bitboard_t o = pieces & mask; + bitboard_t r = bswap64(o); + square_t r_sq = FLIP_V(sq); + + return ( (o - mask(sq) ) + ^ bswap64(r - mask(r_sq))) + & mask; + +} + +static bitboard_t hyperbola_rank_moves(bitboard_t occ, square_t sq) +{ + uint32_t rank = sq & SQ_FILEMASK; + uint32_t file = sq & SQ_RANKMASK; + uint64_t o = (occ >> rank) & 126; /* removes bits 0 & 7 */ + + return ((bitboard_t)bb_rank_attacks[o * 4 + file]) << rank; +} + +static bitboard_t hyperbola_file_moves(bitboard_t occ, square_t sq) +{ + return hyperbola_moves(occ, bb_file[sq], sq); +} + +static bitboard_t hyperbola_diagonal_moves(bitboard_t occ, square_t sq) +{ + return hyperbola_moves(occ, bb_diagonal[sq], sq); +} + +static bitboard_t hyperbola_antidiagonal_moves(bitboard_t occ, square_t sq) +{ + return hyperbola_moves(occ, bb_antidiagonal[sq], sq); +} + +bitboard_t hyperbola_bishop_moves(bitboard_t occ, square_t sq) +{ + return hyperbola_diagonal_moves(occ, sq) + hyperbola_antidiagonal_moves(occ, sq); +} + +bitboard_t hyperbola_rook_moves(bitboard_t occ, square_t sq) +{ + return hyperbola_file_moves(occ, sq) + hyperbola_rank_moves(occ, sq); +} + +bitboard_t hyperbola_queen_moves(bitboard_t occ, square_t sq) +{ + return hyperbola_bishop_moves(occ, sq) + hyperbola_rook_moves(occ, sq); +} diff --git a/src/hyperbola-quintessence.h b/src/hyperbola-quintessence.h new file mode 100644 index 0000000..2ee5a20 --- /dev/null +++ b/src/hyperbola-quintessence.h @@ -0,0 +1,19 @@ +/* hyperbola-quintessence.h - hyperbola-quintessence definitions. + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#ifndef _HYPERBOLA_QUINTESSENCE_H +#define _HYPERBOLA_QUINTESSENCE_H + +void hyperbola_init(void); + +#endif /* _HYPERBOLA_QUINTESSENCE_H */ diff --git a/test/bitboard-test.c b/test/bitboard-test.c index a3ce8e1..1bd0fc6 100644 --- a/test/bitboard-test.c +++ b/test/bitboard-test.c @@ -4,28 +4,35 @@ #include #include -#include "../src/bitboard.h" #include "../src/position.h" #include "../src/piece.h" -//#include "../src/fen.h" +#include "../src/bitboard.h" +#include "../src/hyperbola-quintessence.h" int main(int __unused ac, __unused char**av) { - char str[128]; + char str[256]; bitboard_init(); + hyperbola_init(); for (int i = 0; i < 64; ++i) { - char str[128]; - sprintf(str, "\ndiag/antidiag/vert %d", i); - bitboard_print_multi(str, 5, - sq_bbfile[i] | sq_bbrank[i] | sq_bbdiag[i] | sq_bbanti[i], - sq_bbfile[i], sq_bbrank[i], - sq_bbdiag[i], sq_bbanti[i]); - } - for (square_t sq = 0; sq < 64; ++sq) { - sprintf(str, "%2d %#lx %#lx knight", sq, sq_bb[sq], knight_attacks[sq]); - bitboard_print(str, knight_attacks[sq]); - sprintf(str, "%2d %#lx %#lx knight", sq, sq_bb[sq], king_attacks[sq]); - bitboard_print(str, king_attacks[sq]); + sprintf(str, "\n%#x:\n %-22s%-22s%-22s%-22s%-22s%-22s%-22s", i, + "sliding", "diagonal", "antidiagonal", "file", "rank", "knight", + "king" + ); + bitboard_print_multi(str, 7, + bb_file[i] | bb_rank[i] | + bb_diagonal[i] | bb_antidiagonal[i], + bb_diagonal[i], bb_antidiagonal[i], + bb_file[i], bb_rank[i], + bb_knight[i], bb_king[i]); } + /* + * for (square_t sq = 0; sq < 64; ++sq) { + * sprintf(str, "%2d %#lx %#lx knight", sq, bb_sq[sq], bb_knight[sq]); + * bitboard_print(str, bb_knight[sq]); + * sprintf(str, "%2d %#lx %#lx knight", sq, bb_sq[sq], bb_king[sq]); + * bitboard_print(str, bb_king[sq]); + * } + */ return 0; } diff --git a/test/fen-test.c b/test/fen-test.c index 0e868dd..ae24bcd 100644 --- a/test/fen-test.c +++ b/test/fen-test.c @@ -12,7 +12,7 @@ int main(int ac, char**av) char revfen[128]; int comp; - debug_init(5, stderr, true); + //debug_init(5, stderr, true); //pos_pool_init(); pos = pos_new(); if (ac == 1) {