From d5906b1fb9808e61aa197e1f55077caf3490713a Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Sun, 11 Feb 2024 20:47:09 +0100 Subject: [PATCH] start bitboard init (see commit details) - bitboard.c: make attacks for knight/king - square macros (BB, BBfile, BBrank) renamed sq_make, sq_file, sq_rank, moved to board.h (and become temporarily inline funcs) - different macros/defs moved to "correct place" (bitboard/board/piece): board.[ch]: everything related to board/square bitboard.[ch]: everything related to bitboards piece.[ch]: everything related to pieces --- Makefile | 15 +++++--- src/bitboard.c | 77 +++++++++++++++++++++++++++++++++-------- src/bitboard.h | 79 ++++++------------------------------------ src/board.h | 81 ++++++++++++++++++++++++++++++++++++++++++-- src/chessdefs.h | 12 +------ src/fen.c | 20 +++++------ src/piece.h | 6 +--- src/position.c | 46 ++++--------------------- src/position.h | 13 +++---- test/bitboard-test.c | 23 +++++++++++++ 10 files changed, 208 insertions(+), 164 deletions(-) create mode 100644 test/bitboard-test.c diff --git a/Makefile b/Makefile index 501c030..9691be2 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ TARGET_FN := brchess TARGET := $(addprefix $(BINDIR)/,$(TARGET_FN)) LDFLAGS := -L$(BRLIBDIR) -LIBS := -l$(LIB) -lreadline -lncurses +LIBS := $(strip -l$(LIB) -lreadline) ##################################### pre-processor flags CPPFLAGS := -I$(BRINCDIR) @@ -175,7 +175,7 @@ cleanobjdir: cleanobj # The part right of '|' are "order-only prerequisites": They are build as # "normal" ones, but do not imply to rebuild target. $(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR) $(DEPDIR) - @echo compiling brchess $< "->" $@. + @echo compiling brchess module: $< "->" $@. @$(CC) -c $(DEPFLAGS) $(CPPFLAGS) $(CFLAGS) $< -o $@ ##################################### brlib libraries @@ -255,13 +255,18 @@ memcheck: targets @$(VALGRIND) $(VALGRINDFLAGS) $(BINDIR)/brchess ##################################### test binaries -TEST = bin/fen-test -FENTESTOBJS = obj/fen.o obj/position.o obj/piece.o obj/util.o +TEST = bin/fen-test bin/bitboard-test + +FENTESTOBJS = obj/fen.o obj/position.o obj/piece.o obj/util.o obj/bitboard.o +BITBOARDOBJS = obj/position.o obj/piece.o obj/bitboard.o obj/fen.o testing: $(TEST) bin/fen-test: test/fen-test.c $(FENTESTOBJS) - $(CC) $(LDFLAGS) $(CPPFLAGS) $(CFLAGS) $< $(LIBS) $(FENTESTOBJS) -o $@ + $(CC) $(LDFLAGS) $(CPPFLAGS) $(CFLAGS) $< $(FENTESTOBJS) $(LIBS) -o $@ + +bin/bitboard-test: test/bitboard-test.c $(BITBOARDOBJS) + $(CC) $(LDFLAGS) $(CPPFLAGS) $(CFLAGS) $< $(BITBOARDOBJS) $(LIBS) -o $@ ##################################### Makefile debug diff --git a/src/bitboard.c b/src/bitboard.c index 9277397..2d518e4 100644 --- a/src/bitboard.c +++ b/src/bitboard.c @@ -11,32 +11,79 @@ * */ +#include + #include "brlib.h" + +#include "chessdefs.h" +#include "board.h" #include "bitboard.h" -/* maps are clockwise from N */ -static char knight_vector[8] = { - NORTH*2 + EAST, NORTH + EAST*2, SOUTH + EAST*2, SOUTH*2 + EAST, - SOUTH*2 + WEST, SOUTH + WEST*2, NORTH + WEST*2, NORTH*2 + WEST +/* vectors are clockwise from N */ +static int knight_vector[] = { + NORTH_EAST + NORTH, NORTH_EAST + EAST, + SOUTH_EAST + EAST, SOUTH_EAST + SOUTH, + SOUTH_WEST + SOUTH, SOUTH_WEST + WEST, + NORTH_WEST + WEST, NORTH_WEST + NORTH }; -static char king_vector[8] = { +static int king_vector[8] = { NORTH, NORTH_EAST, EAST, SOUTH_EAST, SOUTH, SOUTH_WEST, WEST, NORTH_WEST }; -static u64 knight_attacks[64], king_attacks[64]; -static int zob=0; +bitboard_t sq_bb[SQUARE_MAX]; +bitboard_t knight_attacks[64], king_attacks[64]; +/* we will create only dest squares for A1-D4 square, then flip + */ + +/** + * raw_bitboard_print() - print simple bitboard representation + * @bb: the bitboard + * @tit: a string or NULL + */ void bitboard_init(void) { - for (int sq = SQ_0; sq < SQ_N; ++sq) { - /* knight/king */ - for (int j = 0; j < 8; ++j) { - zob += *knight_attacks + *king_attacks + *knight_vector + - *king_vector; - //int dest_knight = sq + knight_vector[j]; - //if () - } + square_t sq, dst; + for (sq = A1; sq <= H8; ++sq) + sq_bb[sq] = mask(sq); + + for (sq = A1; sq <= H8; ++sq) { + for (int vec = 0; vec < 8; ++vec) { + dst = sq + knight_vector[vec]; + if (sq_ok(dst)) { + if (sq_dist(dst, sq) == 2) { + knight_attacks[sq] |= sq_bb[dst]; + } + } + dst = sq + king_vector[vec]; + if (sq_ok(dst)) { + if (sq_dist(dst, sq) == 1) { + king_attacks[sq] |= sq_bb[dst]; + } + } + } } } + +/** + * bitboard_print() - print simple bitboard representation + * @bb: the bitboard + * @tit: a string or NULL + */ +void bitboard_print(const bitboard_t bb, const char *tit) +{ + //char c = p? p: 'X'; + if (tit) + printf("%s\n", tit); + for (rank_t r = RANK_8; r >= RANK_1; --r) { + printf("%d ", r); + for (file_t f = FILE_A; f <= FILE_H; ++f) { + printf(" %c", bb & sq_bb[sq_make(f, r)] ? 'X': '.'); + } + printf("\n"); + } + printf("\n A B C D E F G H\n"); + return; +} diff --git a/src/bitboard.h b/src/bitboard.h index 885b405..187f9d6 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -15,56 +15,16 @@ #define _BITBOARD_H #include "brlib.h" -#include "chessdefs.h" -#include "piece.h" #include "bitops.h" -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, - SQUARE_MAX = 64, - SQUARE_NONE = 64 -} square_t; +#include "chessdefs.h" +#include "board.h" -typedef enum { - _SFILE_ = -1, /* force signed enum */ - FILE_A = 0, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, - FILE_MAX, -} file_t; +typedef u64 bitboard_t; -typedef enum { - _SRANK_ = -1, /* force signed enum */ - RANK_1 = 0, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, - RANK_MAX, -} rank_t; +extern bitboard_t sq_bb[64], knight_attacks[64], king_attacks[64]; -typedef enum { - //A1 = 0x01ULL, B1 = 0x02ULL, C1 = 1UL << 2, D1 = 1UL << 3, - //E1 = 1UL << 4, F1 = 1UL << 5, G1 = 1UL << 6, H1 = 1UL << - A1bb = mask(A1), A2bb = mask(A2), A3bb = mask(A3), A4bb = mask(A4), - A5bb = mask(A5), A6bb = mask(A6), A7bb = mask(A7), A8bb = mask(A8), - B1bb = mask(B1), B2bb = mask(B2), B3bb = mask(B3), B4bb = mask(B4), - B5bb = mask(B5), B6bb = mask(B6), B7bb = mask(B7), B8bb = mask(B8), - C1bb = mask(C1), C2bb = mask(C2), C3bb = mask(C3), C4bb = mask(C4), - C5bb = mask(C5), C6bb = mask(C6), C7bb = mask(C7), C8bb = mask(C8), - D1bb = mask(D1), D2bb = mask(D2), D3bb = mask(D3), D4bb = mask(D4), - D5bb = mask(D5), D6bb = mask(D6), D7bb = mask(D7), D8bb = mask(D8), - E1bb = mask(E1), E2bb = mask(E2), E3bb = mask(E3), E4bb = mask(E4), - E5bb = mask(E5), E6bb = mask(E6), E7bb = mask(E7), E8bb = mask(E8), - F1bb = mask(F1), F2bb = mask(F2), F3bb = mask(F3), F4bb = mask(F4), - F5bb = mask(F5), F6bb = mask(F6), F7bb = mask(F7), F8bb = mask(F8), - G1bb = mask(G1), G2bb = mask(G2), G3bb = mask(G3), G4bb = mask(G4), - G5bb = mask(G5), G6bb = mask(G6), G7bb = mask(G7), G8bb = mask(G8), - H1bb = mask(H1), H2bb = mask(H2), H3bb = mask(H3), H4bb = mask(H4), - H5bb = mask(H5), H6bb = mask(H6), H7bb = mask(H7), H8bb = mask(H8), -} sq_bb; +#define mask(i) ( 1ULL << (i) ) typedef enum { FILE_Abb = 0x0101010101010101ULL, @@ -88,31 +48,14 @@ typedef enum { RANK_8bb = 0xff00000000000000ULL } rank_bb; -typedef enum { - NORTH = 8, - EAST = 1, - SOUTH = -NORTH, - WEST = -EAST, - - NORTH_EAST = (NORTH + EAST), - SOUTH_EAST = (SOUTH + EAST), - SOUTH_WEST = (SOUTH + WEST), - NORTH_WEST = (NORTH + WEST), -} dir_t; - -static inline square_t BB(file_t file, rank_t rank) +/* https://www.chessprogramming.org/Flipping_Mirroring_and_Rotating#Rotation + */ +static __always_inline bitboard_t bb_rotate_90(bitboard_t b) { - return (rank << 3) + file; -} -static inline file_t BBfile(square_t square) -{ - return square & 7; -} -static inline rank_t BBrank(square_t square) -{ - return square >> 3; + return b; } -void bitboard_init(void); +extern void bitboard_init(void); +extern void bitboard_print(const bitboard_t bitboard, const char *title); #endif /* _BITBOARD_H */ diff --git a/src/board.h b/src/board.h index 03cd28c..b7a6376 100644 --- a/src/board.h +++ b/src/board.h @@ -1,6 +1,6 @@ -/* board.h - board definitions. +/* board.h - 8x8 board definitions. * - * Copyright (C) 2021 Bruno Raoult ("br") + * Copyright (C) 2021-2024 Bruno Raoult ("br") * Licensed under the GNU General Public License v3.0 or later. * Some rights reserved. See COPYING. * @@ -17,6 +17,83 @@ #include "brlib.h" #include "chessdefs.h" + +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, + SQUARE_MAX = 64, + SQUARE_NONE = 64 +} square_t; + +typedef enum { + _SFILE_ = -1, /* force signed enum */ + FILE_A = 0, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, + FILE_MAX, +} file_t; + +typedef enum { + _SRANK_ = -1, /* force signed enum */ + RANK_1 = 0, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, + RANK_MAX, +} rank_t; + +typedef enum { + NORTH = 8, + EAST = 1, + SOUTH = -NORTH, + WEST = -EAST, + + NORTH_EAST = (NORTH + EAST), + SOUTH_EAST = (SOUTH + EAST), + SOUTH_WEST = (SOUTH + WEST), + NORTH_WEST = (NORTH + WEST), +} dir_t; + +/* flip a 0-63 square: + * Vertical: G8 (62) becomes G1 (6) + * Horizontal: G8 (62) becomes B8 (57) + */ +#define FLIP_V(sq) ((sq) ^ 56) +#define FLIP_H(sq) ((sq) ^ 7) +#define FLIP_HV(sq) (FLIP_V(FLIP_H(sq))) + +/* TODO: revert to macros after bitboard migration */ +static __always_inline square_t sq_make(file_t file, rank_t rank) +{ + return (rank << 3) + file; +} +static __always_inline file_t sq_file(square_t square) +{ + return square & 7; +} +static __always_inline rank_t sq_rank(square_t square) +{ + return square >> 3; +} + + +#define sq_ok(sq) ((sq) >= A1 && (sq) <= H8) + +/* Chebyshev distance: 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 */ +#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 diff --git a/src/chessdefs.h b/src/chessdefs.h index e0617df..85bc150 100644 --- a/src/chessdefs.h +++ b/src/chessdefs.h @@ -16,20 +16,10 @@ #include "brlib.h" /* brlib types */ -#define mask(i) ( 1ULL << (i) ) #define C64(const_u64) const_u64##ULL #define U64(const_s64) const_s64##LL -typedef u64 bitboard; -typedef ushort board; - - -/* flip a 0-63 square: - * Vertical: G8 (62) becomes G1 (6) - * Horizontal: G8 (62) becomes B8 (57) - */ -#define FLIP_V(sq) ((sq) ^ 56) -#define FLIP_H(sq) ((sq) ^ 7) +//typedef ushort board; #define BOARDSIZE (8*8) /* from human to machine */ diff --git a/src/fen.c b/src/fen.c index a2add0c..d1f06db 100644 --- a/src/fen.c +++ b/src/fen.c @@ -110,7 +110,7 @@ pos_t *fen2pos(pos_t *pos, const char *fen) log_i(5, "f=%d r=%d *p=%c piece=%#04x t=%d c=%d\n", file, rank, *cur, piece, PIECE(piece), COLOR(piece)); # endif - pos_set_sq(&tmppos, BB(file, rank), piece); + pos_set_sq(&tmppos, sq_make(file, rank), piece); file++; } else { /* error */ err_line = __LINE__, err_char = *cur, err_pos = cur - fen; @@ -146,7 +146,7 @@ pos_t *fen2pos(pos_t *pos, const char *fen) if (*cur == '-') { cur++; } else { - tmppos.en_passant = BB(C2FILE(*cur), C2RANK(*(cur+1))); + tmppos.en_passant = sq_make(C2FILE(*cur), C2RANK(*(cur+1))); cur += 2; } SKIP_BLANK(cur); @@ -205,21 +205,21 @@ char *pos2fen(const pos_t *pos, char *fen) /* 1) position */ - for (rank_t rank = RANK_8; rank >= RANK_1; --rank) { - for (file_t file = FILE_A; file <= FILE_H;) { - square_t sq = BB(file, rank); + for (rank_t r = RANK_8; r >= RANK_1; --r) { + for (file_t f = FILE_A; f <= FILE_H;) { + square_t sq = sq_make(f, r); piece_t piece =pos->board[sq]; if (pos->board[sq] == EMPTY) { int len = 0; - for (; file <= FILE_H && pos->board[BB(file,rank)] == EMPTY; file++) + for (; f <= FILE_H && pos->board[sq_make(f, r)] == EMPTY; f++) len++; fen[cur++] = '0' + len; } else { fen[cur++] = piece_to_char_color(piece); - file++; + f++; } } - fen[cur++] = rank == RANK_1? ' ': '/'; + fen[cur++] = r == RANK_1? ' ': '/'; } /* 2) next turn color @@ -243,8 +243,8 @@ char *pos2fen(const pos_t *pos, char *fen) if (!pos->en_passant) { fen[cur++] = '-'; } else { - fen[cur++] = FILE2C(BBfile(pos->en_passant)); - fen[cur++] = RANK2C(BBrank(pos->en_passant)); + fen[cur++] = FILE2C(sq_file(pos->en_passant)); + fen[cur++] = RANK2C(sq_rank(pos->en_passant)); } fen[cur++] = ' '; diff --git a/src/piece.h b/src/piece.h index 14ac6f3..1f2b71c 100644 --- a/src/piece.h +++ b/src/piece.h @@ -82,11 +82,7 @@ extern const struct piece_details { s64 end_value; /* value endgame */ } piece_details[PIECE_MAX]; -extern const char pieces_str[6+6+1]; - -//#define P_SYM(p) piece_details[p].sym -//#define P_NAME(p) piece_details[p].name -//#define P_VAL(p) piece_details[p].opn_value +extern const char pieces_str[6+6+1]; /* to search from fen/user input */ #define OPPONENT(p) !(p) diff --git a/src/position.c b/src/position.c index 910b518..84f76bb 100644 --- a/src/position.c +++ b/src/position.c @@ -18,11 +18,12 @@ #include #include +#include "brlib.h" #include "bitops.h" #include "chessdefs.h" #include "position.h" -//#include "move.h" +#include "bitboard.h" #include "fen.h" #include "piece.h" #include "util.h" @@ -154,7 +155,7 @@ void pos_print(pos_t *pos) for (rank = 7; rank >= 0; --rank) { printf("%c |", rank + '1'); for (file = 0; file < 8; ++file) { - pc = board[BB(file, rank)]; + pc = board[sq_make(file, rank)]; printf(" %s |", piece_to_sym_color(pc)); } printf("\n +---+---+---+---+---+---+---+---+\n"); @@ -186,7 +187,7 @@ void pos_pieces_print(pos_t *pos) int bit, count, cur; char pname; u64 tmp; - bitboard p; + bitboard_t p; for (int color = WHITE; color <= BLACK; ++color) { for (int piece = KING; piece >= PAWN; --piece) { p = pos->bb[color][piece]; @@ -196,7 +197,7 @@ void pos_pieces_print(pos_t *pos) printf("%c(0)%s", pname, count? ":": ""); if (count) { bit_for_each64(bit, tmp, p) { - char cf = BBfile(bit), cr = BBrank(bit); + char cf = sq_file(bit), cr = sq_rank(bit); printf("%s%c%c", cur? ",": "", FILE2C(cf), RANK2C(cr)); cur++; } @@ -236,31 +237,12 @@ void raw_board_print(const pos_t *pos) for (rank_t r = RANK_8; r >= RANK_1; --r) { for (file_t f = FILE_A; f <= FILE_H; ++f) - printf("%02x ", pos->board[BB(f, r)]); + printf("%02x ", pos->board[sq_make(f, r)]); printf(" \n"); } return; } -/** - * raw_bitboard_print - print simple bitboard representation - * @bb: the bitboard - * @tit: a string or NULL - */ -void raw_bitboard_print(const bitboard bb, const char *tit, const piece_t p) -{ - if (tit) - printf("%s\n", tit); - for (rank_t r = RANK_8; r >= RANK_1; --r) { - printf("%d ", r); - for (file_t f = FILE_A; f <= FILE_H; ++f) - printf(" %c", bb & (BB(f, r)) ? p: '.'); - printf(" A B C D E F G H\n"); - } - printf(" \n"); - return; -} - /** * pos_bitboards_print() - Print position bitboards * @pos: &position @@ -331,19 +313,3 @@ void raw_bitboard_print(const bitboard bb, const char *tit, const piece_t p) * pool_add(pos_pool, pos); * } */ - - -/******************************************************************** - * pool_t *pos_pool_init() * - * { * - * if (!pos_pool) * - * pos_pool = pool_create("positions", 128, sizeof(pos_t)); * - * return pos_pool; * - * } * - * * - * void pos_pool_stats() * - * { * - * if (pos_pool) * - * pool_stats(pos_pool); * - * } * - ********************************************************************/ diff --git a/src/position.h b/src/position.h index 3f3f6c7..10d57aa 100644 --- a/src/position.h +++ b/src/position.h @@ -17,13 +17,11 @@ #include #include "brlib.h" -//#include "pool.h" -//#include "list.h" #include "bitops.h" -#include "bitboard.h" -#include "board.h" +#include "bitboard.h" #include "chessdefs.h" +#include "piece.h" typedef struct { u64 node_count; /* evaluated nodes */ @@ -41,10 +39,10 @@ typedef struct { //bool moves_generated; //bool moves_counted; - bitboard bb[2][PIECE_TYPE_MAX]; /* bb[0][PAWN], bb[1][ALL_PIECES] */ - bitboard controlled[2]; + bitboard_t bb[2][PIECE_TYPE_MAX]; /* bb[0][PAWN], bb[1][ALL_PIECES] */ + bitboard_t controlled[2]; //u16 mobility[2]; - //struct list_head pieces[2]; /* pieces list, King is first */ + //struct list_head pieces[2]; /* pieces list, King is first */ //struct list_head moves[2]; piece_t board[BOARDSIZE]; } pos_t; @@ -95,7 +93,6 @@ extern void pos_print(pos_t *pos); extern void pos_pieces_print(pos_t *pos); extern void raw_board_print(const pos_t *pos); -extern void raw_bitboard_print(const bitboard bitboard, const char *title, const piece_t piece); //extern pos_t *pos_startpos(pos_t *pos); diff --git a/test/bitboard-test.c b/test/bitboard-test.c new file mode 100644 index 0000000..980050c --- /dev/null +++ b/test/bitboard-test.c @@ -0,0 +1,23 @@ +//#include "debug.h" +//#include "pool.h" + +#include +#include + +#include "../src/bitboard.h" +#include "../src/position.h" +#include "../src/piece.h" +//#include "../src/fen.h" + +int main(int __unused ac, __unused char**av) +{ + char str[128]; + bitboard_init(); + for (square_t sq = 0; sq < 64; ++sq) { + sprintf(str, "%2d %#lx %#lx knight", sq, sq_bb[sq], knight_attacks[sq]); + bitboard_print(knight_attacks[sq], str); + sprintf(str, "%2d %#lx %#lx knight", sq, sq_bb[sq], king_attacks[sq]); + bitboard_print(king_attacks[sq], str); + } + return 0; +}