diff --git a/Makefile b/Makefile index 6804f71..1395b51 100644 --- a/Makefile +++ b/Makefile @@ -51,6 +51,8 @@ LIBS := $(strip -l$(LIB) -lreadline) CPPFLAGS := -I$(BRINCDIR) -I$(INCDIR) CPPFLAGS += -DBUG_ON CPPFLAGS += -DWARN_ON +CPPFLAGS += -NDEBUG # + #CPPFLAGS += -DDEBUG # global CPPFLAGS += -DDEBUG_DEBUG # enable log() functions #CPPFLAGS += -DDEBUG_DEBUG_C # enable verbose log() settings @@ -66,7 +68,7 @@ CPPFLAGS := $(strip $(CPPFLAGS)) ##################################### compiler flags CFLAGS := -std=gnu11 -#CFLAGS += -O2 +CFLAGS += -O3 CFLAGS += -g CFLAGS += -Wall CFLAGS += -Wextra diff --git a/src/bitboard.c b/src/bitboard.c index 75f482a..31eea29 100644 --- a/src/bitboard.c +++ b/src/bitboard.c @@ -20,6 +20,7 @@ #include "piece.h" #include "board.h" #include "bitboard.h" +#include "position.h" /* vectors are clockwise from N */ static int knight_vector[] = { @@ -110,6 +111,15 @@ void bitboard_init(void) } } +bitboard_t bb_knight_moves(bitboard_t notmine, square_t sq) +{ + return bb_knight[sq] & notmine; +} +bitboard_t bb_king_moves(bitboard_t notmine, square_t sq) +{ + return bb_king[sq] & notmine; +} + /** * bitboard_print() - print simple bitboard representation * @title: a string or NULL diff --git a/src/bitboard.h b/src/bitboard.h index d6d0bd3..f99c8b2 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -95,6 +95,10 @@ static __always_inline bitboard_t shift_nw(const bitboard_t bb) } extern void bitboard_init(void); + +extern bitboard_t bb_knight_moves(bitboard_t occ, square_t sq); +extern bitboard_t bb_king_moves(bitboard_t occ, square_t sq); + 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); diff --git a/src/board.h b/src/board.h index 16fd506..37cb4e6 100644 --- a/src/board.h +++ b/src/board.h @@ -68,12 +68,12 @@ typedef enum { */ #define FLIP_V(sq) ((sq) ^ 56) #define FLIP_H(sq) ((sq) ^ 7) -#define FLIP_HV(sq) (FLIP_V(FLIP_H(sq))) +#define FLIP_HV(sq) ((sq) ^ 63) /* FLIP_V ^ FLIP_H */ /* TODO: revert to macros after bitboard migration */ static __always_inline square_t sq_make(file_t file, rank_t rank) { - return (rank << 3) + file; + return (rank << 6) + file; } static __always_inline file_t sq_file(square_t square) { @@ -81,7 +81,7 @@ static __always_inline file_t sq_file(square_t square) } static __always_inline rank_t sq_rank(square_t square) { - return square >> 3; + return square >> 6; } #define sq_ok(sq) ((sq) >= A1 && (sq) <= H8) diff --git a/src/fen.c b/src/fen.c index d1f06db..9b8296d 100644 --- a/src/fen.c +++ b/src/fen.c @@ -208,7 +208,7 @@ char *pos2fen(const pos_t *pos, char *fen) 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]; + piece_t piece = pos->board[sq]; if (pos->board[sq] == EMPTY) { int len = 0; for (; f <= FILE_H && pos->board[sq_make(f, r)] == EMPTY; f++) diff --git a/src/hyperbola-quintessence.c b/src/hyperbola-quintessence.c index c14b385..2b2c335 100644 --- a/src/hyperbola-quintessence.c +++ b/src/hyperbola-quintessence.c @@ -30,15 +30,29 @@ uchar bb_rank_attacks[64 * 8]; * 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 + * + * Rank attacks table: + * bb_rank_hyperbola[512 = 9 bits], indexed by oooooofff where: + * - O = oooooo: occupation of inner 6 bits on rank + * - F = fff: file of sliding piece + * The index is built as (oooooo << 3 + fff), = ((O << 3) + F), where: + * - O = all combinations of 6 bits (loop from 0 to 64) + * - F = all files (loop from 0 to 7) + * To retrieve the index, given an 8 bits mask M=XooooooX, and a file F=fff, + * we get O from M with: + * 1) remove bits 'X' (O = M & 01111110) + * 2) shift left result 2 more bits, as bit 0 is unused and already cleared: + * (O <<= 2) + * + * TODO ? create masks excluding slider (eg. bb_diagonal ^ bb_sq[square]), + * to save one operation in hyperbola_moves(). + * TODO ? replace rank attack with this idea, mapping rank to diagonal ? + * See http://timcooijmans.blogspot.com/2014/04/ * */ void hyperbola_init() { - /* generate rank attacks + /* generate rank attacks, not handled by HQ */ for (int occ = 0; occ < 64; ++occ) { for (int file = 0; file < 8; ++file) { @@ -48,7 +62,7 @@ void hyperbola_init() 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 */ + if ((occ << 1) & b) /* piece on b, we stop */ break; } /* set f right attacks */ @@ -63,6 +77,24 @@ void hyperbola_init() } } +/** + * hyperbola_rank_moves() - generate rank moves for a sliding piece. + * @pieces: occupation bitboard + * @sq: piece square + * + * Rank attacks are not handled by HQ, so we do it with a + * + * @Return: The moves mask for piece + */ +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) & 0x7e; /* 01111110 clear bits 0 & 7 */ + + return ((bitboard_t)bb_rank_attacks[o * 4 + file]) << rank; +} + /** * hyperbola_moves() - generate hyperbola moves mask for a given sliding piece * @pieces: occupation bitboard @@ -80,19 +112,9 @@ static bitboard_t hyperbola_moves(const bitboard_t pieces, const square_t sq, bitboard_t r = bswap64(o); square_t r_sq = FLIP_V(sq); - return ( (o - mask(sq) ) - ^ bswap64(r - mask(r_sq))) + return ( (o - 2 * mask(sq) ) + ^ bswap64(r - 2 * 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) diff --git a/src/hyperbola-quintessence.h b/src/hyperbola-quintessence.h index 2ee5a20..ffcd283 100644 --- a/src/hyperbola-quintessence.h +++ b/src/hyperbola-quintessence.h @@ -14,6 +14,12 @@ #ifndef _HYPERBOLA_QUINTESSENCE_H #define _HYPERBOLA_QUINTESSENCE_H +#include "board.h" +#include "bitboard.h" + void hyperbola_init(void); +extern bitboard_t hyperbola_bishop_moves(bitboard_t occ, square_t sq); +extern bitboard_t hyperbola_rook_moves(bitboard_t occ, square_t sq); +extern bitboard_t hyperbola_queen_moves(bitboard_t occ, square_t sq); #endif /* _HYPERBOLA_QUINTESSENCE_H */ diff --git a/src/move.h b/src/move.h index 2e706e2..9647109 100644 --- a/src/move.h +++ b/src/move.h @@ -16,19 +16,50 @@ #include "chessdefs.h" #include "position.h" -#include "pool.h" +//#include "pool.h" #include "piece.h" -/* move flags +/* move structure: + * 3 2 2 1 1 1 1 1 1 + * 1 5 4 8 7 5 4 2 1 6 5 0 + * UUUUUUU FFFFFFF ppp ccc tttttt ffffff + * + * bits range type mask get desc + * ffffff 0-5 square_t 3f &63 from + * tttttt 6-11 square_t fc0 (>>6)&63 to + * ccc 12-14 piece_type_t 7000 (>>12)&7 captured + * ppp 15-17 piece_type_t 38000 (>>15)&7 promoted + * FFFFFFF 18-24 N/A 1fc0000 N/A flags + * UUUUUUU 25-31 unused fe000000 N/A future usage ? */ -typedef unsigned char move_flags_t; -#define M_NORMAL 0x00 -#define M_CHECK 0x01 /* unsure if we know */ -#define M_CAPTURE 0x02 -#define M_EN_PASSANT 0x04 -#define M_PROMOTION 0x08 -#define M_CASTLE_K 0x10 -#define M_CASTLE_Q 0x20 +#define move_t u32 + +#define M_ +/* move flags */ +#define M_FLAGS_BEG 18 +#define M_HAS_FLAGS mask(_M_FLAGS_BEG + 0) /* probably unused */ +#define M_CAPTURE mask(_M_FLAGS_BEG + 1) +#define M_ENPASSANT mask(_M_FLAGS_BEG + 2) +#define M_PROMOTION mask(_M_FLAGS_BEG + 3) +#define M_CASTLE_K mask(_M_FLAGS_BEG + 4) +#define M_CASTLE_Q mask(_M_FLAGS_BEG + 5) +#define M_CHECK mask(_M_FLAGS_BEG + 6) /* probably unknown/useless */ + +#define M_FLAGS (M_CAPTURE | M_ENPASSANT | M_PROMOTION | \ + M_CASTLE_K | M_CASTLE_Q | M_CHECK) +#define M_NORMAL (~M_FLAGS) + +#define MOVES_MAX 256 + +static inline move_t move_make(square_t from, square_t to) +{ + return (to << 3) | from; +} + +static inline move_t move_from(move_t move) +{ + return move & 56; +} /* moves_print flags */ @@ -40,30 +71,23 @@ typedef unsigned char move_flags_t; #define M_PR_SEPARATE 0x40 /* separate captures */ #define M_PR_LONG 0x80 -typedef struct move_s { - piece_t piece; - square_t from, to; - piece_t capture; /* captured piece */ - piece_t promotion; /* promoted piece */ - move_flags_t flags; - eval_t negamax; - eval_t eval; - eval_t eval_simple; - pos_t *pos; - struct list_head list; /* next move */ -} move_t; +typedef struct { + move_t move[MOVES_MAX]; + int nmoves; /* total moves (fill) */ + int curmove; /* current move (use) */ +} movelist_t; -pool_t *moves_pool_init(); -void moves_pool_stats(); -int move_print(int movenum, move_t *move, move_flags_t flags); -void moves_print(pos_t *move, move_flags_t flags); +//pool_t *moves_pool_init(); +//void moves_pool_stats(); +//int move_print(int movenum, move_t *move, move_flags_t flags); +//void moves_print(pos_t *move, move_flags_t flags); -void move_del(struct list_head *ptr); -int moves_del(pos_t *pos); +//void move_del(struct list_head *ptr); +//int moves_del(pos_t *pos); int pseudo_moves_castle(pos_t *pos, bool color, bool doit, bool do_king); -int pseudo_moves_gen(pos_t *pos, piece_list_t *piece, bool doit, bool do_king); -int pseudo_moves_pawn(pos_t *pos, piece_list_t *piece, bool doit); +//int pseudo_moves_gen(pos_t *pos, piece_list_t *piece, bool doit, bool do_king); +//int pseudo_moves_pawn(pos_t *pos, piece_list_t *piece, bool doit); int moves_gen(pos_t *pos, bool color, bool doit, bool do_king); int moves_gen_king_moves(pos_t *pos, bool color, bool doit); diff --git a/src/movegen.c b/src/movegen.c new file mode 100644 index 0000000..7cdc754 --- /dev/null +++ b/src/movegen.c @@ -0,0 +1,74 @@ +/* movegen.c - move generation + * + * 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 "bitops.h" + +#include "bitboard.h" +#include "hyperbola-quintessence.h" +#include "piece.h" +#include "move.h" + +//void add_pseudo_move(move_t *pmove, square_t from, square_t to) +//{ +// pmov +//} + +/** + * gen_all_pseudomoves() - generate all pseudo moves + * + */ +int gen_all_pseudomoves(pos_t *pos) +{ + int me = pos->turn, other = OPPONENT(me); + bitboard_t my_pieces = pos->bb[me][ALL_PIECES], notmine = ~my_pieces, + other_pieces = pos->bb[other][ALL_PIECES], + occ = my_pieces | other_pieces, movebits; + int tmp1, tmp2, from, to; + movelist_t moves = pos->moves; + + /* sliding pieces */ + bit_for_each64(from, tmp1, pos->bb[me][BISHOP]) { + movebits = hyperbola_bishop_moves(occ, from) & notmine; + bit_for_each64(to, tmp2, movebits) { + moves.move[moves.nmoves++] = move_make(from, to); + } + + } + bit_for_each64(from, tmp1, pos->bb[me][ROOK]) { + movebits = hyperbola_rook_moves(occ, from) & notmine; + bit_for_each64(to, tmp2, movebits) { + moves.move[moves.nmoves++] = move_make(from, to); + } + } + bit_for_each64(from, tmp1, pos->bb[me][QUEEN]) { + movebits = hyperbola_queen_moves(occ, from) & notmine; + bit_for_each64(to, tmp2, movebits) { + moves.move[moves.nmoves++] = move_make(from, to); + } + } + + /* knight */ + bit_for_each64(from, tmp1, pos->bb[me][KNIGHT]) { + movebits = bb_knight_moves(occ, from) & notmine; + bit_for_each64(to, tmp2, movebits) { + moves.move[moves.nmoves++] = move_make(from, to); + } + } + + /* king */ + movebits = bb_king_moves(occ, pos->king[me]) & notmine; + bit_for_each64(to, tmp2, movebits) { + moves.move[moves.nmoves++] = move_make(from, to); + } + /* TODO */ +} diff --git a/src/movegen.h b/src/movegen.h new file mode 100644 index 0000000..0db750a --- /dev/null +++ b/src/movegen.h @@ -0,0 +1,30 @@ +/* movegen.h - move generation + * + * 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 MOVEGEN_H +#define MOVEGEN_H + +#include "bitops.h" + +#include "bitboard.h" +#include "hyperbola-quintessence.h" +#include "piece.h" +#include "move.h" + +/** + * gen_all_pseudomoves() - generate all pseudo moves + * + */ +int gen_all_pseudomoves(pos_t *pos); + +#endif /* MOVEGEN_H */ diff --git a/src/position.h b/src/position.h index 10d57aa..1947a89 100644 --- a/src/position.h +++ b/src/position.h @@ -22,6 +22,7 @@ #include "bitboard.h" #include "chessdefs.h" #include "piece.h" +#include "move.h" typedef struct { u64 node_count; /* evaluated nodes */ @@ -40,11 +41,14 @@ typedef struct { //bool moves_counted; bitboard_t bb[2][PIECE_TYPE_MAX]; /* bb[0][PAWN], bb[1][ALL_PIECES] */ + square_t king[2]; /* dup with bb, faster retrieval */ bitboard_t controlled[2]; //u16 mobility[2]; //struct list_head pieces[2]; /* pieces list, King is first */ //struct list_head moves[2]; piece_t board[BOARDSIZE]; + movelist_t moves; + int nmoves; } pos_t; /** @@ -62,6 +66,8 @@ static inline void pos_set_sq(pos_t *pos, square_t square, piece_t piece) pos->board[square] = piece; pos->bb[color][type] |= 1 << square; pos->bb[color][ALL_PIECES] |= 1 << square; + if (type == KING) + pos->king[color] = square; } /**