diff --git a/Makefile b/Makefile index 3ea66c6..d206fb9 100644 --- a/Makefile +++ b/Makefile @@ -22,11 +22,11 @@ CC=gcc .SECONDEXPANSION: OBJ=$(addprefix $(OBJDIR)/,$(SRC_S:.c=.o)) -BIN=fen pool piece move debug +BIN=fen pool piece move debug eval bits CFLAGS += -std=gnu99 -FLAGS += -O2 +CFLAGS += -O2 CFLAGS += -g CFLAGS += -Wall CFLAGS += -Wextra @@ -41,8 +41,10 @@ CFLAGS += -DDEBUG # global CFLAGS += -DDEBUG_POOL # memory pools management CFLAGS += -DDEBUG_FEN # FEN decoding CFLAGS += -DDEBUG_MOVE # move generation +CFLAGS += -DDEBUG_EVAL # eval functions +CFLAGS += -DDEBUG_BITS # bits functions -#CFLAGS += -DDEBUG_MAINSLEEP # sleep 1 sec within main loop (SIGINTR test) +#CFLAGS += -DDEBUG_EVAL # sleep 1 sec within main loop (SIGINTR test) #CFLAGS += -DDEBUG_EVAL2 # eval 2 #CFLAGS += -DDEBUG_EVAL3 # eval 3 #CFLAGS += -DDEBUG_MEM # malloc @@ -90,7 +92,7 @@ $(BIN): $$(subst $(OBJDIR)/$$@.o,,$(OBJ)) $(SRCDIR)/$$@.c # debug: $$(subst $(OBJDIR)/$$@.o,,$(OBJ)) $(SRCDIR)/$$@.c # $(CC) $(CFLAGS) $^ -o $@ -.PHONY: bits -bits: test/bits.c - $(CC) $(CFLAGS) -S $^ -o $@.s - $(CC) -DFOO $(CFLAGS) $^ -o $@ +#.PHONY: bits +#bits2: src/bits.c +# $(CC) $(CFLAGS) -S $^ -o $@.s +# $(CC) $(CFLAGS) $^ -o $@ diff --git a/make.deps b/make.deps index 895f6a9..7cd9b0b 100644 --- a/make.deps +++ b/make.deps @@ -1,10 +1,13 @@ +./obj/bits.o:: src/bits.c src/bits.h src/debug.h ./obj/debug.o:: src/debug.c src/debug.h -./obj/fen.o:: src/fen.c src/debug.h src/chessdefs.h src/position.h src/board.h \ - src/piece.h src/list.h src/pool.h src/fen.h -./obj/move.o:: src/move.c src/chessdefs.h src/piece.h src/board.h src/position.h \ - src/list.h src/pool.h src/move.h src/debug.h -./obj/piece.o:: src/piece.c src/chessdefs.h src/piece.h src/board.h \ - src/position.h src/list.h src/pool.h src/debug.h +./obj/eval.o:: src/eval.c src/eval.h src/position.h src/chessdefs.h src/bits.h \ + src/debug.h src/board.h src/piece.h src/list.h src/pool.h +./obj/fen.o:: src/fen.c src/debug.h src/chessdefs.h src/bits.h src/position.h \ + src/board.h src/piece.h src/list.h src/pool.h src/fen.h +./obj/move.o:: src/move.c src/chessdefs.h src/bits.h src/debug.h src/piece.h \ + src/board.h src/position.h src/list.h src/pool.h src/move.h +./obj/piece.o:: src/piece.c src/chessdefs.h src/bits.h src/debug.h src/piece.h \ + src/board.h src/position.h src/list.h src/pool.h ./obj/pool.o:: src/pool.c src/list.h src/pool.h src/debug.h -./obj/position.o:: src/position.c src/chessdefs.h src/position.h src/board.h \ - src/piece.h src/list.h src/pool.h src/fen.h +./obj/position.o:: src/position.c src/chessdefs.h src/bits.h src/debug.h \ + src/position.h src/board.h src/piece.h src/list.h src/pool.h src/fen.h diff --git a/src/bits.c b/src/bits.c new file mode 100644 index 0000000..0e28872 --- /dev/null +++ b/src/bits.c @@ -0,0 +1,81 @@ +/* bits.c - bits.h tests + * + * Copyright (C) 2021 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 "bits.h" + +#ifdef BIN_bits +#include +#include + +static inline int _popcount64(u64 n) +{ + int count = 0; + while (n) { + count++; + n &= (n - 1); + } + return count; +} + +static inline int _ctz64(u64 n) +{ + return _popcount64((n & -n) - 1); +} + +static inline int _clz64(u64 n) +{ + u64 r, q; + + r = (n > 0xFFFFFFFF) << 5; n >>= r; + q = (n > 0xFFFF) << 4; n >>= q; r |= q; + q = (n > 0xFF ) << 3; n >>= q; r |= q; + q = (n > 0xF ) << 2; n >>= q; r |= q; + q = (n > 0x3 ) << 1; n >>= q; r |= q; + r |= (n >> 1); + return __WORDSIZE - r - 1; +} + +static inline int _ffs64(u64 n) +{ + if (n == 0) + return (0); + + return _popcount64(n ^ ~-n); +} + + +int main(int ac, char **av) +{ + u64 u = 123, _tmp; + int curbit; + int base = 10; + debug_init(0); + if (ac > 2) + base = atoi(*(av+2)); + if (ac > 1) { + u = strtoul(*(av+1), NULL, base); + printf("base=%d input=%#lx\n", base, u); + printf("popcount64(%lu) = %d/%d\n", u, popcount64(u), _popcount64(u)); + printf("ctz64(%lu) = %d/%d\n", u, ctz64(u), _ctz64(u)); + printf("clz64(%lu) = %d/%d\n", u, clz64(u), _clz64(u)); + printf("ffs64(%lu) = %d/%d\n", u, ffs64(u), _ffs64(u)); + printf("\n"); + + bit_for_each64(curbit, _tmp, u) { + printf("loop: curbit=%d\n", curbit); + } + + } + return 0; +} +#endif /* BIN_bits */ diff --git a/src/bits.h b/src/bits.h new file mode 100644 index 0000000..8d1773c --- /dev/null +++ b/src/bits.h @@ -0,0 +1,165 @@ +/* bits.h - bits functions. + * + * Copyright (C) 2021 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 BITS_H +#define BITS_H + +#include + +/* next include will define __WORDSIZE: 32 or 64 + */ +#include +#include "debug.h" + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +/* no plan to support 32bits for now... + */ +#if __WORDSIZE != 64 +ERROR_64_BYTES_WORDSIZE_ONLY +#endif + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; +typedef unsigned int uint; +typedef unsigned char uchar; + +/* count trailing zeroes : 00101000 -> 3 + * ^^^ + */ +static inline int ctz64(u64 n) +{ +# if __has_builtin(__builtin_ctzl) +# ifdef DEBUG_BITS + log_f(1, "builtin ctzl.\n"); +# endif + return __builtin_ctzl(n); + +# elif __has_builtin(__builtin_clzl) +# ifdef DEBUG_BITS + log_f(1, "builtin clzl.\n"); +# endif + return __WORDSIZE - (__builtin_clzl(n & -n) + 1); + +# else +# ifdef DEBUG_BITS + log_f(1, "emulated.\n"); +# endif + return popcount64((n & −n) − 1); +# endif +} + +/* count leading zeroes : 00101000 -> 2 + * ^^ + */ +static inline int clz64(u64 n) +{ +# if __has_builtin(__builtin_clzl) +# ifdef DEBUG_BITS + log_f(1, "builtin.\n"); +# endif + return __builtin_clzl(n); + +# else +# ifdef DEBUG_BITS + log_f(1, "emulated.\n"); +# endif + u64 r, q; + + r = (n > 0xFFFFFFFF) << 5; n >>= r; + q = (n > 0xFFFF) << 4; n >>= q; r |= q; + q = (n > 0xFF ) << 3; n >>= q; r |= q; + q = (n > 0xF ) << 2; n >>= q; r |= q; + q = (n > 0x3 ) << 1; n >>= q; r |= q; + r |= (n >> 1); + return __WORDSIZE - r - 1; +# endif +} + +/* find first set : 00101000 -> 4 + * ^ + */ +static inline uint ffs64(u64 n) +{ +# if __has_builtin(__builtin_ffsl) +# ifdef DEBUG_BITS + log_f(1, "builtin ffsl.\n"); +# endif + return __builtin_ffsll(n); + +# elif __has_builtin(__builtin_ctzl) +# ifdef DEBUG_BITS + log_f(1, "builtin ctzl.\n"); +# endif + if (n == 0) + return (0); + return __builtin_ctzl(n) + 1; + +# else +# ifdef DEBUG_BITS + log_f(1, "emulated.\n"); +# endif + return popcount64(n ^ ~-n); +# endif +} + +static inline int popcount64(u64 n) +{ +# if __has_builtin(__builtin_popcountl) +# ifdef DEBUG_BITS + log_f(1, "builtin.\n"); +# endif + return __builtin_popcountl(n); + +# else +# ifdef DEBUG_BITS + log_f(1, "emulated.\n"); +# endif + int count = 0; + while (n) { + count++; + n &= (n - 1); + } + return count; +# endif +} + +/** bit_for_each64 - iterate over an u64 bits + * @pos: an int used as current bit + * @tmp: a temp u64 used as temporary storage + * @ul: the u64 to loop over + * + * Usage: + * u64 u=139, _t; // u=b10001011 + * int cur; + * bit_for_each64(cur, _t, u) { + * printf("%d\n", cur); + * } + * This will display the position of each bit in u: 1, 2, 4, 8 + * + * I should probably re-think the implementation... + */ +#define bit_for_each64(pos, tmp, ul) \ + for (tmp = ul, pos = ffs64(tmp); pos; tmp &= (tmp - 1), pos = ffs64(tmp)) + +/* +U64 ls1b = x & -x; // isolate LS1B + ... + x &= x-1; // reset LS1B +} +*/ + +#endif /* BITS_H */ diff --git a/src/chessdefs.h b/src/chessdefs.h index 5543af2..e0eabcc 100644 --- a/src/chessdefs.h +++ b/src/chessdefs.h @@ -14,11 +14,11 @@ #ifndef CHESSDEFS_H #define CHESSDEFS_H -#include +#include "bits.h" /* piece_t bits structure */ -typedef unsigned char piece_t; +typedef u8 piece_t; #define EMPTY 0 diff --git a/src/eval.c b/src/eval.c new file mode 100644 index 0000000..d867854 --- /dev/null +++ b/src/eval.c @@ -0,0 +1,85 @@ +/* eval.c - static position evaluation. + * + * Copyright (C) 2021 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 "eval.h" +#include "list.h" +#include "debug.h" + +eval_t eval(pos_t *pos) +{ + eval_t eval[2] = {0}, res = 0; + struct list_head *p_cur, *p_tmp, *list; + piece_list_t *piece; + + /* 1) pieces value + */ + for (int color=WHITE; color <=BLACK; ++color) { + list = &pos->pieces[color]; + list_for_each_safe(p_cur, p_tmp, list) { + piece = list_entry(p_cur, piece_list_t, list); + eval[color] += piece->value; + } + } + + res += eval[WHITE] - eval[BLACK]; +# ifdef DEBUG_EVAL + log_f(2, "pieces: W:%lu B:%lu diff=%lu eval=\n", eval[WHITE], eval[BLACK], + res); +# endif + + /* 2) square control + */ + eval[WHITE] = popcount64(pos->controlled[WHITE]); + eval[BLACK] = popcount64(pos->controlled[BLACK]); + res += eval[WHITE] - eval[BLACK]; +# ifdef DEBUG_EVAL + log_f(2, "square control: W:%lu B:%lu eval=%lu\n", eval[WHITE], eval[BLACK], + res); +# endif + + /* 3) mobility + */ + + + return res; +} + +#ifdef BIN_eval +#include "fen.h" +#include "move.h" + +int main(int ac, char**av) +{ + pos_t *pos; + + debug_init(2); + piece_pool_init(); + moves_pool_init(); + pos = pos_create(); + + if (ac == 1) { + pos_startpos(pos); + } else { + fen2pos(pos, av[1]); + } + + moves_gen(pos, WHITE); + moves_gen(pos, BLACK); + pos_print(pos); + pos_pieces_print(pos); + + printf("eval=%lu\n", eval(pos)); +} +#endif diff --git a/src/eval.h b/src/eval.h new file mode 100644 index 0000000..fadf0bf --- /dev/null +++ b/src/eval.h @@ -0,0 +1,21 @@ +/* eval.h - static position evaluation. + * + * Copyright (C) 2021 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 EVAL_H +#define EVAL_H + +#include "position.h" + +eval_t eval(pos_t *pos); + +#endif /* EVAL_H */ diff --git a/src/move.c b/src/move.c index 1dafa56..4f55d93 100644 --- a/src/move.c +++ b/src/move.c @@ -120,7 +120,7 @@ static move_t *move_add(pos_t *pos, piece_t piece, square_t from, move_t *move; # ifdef DEBUG_MOVE - log_i(2, "piece_color=%d turn=%d from=%c%c to=%c%c\n", + log_i(3, "piece_color=%d turn=%d from=%c%c to=%c%c\n", COLOR(piece), pos->turn, FILE2C(GET_F(from)), RANK2C(GET_R(from)), @@ -141,7 +141,7 @@ static move_t *move_add(pos_t *pos, piece_t piece, square_t from, move->flags |= M_CAPTURE; list_add(&move->list, &pos->moves); # ifdef DEBUG_MOVE - log_i(2, "added move from %c%c to %c%c\n", + log_i(3, "added move from %c%c to %c%c\n", FILE2C(GET_F(move->from)), RANK2C(GET_R(move->from)), FILE2C(GET_F(move->to)), @@ -247,7 +247,7 @@ int pseudo_moves_pawn(pos_t *pos, piece_list_t *ppiece) int two=0; for (new = square + dir * 15; two < 2; new = square + dir * 17, two++) { # ifdef DEBUG_MOVE - log_i(3, "pawn capture %#04x %#04x\n", square, new); + log_i(4, "pawn capture %#04x %#04x\n", square, new); # endif if (SQ88_NOK(new)) continue; @@ -319,14 +319,14 @@ int pseudo_moves_gen(pos_t *pos, piece_list_t *ppiece) int count = 0; # ifdef DEBUG_MOVE - log_f(2, "pos:%p turn:%s piece:%d [%s %s] at %#04x[%c%c]\n", + log_f(4, "pos:%p turn:%s piece:%d [%s %s] at %#04x[%c%c]\n", pos, pos->turn ==WHITE? "white": "black", piece, color==WHITE? "white": "black", P_NAME(piece), square, FILE2C(GET_F(square)), RANK2C(GET_R(square))); - log_i(4, "vector=%ld ndirs=%d slide=%d\n", vector-vectors, ndirs, slide); + log_i(5, "vector=%ld ndirs=%d slide=%d\n", vector-vectors, ndirs, slide); # endif for (int curdir = 0; curdir < ndirs; ++curdir) { @@ -340,18 +340,18 @@ int pseudo_moves_gen(pos_t *pos, piece_list_t *ppiece) break; } # ifdef DEBUG_MOVE - log_i(3, "trying %c%c\n", FILE2C(GET_F(new)), RANK2C(GET_R(new))); + log_i(4, "trying %c%c\n", FILE2C(GET_F(new)), RANK2C(GET_R(new))); # endif pos->controlled[color] |= (1ULL << BB(FILE88(new), RANK88(new))); if (board[new].piece) { # ifdef DEBUG_MOVE - log_i(4, "color=%d color2=%d\n", color, COLOR(board[new].piece)); + log_i(5, "color=%d color2=%d\n", color, COLOR(board[new].piece)); # endif /* own color on dest square */ if (COLOR(board[new].piece) == color) { # ifdef DEBUG_MOVE - log_i(3, "skipping %04x (same color piece)\n", new); + log_i(5, "skipping %04x (same color piece)\n", new); # endif break; } diff --git a/src/piece.c b/src/piece.c index 9d6f5bf..e246b49 100644 --- a/src/piece.c +++ b/src/piece.c @@ -74,7 +74,7 @@ piece_list_t *piece_add(pos_t *pos, piece_t piece, square_t square) # endif if ((new = pool_get(pieces_pool))) { list_add_tail(&new->list, &pos->pieces[color]); - //color? &pos->pieces_black: &pos->pieces_white); + //color? &pos->pieces_black: &pos->pieces_white); new->piece = piece; new->square = square; new->castle = 0; diff --git a/src/pool.c b/src/pool.c index 70917e0..dab0714 100644 --- a/src/pool.c +++ b/src/pool.c @@ -94,7 +94,7 @@ void *pool_get(pool_t *pool) void *cur; uint32_t i; # ifdef DEBUG_POOL - log_f(2, "[%s]: growing pool from %u to %u elements.\n", + log_f(1, "[%s]: growing pool from %u to %u elements.\n", pool->name, pool->allocated, pool->allocated + pool->growsize); diff --git a/src/position.h b/src/position.h index f5792d3..8da725f 100644 --- a/src/position.h +++ b/src/position.h @@ -22,8 +22,8 @@ typedef struct pos_s { piece_t turn; /* we use only color bit */ castle_t castle; - short clock_50; - short curmove; + uint16_t clock_50; + uint16_t curmove; eval_t eval; board_t *board;