51 Commits

Author SHA1 Message Date
345834159a change submodule url to relative 2023-12-29 16:35:19 +01:00
09633a4287 replace libs with brlib submodule 2023-12-29 09:44:33 +01:00
cb9c81e8f9 edit env.sh 2023-12-29 08:31:30 +01:00
072f3ced9b Makefile/.gitignore: cleanup (cont.) 2023-12-15 09:17:12 +01:00
77bde22d00 add brlib's bits.c (to remove problematic logs in bits.h) 2023-12-13 21:35:57 +01:00
e2d32a7300 Makefile: Hure rewriting/simplification (!) 2023-12-13 21:31:01 +01:00
01c5765888 remove -DBIN_xxx from brchess source files. parts moved to ./test (TODO) 2023-12-13 21:30:39 +01:00
a0ccad58e5 bits.h: remove logs in macros 2023-12-11 15:05:57 +01:00
be790056f6 Makefile: fix/simplify binaries generation 2023-12-11 15:02:31 +01:00
e0da38e697 debug.[ch]: remove dependancies from bits.h 2023-12-11 14:59:30 +01:00
11d3501a35 copyright dates 2023-11-15 12:28:57 +01:00
48b5420830 Add check array in position structure 2023-07-25 06:54:33 +02:00
f27b649503 typo in bits.c 2023-07-14 23:14:57 +02:00
605ef7d201 add first TODO (valgrind/mem check) 2023-07-14 22:15:33 +02:00
aa1e9fdeda add license 2023-07-14 21:46:10 +02:00
d3c78cb0af remove TOC 2023-07-14 21:05:44 +02:00
4ff9df9369 Initial STATUS.org 2023-07-14 21:00:29 +02:00
b3fde55107 .gitignore: valgrind.out 2023-07-14 08:45:46 +02:00
f2d4f07069 valgrind: ignore libreadline errors 2023-07-14 08:35:47 +02:00
b855ba59aa do not generate moves in pvs() terminal nodes 2023-07-13 10:39:36 +02:00
20403a0795 improve search() and pvs() output 2023-07-13 09:49:02 +02:00
c7e2aec77c Move bitboards constants to bitboard.h 2023-07-13 09:48:16 +02:00
754b011d05 fix _moves_gen_eval_sort() 2023-07-12 21:34:04 +02:00
e2a3563fce use eval_simple() as base for eval() 2023-07-12 21:32:26 +02:00
d852e0bc1d adjust nodecounts, fix PVS 2023-07-12 21:31:27 +02:00
3de87daa5a bug fix in pos_dup() 2023-07-12 21:30:24 +02:00
b5ed42746e cosmetic changes 2023-07-12 21:29:51 +02:00
0ca495576d add simple_eval and pvs. DO NOT USE PVS (need to use simple_eval) 2023-07-11 22:24:26 +02:00
ed9b9cc646 add nodes_count and moves_{generated,counted} in pos struct 2023-07-11 22:22:33 +02:00
88d2d4061f add moves sort 2023-07-11 22:21:42 +02:00
9932a64c97 add pvs(), aka Principal Variation Search 2023-07-11 22:20:43 +02:00
af1f5db507 add BB flip_V and FLIP_H macros 2023-07-11 22:13:06 +02:00
d76c10797a add debug_timer_elapsed() 2023-07-10 18:35:17 +02:00
36aa34a38b add list_sort lib 2023-07-10 18:33:58 +02:00
d9f03acb02 fix piece color in move_do 2023-07-10 13:17:26 +02:00
683b6ad66b add debug_level_get 2023-07-10 13:11:38 +02:00
b1e6461f6f Add DEBUG_DEBUG_C 2023-07-10 13:10:53 +02:00
65f1bef987 cleanup 2023-07-09 16:22:42 +02:00
23e49f463e add Emacs .dirs-local.el 2023-07-09 16:04:20 +02:00
48319cf21a add M_PR_NL option in move_print() 2023-07-09 15:53:57 +02:00
0b787c8a90 Add negamax function (no α β pruning) 2023-07-09 15:44:50 +02:00
4bca805404 add move_t forward decl. 2023-07-09 15:42:41 +02:00
0df87ff41c Add bestmove in pos struct, add pos_check() 2023-07-09 15:36:14 +02:00
892bdcd004 debug: Add flush option 2023-07-09 15:33:37 +02:00
120a459206 add pos_del function 2023-07-07 15:53:57 +02:00
7952a34c88 Add pos negamax function 2023-07-07 15:53:26 +02:00
531bfa4fb0 remove unused lib includes 2023-07-07 12:28:22 +02:00
12f972e152 move.c: Code cleanup, add promotion in move_do 2023-07-07 12:11:17 +02:00
183e9ef2be Improve move gen, delete ling pos, 3 steps moves generation 2023-07-07 02:21:14 +02:00
6f7a04cc89 Add move_do (dup position), remove &board in move struct 2023-07-06 12:22:26 +02:00
a08b006b98 Add pre-processor and assembly generation 2023-07-06 12:19:00 +02:00
54 changed files with 1702 additions and 4621 deletions

3
.dir-locals.el Normal file
View File

@@ -0,0 +1,3 @@
((nil . ((compile-command . (concat "make -C "
(vc-root-dir)
" -k -j2")))))

24
.gitignore vendored
View File

@@ -1,21 +1,17 @@
core
vgcore.*
GPATH
GRTAGS
GTAGS
fen
pool
piece
move
bits
eval
debug
brchess
*.o
*.s
*.i
*.old
*.save
/test/
/.ccls-root
/.ccls-cache/
/obj/
/lib/
/.deps/
/libobj/
/bin/
/dep/
/tmp/
/notused/
valgrind.out
compile_commands.json

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "brlib"]
path = brlib
url = ../brlib.git

View File

@@ -1,30 +0,0 @@
4k3/4p3/8/b7/1BR1p2p/1Q3P2/5N2/4K3 w - - 0 1
r1bq1rk1/pppp1ppp/2n2n2/4p3/2B1P3/3PPN2/PPP3PP/RN1QK2R b KQ - 1 7
6k1/6pp/R2p4/p1p5/8/1P1r3P/6P1/6K1 b - - 3 37
# both can castle queen only
r3k2r/8/3B4/8/8/3b4/8/R3K2R w KQkq - 0 1
r3k2r/8/3BB3/8/8/3bb3/8/R3K2R w KQkq - 0 1
r2bkb1r/8/8/8/8/3bb3/8/R2BKB1R w KQkq - 0 1
# illegal positions
4k3/8/8/8/7b/8/8/4K3 b - - 0 1
2r1k3/3P4/8/8/8/8/8/4K3 w - - 0 1
# only kings on A1/H8
k7/8/8/8/8/8/8/K7 b - - 0 1
# only one move possible (Pf2xBg3)
k7/8/8/1p1p4/pPpPp3/P1PpPpb1/NBNP1P2/KBB1B3 w - - 0 1
# only 2 moves possible (Ph5xg6 e.p., Ph5-h6)
k7/8/8/1p1p2pP/pPpPp3/P1PpPp2/NBNP1P2/KBB1B3 w - g6 0 1
# 2 Kings, W/B/ pawns on 7th for promotion
k4n2/4P3/8/8/8/8/4p3/K4N2 w - - 0 1
# white castled, and can e.p. on c6 black can castle
# white is a pawn down
# white has 35 or 36 moves: P=11 + 1 e.p. N=6+3 B=5+5 R=1 Q=3 K=1 + 1 e.p.
# black has 32 or 33 moves: P=11 N=2+7 B=5 R=3 Q=3 K=1 + castle
rnbqk2r/pp1pbpp1/7p/2pPp3/4n3/3B1N2/PPP2PPP/RNBQ1RK1 w kq c6 0 7

304
Makefile
View File

@@ -12,36 +12,65 @@
SHELL := /bin/bash
CC := gcc
LD := ld
BEAR := bear
TOUCH := touch
RM := rm
CCLSFILE := compile_commands.json
RMDIR := rmdir
SRCDIR := ./src
INCDIR := ./include
LIBSRCDIR := ./libsrc
BINDIR := ./bin
OBJDIR := ./obj
LIBSRCDIR := ./libsrc
LIBOBJDIR := ./libobj
BINDIR := ./bin
LIBDIR := ./lib
LDFLAGS := -L$(LIBDIR)
DEPDIR := ./dep
CCLSROOT := .ccls-root
CCLSFILE := compile_commands.json
SRC := $(wildcard $(SRCDIR)/*.c) # project sources
SRC_FN := $(notdir $(SRC)) # source basename
OBJ := $(addprefix $(OBJDIR)/,$(SRC_FN:.c=.o))
LIBSRC := $(wildcard $(LIBSRCDIR)/*.c) # lib sources
LIBOBJ := $(patsubst %.c,%.o,$(LIBSRC)) # and objects
LIBSRC_FN := $(notdir $(LIBSRC)) # lib sources basename
LIBOBJ := $(addprefix $(LIBOBJDIR)/,$(LIBSRC_FN:.c=.o)) # and lib obj ones
LIB := br_$(shell uname -m) # library name
SLIB := $(addsuffix .a, $(LIBDIR)/lib$(LIB)) # static lib
DLIB := $(addsuffix .so, $(LIBDIR)/lib$(LIB)) # dynamic lib
BIN := fen piece move eval brchess
DEP_FN := $(SRC_FN) $(LIBSRC_FN)
DEP := $(addprefix $(DEPDIR)/,$(DEP_FN:.c=.d))
TARGET_FN := brchess
TARGET := $(addprefix $(BINDIR)/,$(TARGET_FN))
LDFLAGS := -L$(LIBDIR)
LIBS := -l$(LIB) -lreadline -lncurses
CFLAGS := -std=gnu11
##################################### pre-processor flags
CPPFLAGS := -I$(INCDIR)
#CPPFLAGS += -DDEBUG # global
CPPFLAGS += -DDEBUG_DEBUG # enable log() functions
#CPPFLAGS += -DDEBUG_DEBUG_C # enable verbose log() settings
CPPFLAGS += -DDEBUG_POOL # memory pools management
CPPFLAGS += -DDEBUG_FEN # FEN decoding
CPPFLAGS += -DDEBUG_MOVE # move generation
CPPFLAGS += -DDEBUG_EVAL # eval functions
CPPFLAGS += -DDEBUG_PIECE # piece list management
CPPFLAGS += -DDEBUG_SEARCH # move search
#CFLAGS += -O2
# remove extraneous spaces (due to spaces before comments)
CPPFLAGS := $(strip $(CPPFLAGS))
##################################### compiler flags
CFLAGS := -std=gnu11
#CFLAGS += -O2
CFLAGS += -g
CFLAGS += -Wall
CFLAGS += -Wextra
@@ -52,149 +81,220 @@ CFLAGS += -Wmissing-declarations
# Next one may be useful for valgrind (when invalid instructions)
# CFLAGS += -mno-tbm
##################################### DEBUG flags
CPPFLAGS := -I$(INCDIR)
CPPFLAGS += -DDEBUG # global
CPPFLAGS += -DDEBUG_DEBUG # enable log() functions
CPPFLAGS += -DDEBUG_POOL # memory pools management
CPPFLAGS += -DDEBUG_FEN # FEN decoding
CPPFLAGS += -DDEBUG_MOVE # move generation
CPPFLAGS += -DDEBUG_EVAL # eval functions
CPPFLAGS += -DDEBUG_PIECE # piece list management
CFLAGS := $(strip $(CFLAGS))
##################################### archiver/linker/dependency flags
ARFLAGS := rcs
LDFLAGS := -L$(LIBDIR)
DEPFLAGS = -MMD -MP -MF $(DEPDIR)/$*.d
##################################### General targets
.PHONY: compile cflags all clean cleanall
.PHONY: all compile clean cleanall
compile: objects bin
all: $(TARGET)
cflags:
@echo CFLAGS: "$(CFLAGS)"
@echo CPPFLAGS: $(CPPFLAGS)
@echo DEPFLAGS: $(DEPFLAGS)
@echo LDFLAGS: $(LDFLAGS)
compile: libobjs objs
all: clean compile
clean: cleandep cleanobj cleanlib cleanlibobj cleanbin
clean: cleanobj cleanbin
cleanall: clean cleandepdir cleanobjdir cleanlibdir cleanlibobjdir cleanbindir
cleanall: clean cleandeps cleanlib
##################################### cleaning functions
# rmfiles - deletes a list of files in a directory if they exist.
# $(1): the directory
# $(2): the list of files to delete
# $(3): The string to include in action output - "cleaning X files."
# see: https://stackoverflow.com/questions/6783243/functions-in-makefiles
#
# Don't use wildcard like "$(DIR)/*.o", so we can control mismatches between
# list and actual files in directory.
# See rmdir below.
define rmfiles
@#echo "rmfiles=+$(1)+"
$(eval $@_EXIST = $(wildcard $(1)))
@#echo "existfile=+${$@_EXIST}+"
@if [[ -n "${$@_EXIST}" ]]; then \
echo "cleaning $(2) files." ; \
$(RM) ${$@_EXIST} ; \
fi
endef
# rmdir - deletes a directory if it exists.
# $(1): the directory
# $(2): The string to include in action output - "removing X dir."
#
# Don't use $(RM) -rf, to control unexpected dep files.
# See rmfile above.
define rmdir
@#echo "rmdir +$(1)+"
$(eval $@_EXIST = $(wildcard $(1)))
@#echo "existdir=+${$@_EXIST}+"
@if [[ -n "${$@_EXIST}" ]]; then \
echo "removing $(2) dir." ; \
$(RMDIR) ${$@_EXIST} ; \
fi
endef
##################################### dirs creation
.PHONY: alldirs
ALLDIRS := $(DEPDIR) $(OBJDIR) $(LIBDIR) $(LIBOBJDIR) $(BINDIR)
alldirs: $(ALLDIRS)
# Here, we have something like:
# a: a
# a will be built if (1) older than a, or (2) does not exist. Here only (2).
$(ALLDIRS): $@
@echo creating $@ directory.
@mkdir -p $@
##################################### Dependencies files
.PHONY: deps cleandeps
.PHONY: cleandep cleandepdir
DEPDIR := ./.deps
DEPFILES := $(addprefix $(DEPDIR)/,$(SRC_FN:.c=.d))
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d
-include $(wildcard $(DEP))
$(DEPFILES):
# Don't use $(DEPDIR)/*.d, to control mismatches between dep and src files.
# See second rule below.
cleandep:
$(call rmfiles,$(DEP),depend)
@#echo cleaning dependency files.
@#$(RM) -f $(DEP)
include $(wildcard $(DEPFILES))
cleandepdir:
$(call rmdir,$(DEPDIR),depend)
@#[ -d $(DEPDIR) ] && echo cleaning depend files && $(RM) -f $(DEP) || true
$(DEPDIR):
@echo creating $@ directory.
@mkdir -p $@
##################################### brchess objects
.PHONY: objs cleanobj cleanobjdir
cleandeps:
$(RM) -rf $(DEPDIR)
##################################### objects
.SECONDEXPANSION:
OBJ = $(addprefix $(OBJDIR)/,$(SRC_FN:.c=.o))
.PHONY: cleanobj
objects: $(OBJ)
objs: $(OBJ)
cleanobj:
$(RM) -rf $(OBJDIR)
$(call rmfiles,$(OBJ),object)
$(OBJDIR):
@echo creating $@ directory.
@mkdir -p $@
cleanobjdir: cleanobj
$(call rmdir,$(OBJDIR),objects)
$(OBJDIR)/%.o: $(SRCDIR)/%.c $(DEPDIR)/%.d | $(OBJDIR) $(DEPDIR)
@echo compiling $<.
@$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEPFLAGS) -I $(INCDIR) -o $@ $<
# 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 $< "->" $@.
@$(CC) -c $(DEPFLAGS) $(CPPFLAGS) $(CFLAGS) $< -o $@
##################################### br library
.PHONY: cleanlib libs
##################################### brlib objects
.PHONY: libobjs cleanlibobj cleanlibobjdir
ARFLAGS = r
libobjs: $(LIBOBJ)
cleanlibobj:
$(call rmfiles,$(LIBOBJ),brlib object)
cleanlibobjdir:
$(call rmdir,$(LIBOBJDIR),brlib objects)
$(LIBOBJDIR)/%.o: $(LIBSRCDIR)/%.c | $(LIBOBJDIR) $(DEPDIR)
@echo compiling library $< "->" $@.
$(CC) -c $(DEPFLAGS) $(CPPFLAGS) $(CFLAGS) $< -o $@
##################################### brlib libraries
.PHONY: libs cleanlib cleanlibdir
cleanlib:
$(RM) -rf $(LIBDIR) $(LIBOBJ)
$(call rmfiles,$(DLIB) $(SLIB),library)
cleanlibdir:
$(call rmdir,$(LIBDIR),libraries)
libs: $(DLIB) $(SLIB)
$(LIBDIR):
@echo creating $@ directory.
@mkdir -p $@
# remove default rule
%.o: %.c
$(LIBSRCDIR)/%.o: $(LIBSRCDIR)/%.c
@echo compiling library $< "->" $@.
@$(CC) -c $(CPPFLAGS) $(CFLAGS) -I $(INCDIR) -o $@ $<
$(SLIB): $(LIBOBJ) | $(LIBDIR)
@echo building $@ static library.
@$(AR) $(ARFLAGS) -o $@ $^
$(DLIB): CFLAGS += -fPIC
$(DLIB): LDFLAGS += -shared
$(DLIB): $(LIBOBJ) | $(LIBDIR)
@echo building $@ shared library.
@$(CC) $(LDFLAGS) $^ -o $@
@$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@
##################################### binaries
.PHONY: bin prebin cleanbin
$(SLIB): $(LIBOBJ) | $(LIBDIR)
@echo building $@ static library.
$(AR) $(ARFLAGS) $@ $^
BINMARK := .bindone
##################################### brchess binaries
.PHONY: targets cleanbin cleanbindir
bin: $(BIN) postbin
postbin:
@[[ ! -f $(BINMARK) ]] || echo done.
@$(RM) -f $(BINMARK)
targets: $(TARGET)
cleanbin:
$(RM) -f $(BIN) core
$(call rmfiles,$(TARGET),binary)
# TODO: find a better dependancy graph
$(BIN): $(SRCDIR)/$$@.c $(DLIB) $$(subst $(OBJDIR)/$$@.o,,$(OBJ))
@[[ -f $(BINMARK) ]] || echo -n "generating binaries: "
@echo -n "$@... "
@$(CC) -DBIN_$@ $(CPPFLAGS) $(CFLAGS) -I $(INCDIR) $(subst libs,,$^) $(LDFLAGS) $(LIBS) -o $@
@$(TOUCH) $(BINMARK)
cleanbindir:
$(call rmdir,$(BINDIR),binaries)
##################################### ccls
# We don't use static lib, but we could build it here
$(TARGET): $(DLIB) $(OBJ) | $(BINDIR) $(SLIB)
@echo generating $@ executable.
@$(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@
##################################### pre-processed (.i) and assembler (.s) output
%.i: %.c
@echo generating $@
@$(CC) -E $(CPPFLAGS) $(CFLAGS) $< -o $@
%.s: %.c
@echo generating $@
@$(CC) -S -fverbose-asm $(CPPFLAGS) $(CFLAGS) $< -o $@
##################################### LSP (ccls)
.PHONY: ccls
ccls: $(CCLSFILE)
# generate compile_commands.json
$(CCLSFILE): brchess Makefile
$(BEAR) -- make clean bin
##################################### LSP (ccls)
.PHONY: bear
ROOTDIR = .ccls-root
$(CCLSROOT):
@echo creating root marker file.
@echo creating project root file.
@$(TOUCH) $@
bear: clean $(CCLSROOT)
@touch .ccls-root
# generate compile_commands.json.
# Need to add includes and Makefile dependencies.
# also, if cclsfile is newer than sources, no need to clean objects file
# (and to run bear).
# maybe run cleanobj cleanlibobj in commands ?
$(CCLSFILE): cleanobj cleanlibobj $(SRC) $(LIBSRC) | $(CCLSROOT)
@echo "Generating ccls compile commands file ($@)."
@$(BEAR) -- make compile
##################################### LSP (ccls)
#.PHONY: bear
#bear: cleanobj cleanlibobj Makefile | $(CCLSROOT)
# @$(BEAR) -- make compile
##################################### valgrind (mem check)
.PHONY: memcheck
VALGRIND = valgrind
VALGRINDFLAGS = --leak-check=full --show-leak-kinds=all
VALGRINDFLAGS += --track-origins=yes --sigill-diagnostics=yes
VALGRINDFLAGS += --quiet --show-error-list=yes
VALGRINDFLAGS += --log-file=valgrind.out
# We need to suppress libreadline leaks here. See :
# https://stackoverflow.com/questions/72840015
VALGRINDFLAGS += --suppressions=etc/libreadline.supp
memcheck: brchess
memcheck: targets
@$(VALGRIND) $(VALGRINDFLAGS) $(BINDIR)/brchess
##################################### Makefile debug
.PHONY: showflags wft
showflags:
@echo CFLAGS: "$(CFLAGS)"
@echo CPPFLAGS: $(CPPFLAGS)
@echo DEPFLAGS: $(DEPFLAGS)
@echo LDFLAGS: $(LDFLAGS)
@echo DEPFLAGS: $(DEPFLAGS)
wtf:
@printf "LIBOBJDIR=%s\n\n" "$(LIBOBJDIR)"
@printf "LIBOBJ=%s\n\n" "$(LIBOBJ)"
@printf "OBJDIR=%s\n\n" "$(OBJDIR)"
@printf "OBJ=%s\n\n" "$(OBJ)"
@#echo LIBOBJ=$(LIBOBJ)
@#echo DEP=$(DEP)
@#echo LIBSRC=$(LIBSRC)

1
README.org Normal file
View File

@@ -0,0 +1 @@
A basic chess program. The goal is not to make it good, but at least to be able to play a full game - Far to be the case !

57
STATUS.org Normal file
View File

@@ -0,0 +1,57 @@
#+OPTIONS: toc:nil
* Misc
- License: [[https://www.gnu.org/licenses/gpl-3.0-standalone.html][GNU General Public License v3.0 or later]].
- Code is in C (gnu11 = C11 + some GNU extensions), tested only on Linux x86-64.
- [[https://git.raoult.com/bruno/brchess][Source Code]].
* Done or partially done
*** Text interface
- basic commands like "init", "fen xxxx", "depth n", "search", "pvs", etc...
*** Dual [[https://en.wikipedia.org/wiki/0x88][0x88]] / [[https://en.wikipedia.org/wiki/Bitboard#Chess_bitboards][bitboard]] representations
- I started with a [[https://en.wikipedia.org/wiki/0x88][0x88 board representation]]
- But... wanted to switch to [[https://en.wikipedia.org/wiki/Bitboard#Chess_bitboards][bitboard]] lately
- Today there is a messy mix of the two representations.
*** A [[https://www.chessprogramming.org/Pseudo-Legal_Move][pseudo-legal move]] generator
- Does not check for some invalid moves (especially king-pinned pieces moves, which could be very expensive).
I believe some programs do not do it too, and prefer to see invalid positions at next ply (TODO).
- Still with 0x88 board (needs to be rewritten with bitboards).
*** *Very expensive* pieces and moves list
- They should be converted into arrays, to allow fast duplication for move_do(), the function which actually makes a move.
*** A basic eval function
- preferred squares for pieces
- mobility
*** "Move Search" functions
**** Basic [[https://en.wikipedia.org/wiki/Negamax][negamax]] search function
- No [[https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning][alpha-beta pruning]]
**** [[https://en.wikipedia.org/wiki/Principal_variation_search][Principal Variation Search]] (PVS) function
- Alpha-beta pruning
- Basic moves [[https://www.chessprogramming.org/Move_Ordering][pre-ordering]]
It makes the PVS/alpha-beta pruning quite decent. For example, a 6 ply depth search gives:
- negamax: 1,196 secs for 125,799 Knodes
- PVS: 14 secs for 1,575 Knodes
- Both make search at a fixed depth (still no [[https://www.chessprogramming.org/Quiescence_Search][quiescence search]] at terminal nodes).
* Missing
*** All Chess rules
- Mate/Pat detection (!)
Not trivial, as using pseudo-valid moves implies mate/pat detection is late
- Special rules like 50 moves/position repetition
*** Actual game management
- Play a move, ask engine to play a move
- A standard interface for usual software (like SCID)
Probably [[https://www.gnu.org/software/xboard/engine-intf.html][xboard]] first, as it looks simpler than [[https://en.wikipedia.org/wiki/Universal_Chess_Interface][UCI]].
*** Search improvement
- Book-keeping of moves during search (did not decide the method).
- [[https://en.wikipedia.org/wiki/Zobrist_hashing][Positions hashing]] / transposition detection
* Next steps planned (no specific order)
*** Memory cleanup
- proper recusive position/moves cleanup in search() and pvs()
- use valgrind to find possible other unfreed memory
*** Replace the current interface with a basic xboard one
1. Check which commands are necessary/mandatory.
1. This will allow easy testing with common software
*** Mate/Pat detection
*** In Search, detect King capture
*** In Search, do something when no move available

1
brlib Submodule

Submodule brlib added at 97d6570be7

1
env.sh
View File

@@ -1 +0,0 @@
export LD_LIBRARY_PATH=./lib

6
etc/libreadline.supp Normal file
View File

@@ -0,0 +1,6 @@
{
ignore_libreadline_leaks
Memcheck:Leak
...
obj:*/libreadline.so.*
}

View File

@@ -1,539 +0,0 @@
/* bits.h - bits functions.
*
* Copyright (C) 2021-2022 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>
*
*/
#ifndef _BITS_H
#define _BITS_H
#include <stdint.h>
#include <stdbool.h>
/* next include will define __WORDSIZE: 32 or 64
*/
#include <bits/wordsize.h>
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
/* no plan to support 32bits for now...
* #if __WORDSIZE != 64
* #error "Only 64 bits word size supported."
* #endif
*/
/* fixed-size types
*/
typedef int64_t s64;
typedef int32_t s32;
typedef int16_t s16;
typedef int8_t s8;
typedef uint64_t u64;
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;
/* convenience types
*/
typedef long long int llong;
typedef unsigned long long int ullong;
typedef unsigned long int ulong;
typedef unsigned int uint;
typedef unsigned short ushort;
typedef unsigned char uchar;
/* char is a special case, as it can be signed or unsigned
*/
typedef signed char schar;
/* define common types sizes
*/
#define BITS_PER_CHAR 8
#ifndef BITS_PER_SHORT
#define BITS_PER_SHORT (BITS_PER_CHAR * sizeof (short))
#endif
#ifndef BITS_PER_INT
#define BITS_PER_INT (BITS_PER_CHAR * sizeof (int))
#endif
#ifndef BITS_PER_LONG
#define BITS_PER_LONG (BITS_PER_CHAR * sizeof (long))
#endif
#ifndef BITS_PER_LLONG
#define BITS_PER_LLONG (BITS_PER_CHAR * sizeof (long long))
#endif
/* count set bits: 10101000 -> 3
* ^ ^ ^
*/
static __always_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
}
static __always_inline int popcount32(u32 n)
{
# if __has_builtin(__builtin_popcount)
# ifdef DEBUG_BITS
log_f(1, "builtin.\n");
# endif
return __builtin_popcount(n);
# else
# ifdef DEBUG_BITS
log_f(1, "emulated.\n");
# endif
int count = 0;
while (n) {
count++;
n &= (n - 1);
}
return count;
# endif
}
/* count trailing zeroes : 00101000 -> 3
* ^^^
*/
static __always_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
}
static __always_inline int ctz32(u32 n)
{
# if __has_builtin(__builtin_ctz)
# ifdef DEBUG_BITS
log_f(1, "builtin ctz.\n");
# endif
return __builtin_ctzl(n);
# elif __has_builtin(__builtin_clz)
# ifdef DEBUG_BITS
log_f(1, "builtin clz.\n");
# endif
return __WORDSIZE - (__builtin_clz(n & -n) + 1);
# else
# ifdef DEBUG_BITS
log_f(1, "emulated.\n");
# endif
return popcount32((n & -n) - 1);
# endif
}
/* clz - count leading zeroes : 00101000 -> 2
* ^^
*/
static __always_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 64 - r - 1;
# endif
}
static __always_inline int clz32(u32 n)
{
# if __has_builtin(__builtin_clz)
# ifdef DEBUG_BITS
log_f(1, "builtin.\n");
# endif
return __builtin_clz(n);
# else
# ifdef DEBUG_BITS
log_f(1, "emulated.\n");
# endif
u32 r, q;
r = (n > 0xFFFF) << 4; n >>= r;
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 32 - r - 1;
# endif
}
/* fls - find last set : 00101000 -> 6
* ^
*/
static __always_inline int fls64(u64 n)
{
if (!n)
return 0;
return 64 - clz64(n);
}
static __always_inline int fls32(u32 n)
{
if (!n)
return 0;
return 32 - clz32(n);
}
/* find first set : 00101000 -> 4
* ^
*/
static __always_inline uint ffs64(u64 n)
{
# if __has_builtin(__builtin_ffsl)
# ifdef DEBUG_BITS
log_f(1, "builtin ffsl.\n");
# endif
return __builtin_ffsl(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 __always_inline uint ffs32(u32 n)
{
# if __has_builtin(__builtin_ffs)
# ifdef DEBUG_BITS
log_f(1, "builtin ffs.\n");
# endif
return __builtin_ffs(n);
# elif __has_builtin(__builtin_ctz)
# ifdef DEBUG_BITS
log_f(1, "builtin ctz.\n");
# endif
if (n == 0)
return (0);
return __builtin_ctz(n) + 1;
# else
# ifdef DEBUG_BITS
log_f(1, "emulated.\n");
# endif
return popcount32(n ^ ~-n);
# endif
}
/* rolXX are taken from kernel's <linux/bitops.h> are are:
* SPDX-License-Identifier: GPL-2.0
*/
/**
* rol64 - rotate a 64-bit value left
* @word: value to rotate
* @shift: bits to roll
*/
static inline u64 rol64(u64 word, unsigned int shift)
{
return (word << (shift & 63)) | (word >> ((-shift) & 63));
}
/**
* ror64 - rotate a 64-bit value right
* @word: value to rotate
* @shift: bits to roll
*/
static inline u64 ror64(u64 word, unsigned int shift)
{
return (word >> (shift & 63)) | (word << ((-shift) & 63));
}
/**
* rol32 - rotate a 32-bit value left
* @word: value to rotate
* @shift: bits to roll
*/
static inline u32 rol32(u32 word, unsigned int shift)
{
return (word << (shift & 31)) | (word >> ((-shift) & 31));
}
/**
* ror32 - rotate a 32-bit value right
* @word: value to rotate
* @shift: bits to roll
*/
static inline u32 ror32(u32 word, unsigned int shift)
{
return (word >> (shift & 31)) | (word << ((-shift) & 31));
}
/**
* rol16 - rotate a 16-bit value left
* @word: value to rotate
* @shift: bits to roll
*/
static inline u16 rol16(u16 word, unsigned int shift)
{
return (word << (shift & 15)) | (word >> ((-shift) & 15));
}
/**
* ror16 - rotate a 16-bit value right
* @word: value to rotate
* @shift: bits to roll
*/
static inline u16 ror16(u16 word, unsigned int shift)
{
return (word >> (shift & 15)) | (word << ((-shift) & 15));
}
/**
* rol8 - rotate an 8-bit value left
* @word: value to rotate
* @shift: bits to roll
*/
static inline u8 rol8(u8 word, unsigned int shift)
{
return (word << (shift & 7)) | (word >> ((-shift) & 7));
}
/**
* ror8 - rotate an 8-bit value right
* @word: value to rotate
* @shift: bits to roll
*/
static inline u8 ror8(u8 word, unsigned int shift)
{
return (word >> (shift & 7)) | (word << ((-shift) & 7));
}
/**
* ilog2 -
*/
static __always_inline __attribute__((const))
int __ilog2_u32(u32 n)
{
return fls32(n) - 1;
}
static __always_inline __attribute__((const))
int __ilog2_u64(u64 n)
{
return fls64(n) - 1;
}
/**
* is_power_of_2() - check if a value is a power of two
* @n: the value to check
*
* Determine whether some value is a power of two, where zero is
* *not* considered a power of two.
* Return: true if @n is a power of 2, otherwise false.
*/
static inline __attribute__((const))
bool is_power_of_2(unsigned long n)
{
return (n != 0 && ((n & (n - 1)) == 0));
}
/**
* ilog2 - log base 2 of 32-bit or a 64-bit unsigned value
* @n: parameter
*
* constant-capable log of base 2 calculation
* - this can be used to initialise global variables from constant data, hence
* the massive ternary operator construction
*
* selects the appropriately-sized optimised version depending on sizeof(n)
*/
#define ilog2(n) \
( \
__builtin_constant_p(n) ? \
((n) < 2 ? 0 : \
63 - __builtin_clzll(n)) : \
(sizeof(n) <= 4) ? \
__ilog2_u32(n) : \
__ilog2_u64(n) \
)
/**
* roundup_pow_of_two - round the given value up to nearest power of two
* @n: parameter
*
* round the given value up to the nearest power of two
* - the result is undefined when n == 0
* - this can be used to initialise global variables from constant data
*/
#define roundup_pow_of_two(n) \
( \
__builtin_constant_p(n) ? ( \
((n) == 1) ? 1 : \
(1UL << (ilog2((n) - 1) + 1)) \
) : \
__roundup_pow_of_two(n) \
)
/**
* rounddown_pow_of_two - round the given value down to nearest power of two
* @n: parameter
*
* round the given value down to the nearest power of two
* - the result is undefined when n == 0
* - this can be used to initialise global variables from constant data
*/
#define rounddown_pow_of_two(n) \
( \
__builtin_constant_p(n) ? ( \
(1UL << ilog2(n))) : \
__rounddown_pow_of_two(n) \
)
static inline __attribute_const__
int __order_base_2(unsigned long n)
{
return n > 1 ? ilog2(n - 1) + 1 : 0;
}
/**
* order_base_2 - calculate the (rounded up) base 2 order of the argument
* @n: parameter
*
* The first few values calculated by this routine:
* ob2(0) = 0
* ob2(1) = 0
* ob2(2) = 1
* ob2(3) = 2
* ob2(4) = 2
* ob2(5) = 3
* ... and so on.
*/
#define order_base_2(n) \
( \
__builtin_constant_p(n) ? ( \
((n) == 0 || (n) == 1) ? 0 : \
ilog2((n) - 1) + 1) : \
__order_base_2(n) \
)
static inline __attribute__((const))
int __bits_per(unsigned long n)
{
if (n < 2)
return 1;
if (is_power_of_2(n))
return order_base_2(n) + 1;
return order_base_2(n);
}
/**
* bits_per - calculate the number of bits required for the argument
* @n: parameter
*
* This is constant-capable and can be used for compile time
* initializations, e.g bitfields.
*
* The first few values calculated by this routine:
* bf(0) = 1
* bf(1) = 1
* bf(2) = 2
* bf(3) = 2
* bf(4) = 3
* ... and so on.
*/
#define bits_per(n) \
( \
__builtin_constant_p(n) ? ( \
((n) == 0 || (n) == 1) \
? 1 : ilog2(n) + 1 \
) : \
__bits_per(n) \
)
/** bit_for_each - iterate over an u64/u32 bits
* @pos: an int used as current bit
* @tmp: a temp u64/u32 used as temporary storage
* @ul: the u64/u32 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 set in ul: 1, 2, 4, 8
*
* I should probably re-think the implementation...
*/
#define bit_for_each64(pos, tmp, ul) \
for (tmp = ul, pos = ffs64(tmp); tmp; tmp &= (tmp - 1), pos = ffs64(tmp))
#define bit_for_each32(pos, tmp, ul) \
for (tmp = ul, pos = ffs32(tmp); tmp; tmp &= (tmp - 1), pos = ffs32(tmp))
/** or would it be more useful (counting bits from zero instead of 1) ?
*/
#define bit_for_each64_2(pos, tmp, ul) \
for (tmp = ul, pos = ctz64(tmp); tmp; tmp ^= 1UL << pos, pos = ctz64(tmp))
#define bit_for_each32_2(pos, tmp, ul) \
for (tmp = ul, pos = ctz32(tmp); tmp; tmp ^= 1U << pos, pos = ctz32(tmp))
#endif /* _BITS_H */

View File

@@ -1,224 +0,0 @@
/* br.h - misc macros.
*
* Copyright (C) 2021-2022 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>
*
* Some parts are taken from Linux's kernel <linux/kernel.h> and others, and are :
* SPDX-License-Identifier: GPL-2.0
*
* This header contains generic stuff.
*/
#ifndef _BR_H
#define _BR_H
#include "struct-group.h"
/* Indirect stringification. Doing two levels allows the parameter to be a
* macro itself. For example, compile with -DFOO=bar, __stringify(FOO)
* converts to "bar".
*/
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)
/* generate a (maybe) unique id.
*/
#define ___PASTE(x, y) x##y
#define __PASTE(x, y) ___PASTE(x, y)
#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
/* unused/used parameters/functions
* https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-unused-function-attribute
* https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#index-unused-type-attribute
* https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-unused-variable-attribute
* https://gcc.gnu.org/onlinedocs/gcc/Label-Attributes.html#index-unused-label-attribute
* https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-used-function-attribute
* https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-used-variable-attribute
*/
#define __unused __attribute__((__unused__))
#define __used __attribute__((__used__))
/* see https://lkml.org/lkml/2018/3/20/845 for explanation of this monster
*/
#define __is_constexpr(x) \
(sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
/*
* min()/max()/clamp() macros must accomplish three things:
*
* - avoid multiple evaluations of the arguments (so side-effects like
* "x++" happen only once) when non-constant.
* - perform strict type-checking (to generate warnings instead of
* nasty runtime surprises). See the "unnecessary" pointer comparison
* in __typecheck().
* - retain result as a constant expressions when called with only
* constant expressions (to avoid tripping VLA warnings in stack
* allocation usage).
*/
#define __typecheck(x, y) \
(!!(sizeof((typeof(x) *)1 == (typeof(y) *)1)))
#define __no_side_effects(x, y) \
(__is_constexpr(x) && __is_constexpr(y))
#define __safe_cmp(x, y) \
(__typecheck(x, y) && __no_side_effects(x, y))
#define __cmp(x, y, op) ((x) op (y) ? (x) : (y))
#define __cmp_once(x, y, unique_x, unique_y, op) ({ \
typeof(x) unique_x = (x); \
typeof(y) unique_y = (y); \
__cmp(unique_x, unique_y, op); })
#define __careful_cmp(x, y, op) \
__builtin_choose_expr(__safe_cmp(x, y), \
__cmp(x, y, op), \
__cmp_once(x, y, __UNIQUE_ID(__x), __UNIQUE_ID(__y), op))
#define __pure __attribute__((__pure__))
/**
* min - return minimum of two values of the same or compatible types
* @x: first value
* @y: second value
*/
#define min(x, y) __careful_cmp(x, y, <)
/**
* max - return maximum of two values of the same or compatible types
* @x: first value
* @y: second value
*/
#define max(x, y) __careful_cmp(x, y, >)
/**
* min3 - return minimum of three values
* @x: first value
* @y: second value
* @z: third value
*/
#define min3(x, y, z) min((typeof(x))min(x, y), z)
/**
* max3 - return maximum of three values
* @x: first value
* @y: second value
* @z: third value
*/
#define max3(x, y, z) max((typeof(x))max(x, y), z)
/**
* min_not_zero - return the minimum that is _not_ zero, unless both are zero
* @x: value1
* @y: value2
*/
#define min_not_zero(x, y) ({ \
typeof(x) __x = (x); \
typeof(y) __y = (y); \
__x == 0 ? __y : ((__y == 0) ? __x : min(__x, __y)); })
/**
* clamp - return a value clamped to a given range with strict typechecking
* @val: current value
* @lo: lowest allowable value
* @hi: highest allowable value
*
* This macro does strict typechecking of @lo/@hi to make sure they are of the
* same type as @val. See the unnecessary pointer comparisons.
*/
#define clamp(val, lo, hi) min((typeof(val))max(val, lo), hi)
/*
* ..and if you can't take the strict
* types, you can specify one yourself.
*
* Or not use min/max/clamp at all, of course.
*/
/**
* min_t - return minimum of two values, using the specified type
* @type: data type to use
* @x: first value
* @y: second value
*/
#define min_t(type, x, y) __careful_cmp((type)(x), (type)(y), <)
/**
* max_t - return maximum of two values, using the specified type
* @type: data type to use
* @x: first value
* @y: second value
*/
#define max_t(type, x, y) __careful_cmp((type)(x), (type)(y), >)
/**
* clamp_t - return a value clamped to a given range using a given type
* @type: the type of variable to use
* @val: current value
* @lo: minimum allowable value
* @hi: maximum allowable value
*
* This macro does no typechecking and uses temporary variables of type
* @type to make all the comparisons.
*/
#define clamp_t(type, val, lo, hi) min_t(type, max_t(type, val, lo), hi)
/**
* clamp_val - return a value clamped to a given range using val's type
* @val: current value
* @lo: minimum allowable value
* @hi: maximum allowable value
*
* This macro does no typechecking and uses temporary variables of whatever
* type the input argument @val is. This is useful when @val is an unsigned
* type and @lo and @hi are literals that will otherwise be assigned a signed
* integer type.
*/
#define clamp_val(val, lo, hi) clamp_t(typeof(val), val, lo, hi)
/**
* swap - swap values of @a and @b
* @a: first value
* @b: second value
*/
#define swap(a, b) \
do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0)
/**
* ARRAY_SIZE - get the number of elements in array @arr
* @arr: array to be sized
*/
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/**
* abs - return absolute value of an argument
* @x: the value. If it is unsigned type, it is converted to signed type first.
* char is treated as if it was signed (regardless of whether it really is)
* but the macro's return type is preserved as char.
*
* Return: an absolute value of x.
*/
#define abs(x) __abs_choose_expr(x, long long, \
__abs_choose_expr(x, long, \
__abs_choose_expr(x, int, \
__abs_choose_expr(x, short, \
__abs_choose_expr(x, char, \
__builtin_choose_expr( \
__builtin_types_compatible_p(typeof(x), char), \
(char)({ signed char __x = (x); __x<0?-__x:__x; }), \
((void)0)))))))
#define __abs_choose_expr(x, type, other) __builtin_choose_expr( \
__builtin_types_compatible_p(typeof(x), signed type) || \
__builtin_types_compatible_p(typeof(x), unsigned type), \
({ signed type __x = (x); __x < 0 ? -__x : __x; }), other)
#endif /* _BR_H */

View File

@@ -1,71 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _BR_BUG_H
#define _BR_BUG_H
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "likely.h"
#include "debug.h"
/* BUG functions inspired by Linux kernel's <asm/bug.h>
*/
#define panic() exit(0xff)
/*
* Don't use BUG() or BUG_ON() unless there's really no way out; one
* example might be detecting data structure corruption in the middle
* of an operation that can't be backed out of. If the (sub)system
* can somehow continue operating, perhaps with reduced functionality,
* it's probably not BUG-worthy.
*
* If you're tempted to BUG(), think again: is completely giving up
* really the *only* solution? There are usually better options, where
* users don't need to reboot ASAP and can mostly shut down cleanly.
*/
#define BUG() do { \
fprintf(stderr, "BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \
panic(); \
} while (0)
#define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while (0)
/*
* WARN(), WARN_ON(), WARN_ON_ONCE, and so on can be used to report
* significant kernel issues that need prompt attention if they should ever
* appear at runtime.
*
* Do not use these macros when checking for invalid external inputs
* (e.g. invalid system call arguments, or invalid data coming from
* network/devices), and on transient conditions like ENOMEM or EAGAIN.
* These macros should be used for recoverable kernel issues only.
* For invalid external inputs, transient conditions, etc use
* pr_err[_once/_ratelimited]() followed by dump_stack(), if necessary.
* Do not include "BUG"/"WARNING" in format strings manually to make these
* conditions distinguishable from kernel issues.
*
* Use the versions with printk format strings to provide better diagnostics.
*/
#define __WARN() do { \
fprintf(stderr, "WARNING: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \
} while (0)
#define __WARN_printf(arg...) do { \
vfprintf(stderr, arg); \
} while (0)
#define WARN_ON(condition) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN(); \
unlikely(__ret_warn_on); \
})
#define WARN(condition, format...) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN_printf(format); \
unlikely(__ret_warn_on); \
})
#endif /* _BR_BUG_H */

View File

@@ -1,44 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* See Documentation/core-api/circular-buffers.rst for more information.
*/
#ifndef _LINUX_CIRC_BUF_H
#define _LINUX_CIRC_BUF_H 1
#define CIRC_BUF(name, type, bits) \
struct s##name { \
type buf[1 << (bits)]; \
int head; \
int tail; \
};
struct circ_buf {
char *buf;
int head;
int tail;
};
/* Return count in buffer. */
#define CIRC_CNT(head,tail,size) (((head) - (tail)) & ((size)-1))
/* Return space available, 0..size-1. We always leave one free char
as a completely full buffer has head == tail, which is the same as
empty. */
#define CIRC_SPACE(head,tail,size) CIRC_CNT((tail),((head)+1),(size))
/* Return count up to the end of the buffer. Carefully avoid
accessing head and tail more than once, so they can change
underneath us without returning inconsistent results. */
#define CIRC_CNT_TO_END(head,tail,size) \
({int end = (size) - (tail); \
int n = ((head) + end) & ((size)-1); \
n < end ? n : end;})
/* Return space available up to the end of the buffer. */
#define CIRC_SPACE_TO_END(head,tail,size) \
({int end = (size) - 1 - (head); \
int n = (end + (tail)) & ((size)-1); \
n <= end ? n : end+1;})
#endif /* _LINUX_CIRC_BUF_H */

View File

@@ -1,30 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* adaptation of Linux kernel's <linux/container_of.h>
*/
#ifndef _BR_CONTAINER_OF_H
#define _BR_CONTAINER_OF_H
/* Are two types/vars the same type (ignoring qualifiers)? */
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
/**
* typeof_member -
*/
#define typeof_member(T, m) typeof(((T*)0)->m)
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
_Static_assert(__same_type(*(ptr), ((type *)0)->member) || \
__same_type(*(ptr), void), \
"pointer type mismatch in container_of()"); \
((type *)(__mptr - offsetof(type, member))); })
#endif /* BR_CONTAINER_OF_H */

View File

@@ -1,104 +0,0 @@
/* debug.h - debug/log management.
*
* Copyright (C) 2021-2022 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>
*
*/
#ifndef DEBUG_H
#define DEBUG_H
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <br.h>
#include <bits.h>
#define _printf __attribute__ ((format (printf, 6, 7)))
#ifdef DEBUG_DEBUG
void debug_init(u32 level, FILE *stream);
void debug_level_set(u32 level);
void debug_stream_set(FILE *stream);
void _printf debug(u32 level, bool timestamp,
u32 indent, const char *src,
u32 line, const char *fmt, ...);
#else /* DEBUG_DEBUG */
static inline void debug_init(__unused u32 level, __unused FILE *stream) {}
static inline void debug_level_set(__unused u32 level) {}
static inline void debug_stream_set(__unused FILE *stream) {}
static inline void _printf debug(__unused u32 level, __unused bool timestamp,
__unused u32 indent, __unused const char *src,
__unused u32 line, __unused const char *fmt, ...) {}
#endif /* DEBUG_DEBUG */
#undef _printf
/**
* log - simple log (no function name, no indent, no timestamp)
* @level: log level
* @fmt: printf format string
* @args: subsequent arguments to printf
*/
#define log(level, fmt, args...) \
debug((level), false, 0, NULL, 0, fmt, ##args)
/**
* log_i - log with indent (no function name, no timestamp)
* @level: log level
* @fmt: printf format string
* @args: subsequent arguments to printf
*
* Output example:
* >>>>val=2
*/
#define log_i(level, fmt, args...) \
debug((level), false, (level), NULL, 0, fmt, ##args)
/**
* log_f - log with function name (no indent name, no timestamp)
* @level: log level
* @fmt: printf format string
* @args: subsequent arguments to printf
*
* Output example:
* [function] val=2
*/
#define log_f(level, fmt, args...) \
debug((level), false, 0, __func__, 0, fmt, ##args)
/**
* log_if - log with function name and line number (no indent name, no timestamp)
* @level: log level
* @fmt: printf format string
* @args: subsequent arguments to printf
*
* Output example:
* >>>> [function:15] val=2
*/
#define log_if(level, fmt, args...) \
debug((level), false, (level), __func__, __LINE__, fmt, ##args)
/**
* log_it - log with function name, line number, indent, and timestamp
* @level: log level
* @fmt: printf format string
* @args: subsequent arguments to printf
*
* Output example:
* >>>> [function:15] val=2
*/
#define log_it(level, fmt, args...) \
debug((level), true, (level), __func__, __LINE__, fmt, ##args)
#endif /* DEBUG_H */

View File

@@ -1,172 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _BR_HASH_H
#define _BR_HASH_H
/* adaptation of Linux kernel's <linux/hash.h> and <linux/stringhash.h>
*/
/* Fast hashing routine for ints, longs and pointers.
(C) 2002 Nadia Yvette Chambers, IBM */
#include <asm/bitsperlong.h>
#include "bits.h"
#include "br.h"
/*
* The "GOLDEN_RATIO_PRIME" is used in ifs/btrfs/brtfs_inode.h and
* fs/inode.c. It's not actually prime any more (the previous primes
* were actively bad for hashing), but the name remains.
*/
#if __BITS_PER_LONG == 32
#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_32
#define hash_long(val, bits) hash_32(val, bits)
#elif __BITS_PER_LONG == 64
#define hash_long(val, bits) hash_64(val, bits)
#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_64
#else
#error Wordsize not 32 or 64
#endif
/*
* This hash multiplies the input by a large odd number and takes the
* high bits. Since multiplication propagates changes to the most
* significant end only, it is essential that the high bits of the
* product be used for the hash value.
*
* Chuck Lever verified the effectiveness of this technique:
* http://www.citi.umich.edu/techreports/reports/citi-tr-00-1.pdf
*
* Although a random odd number will do, it turns out that the golden
* ratio phi = (sqrt(5)-1)/2, or its negative, has particularly nice
* properties. (See Knuth vol 3, section 6.4, exercise 9.)
*
* These are the negative, (1 - phi) = phi**2 = (3 - sqrt(5))/2,
* which is very slightly easier to multiply by and makes no
* difference to the hash distribution.
*/
#define GOLDEN_RATIO_32 0x61C88647
#define GOLDEN_RATIO_64 0x61C8864680B583EBull
/*
* The _generic versions exist only so lib/test_hash.c can compare
* the arch-optimized versions with the generic.
*
* Note that if you change these, any <asm/hash.h> that aren't updated
* to match need to have their HAVE_ARCH_* define values updated so the
* self-test will not false-positive.
*/
#ifndef HAVE_ARCH__HASH_32
#define __hash_32 __hash_32_generic
#endif
static inline u32 __hash_32_generic(u32 val)
{
return val * GOLDEN_RATIO_32;
}
static inline u32 hash_32(u32 val, unsigned int bits)
{
/* High bits are more random, so use them. */
return __hash_32(val) >> (32 - bits);
}
#ifndef HAVE_ARCH_HASH_64
#define hash_64 hash_64_generic
#endif
static __always_inline u32 hash_64_generic(u64 val, unsigned int bits)
{
#if __BITS_PER_LONG == 64
/* 64x64-bit multiply is efficient on all 64-bit processors */
return val * GOLDEN_RATIO_64 >> (64 - bits);
#else
/* Hash 64 bits using only 32x32-bit multiply. */
return hash_32((u32)val ^ __hash_32(val >> 32), bits);
#endif
}
static inline u32 hash_ptr(const void *ptr, unsigned int bits)
{
return hash_long((unsigned long)ptr, bits);
}
/* This really should be called fold32_ptr; it does no hashing to speak of. */
static inline u32 hash32_ptr(const void *ptr)
{
unsigned long val = (unsigned long)ptr;
#if __BITS_PER_LONG == 64
val ^= (val >> 32);
#endif
return (u32)val;
}
/*
* Routines for hashing strings of bytes to a 32-bit hash value.
*
* These hash functions are NOT GUARANTEED STABLE between kernel
* versions, architectures, or even repeated boots of the same kernel.
* (E.g. they may depend on boot-time hardware detection or be
* deliberately randomized.)
*
* They are also not intended to be secure against collisions caused by
* malicious inputs; much slower hash functions are required for that.
*
* They are optimized for pathname components, meaning short strings.
* Even if a majority of files have longer names, the dynamic profile of
* pathname components skews short due to short directory names.
* (E.g. /usr/lib/libsesquipedalianism.so.3.141.)
*/
/*
* Version 1: one byte at a time. Example of use:
*
* unsigned long hash = init_name_hash;
* while (*p)
* hash = partial_name_hash(tolower(*p++), hash);
* hash = end_name_hash(hash);
*
* Although this is designed for bytes, fs/hfsplus/unicode.c
* abuses it to hash 16-bit values.
*/
/* Hash courtesy of the R5 hash in reiserfs modulo sign bits */
#define init_name_hash(salt) (unsigned long)(salt)
/* partial hash update function. Assume roughly 4 bits per character */
static inline unsigned long
partial_name_hash(unsigned long c, unsigned long prevhash)
{
return (prevhash + (c << 4) + (c >> 4)) * 11;
}
/*
* Finally: cut down the number of bits to a int value (and try to avoid
* losing bits). This also has the property (wanted by the dcache)
* that the msbits make a good hash table index.
*/
static inline unsigned int end_name_hash(unsigned long hash)
{
return hash_long(hash, 32);
}
/*
* Version 2: One word (32 or 64 bits) at a time.
* If CONFIG_DCACHE_WORD_ACCESS is defined (meaning <asm/word-at-a-time.h>
* exists, which describes major Linux platforms like x86 and ARM), then
* this computes a different hash function much faster.
*
* If not set, this falls back to a wrapper around the preceding.
*/
extern unsigned int __pure hash_string(const void *salt, const char *, unsigned int);
/*
* A hash_len is a u64 with the hash of a string in the low
* half and the length in the high half.
*/
#define hashlen_hash(hashlen) ((u32)(hashlen))
#define hashlen_len(hashlen) ((u32)((hashlen) >> 32))
#define hashlen_create(hash, len) ((u64)(len)<<32 | (u32)(hash))
/* Return the "hash_len" (hash and length) of a null-terminated string */
extern u64 __pure hashlen_string(const void *salt, const char *name);
#endif /* _BR_HASH_H */

View File

@@ -1,202 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* adaptation of Linux kernel's <linux/hashtable.h>
*/
/*
* Statically sized hash table implementation
* (C) 2012 Sasha Levin <levinsasha928@gmail.com>
*/
#ifndef _LINUX_HASHTABLE_H
#define _LINUX_HASHTABLE_H
#include "list.h"
#include "hash.h"
//#include <linux/rculist.h>
#define DEFINE_HASHTABLE(name, bits) \
struct hlist_head name[1 << (bits)] = \
{ [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT }
#define DEFINE_READ_MOSTLY_HASHTABLE(name, bits) \
struct hlist_head name[1 << (bits)] __read_mostly = \
{ [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT }
#define DECLARE_HASHTABLE(name, bits) \
struct hlist_head name[1 << (bits)]
#define HASH_SIZE(name) (ARRAY_SIZE(name))
#define HASH_BITS(name) ilog2(HASH_SIZE(name))
/* Use hash_32 when possible to allow for fast 32bit hashing in 64bit kernels. */
#define hash_min(val, bits) \
(sizeof(val) <= 4 ? hash_32(val, bits) : hash_long(val, bits))
static inline void __hash_init(struct hlist_head *ht, unsigned int sz)
{
unsigned int i;
for (i = 0; i < sz; i++)
INIT_HLIST_HEAD(&ht[i]);
}
/**
* hash_init - initialize a hash table
* @hashtable: hashtable to be initialized
*
* Calculates the size of the hashtable from the given parameter, otherwise
* same as hash_init_size.
*
* This has to be a macro since HASH_BITS() will not work on pointers since
* it calculates the size during preprocessing.
*/
#define hash_init(hashtable) __hash_init(hashtable, HASH_SIZE(hashtable))
/**
* hash_add - add an object to a hashtable
* @hashtable: hashtable to add to
* @node: the &struct hlist_node of the object to be added
* @key: the key of the object to be added
*/
#define hash_add(hashtable, node, key) \
hlist_add_head(node, &hashtable[hash_min(key, HASH_BITS(hashtable))])
/**
* hash_add_rcu - add an object to a rcu enabled hashtable
* @hashtable: hashtable to add to
* @node: the &struct hlist_node of the object to be added
* @key: the key of the object to be added
*/
#define hash_add_rcu(hashtable, node, key) \
hlist_add_head_rcu(node, &hashtable[hash_min(key, HASH_BITS(hashtable))])
/**
* hash_hashed - check whether an object is in any hashtable
* @node: the &struct hlist_node of the object to be checked
*/
static inline bool hash_hashed(struct hlist_node *node)
{
return !hlist_unhashed(node);
}
static inline bool __hash_empty(struct hlist_head *ht, unsigned int sz)
{
unsigned int i;
for (i = 0; i < sz; i++)
if (!hlist_empty(&ht[i]))
return false;
return true;
}
/**
* hash_empty - check whether a hashtable is empty
* @hashtable: hashtable to check
*
* This has to be a macro since HASH_BITS() will not work on pointers since
* it calculates the size during preprocessing.
*/
#define hash_empty(hashtable) __hash_empty(hashtable, HASH_SIZE(hashtable))
/**
* hash_del - remove an object from a hashtable
* @node: &struct hlist_node of the object to remove
*/
static inline void hash_del(struct hlist_node *node)
{
hlist_del_init(node);
}
/**
* hash_for_each - iterate over a hashtable
* @name: hashtable to iterate
* @bkt: integer to use as bucket loop cursor
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
*/
#define hash_for_each(name, bkt, obj, member) \
for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
(bkt)++)\
hlist_for_each_entry(obj, &name[bkt], member)
/**
* hash_for_each_rcu - iterate over a rcu enabled hashtable
* @name: hashtable to iterate
* @bkt: integer to use as bucket loop cursor
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
*/
#define hash_for_each_rcu(name, bkt, obj, member) \
for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
(bkt)++)\
hlist_for_each_entry_rcu(obj, &name[bkt], member)
/**
* hash_for_each_safe - iterate over a hashtable safe against removal of
* hash entry
* @name: hashtable to iterate
* @bkt: integer to use as bucket loop cursor
* @tmp: a &struct hlist_node used for temporary storage
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
*/
#define hash_for_each_safe(name, bkt, tmp, obj, member) \
for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
(bkt)++)\
hlist_for_each_entry_safe(obj, tmp, &name[bkt], member)
/**
* hash_for_each_possible - iterate over all possible objects hashing to the
* same bucket
* @name: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
* @key: the key of the objects to iterate over
*/
#define hash_for_each_possible(name, obj, member, key) \
hlist_for_each_entry(obj, &name[hash_min(key, HASH_BITS(name))], member)
/**
* hash_for_each_possible_rcu - iterate over all possible objects hashing to the
* same bucket in an rcu enabled hashtable
* @name: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
* @key: the key of the objects to iterate over
*/
#define hash_for_each_possible_rcu(name, obj, member, key, cond...) \
hlist_for_each_entry_rcu(obj, &name[hash_min(key, HASH_BITS(name))],\
member, ## cond)
/**
* hash_for_each_possible_rcu_notrace - iterate over all possible objects hashing
* to the same bucket in an rcu enabled hashtable in a rcu enabled hashtable
* @name: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
* @key: the key of the objects to iterate over
*
* This is the same as hash_for_each_possible_rcu() except that it does
* not do any RCU debugging or tracing.
*/
#define hash_for_each_possible_rcu_notrace(name, obj, member, key) \
hlist_for_each_entry_rcu_notrace(obj, \
&name[hash_min(key, HASH_BITS(name))], member)
/**
* hash_for_each_possible_safe - iterate over all possible objects hashing to the
* same bucket safe against removals
* @name: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @tmp: a &struct hlist_node used for temporary storage
* @member: the name of the hlist_node within the struct
* @key: the key of the objects to iterate over
*/
#define hash_for_each_possible_safe(name, obj, tmp, member, key) \
hlist_for_each_entry_safe(obj, tmp,\
&name[hash_min(key, HASH_BITS(name))], member)
#endif

View File

@@ -1,18 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* taken from Kernel's <linux/compiler.h
*/
#ifndef __LIKELY_H
#define __LIKELY_H
/* See https://kernelnewbies.org/FAQ/LikelyUnlikely
*
* In 2 words:
* "You should use it [likely() and unlikely()] only in cases when the likeliest
* branch is very very very likely, or when the unlikeliest branch is very very
* very unlikely."
*/
# define likely(x) __builtin_expect(!!(x), 1)
# define unlikely(x) __builtin_expect(!!(x), 0)
#endif /* __LIKELY_H */

View File

@@ -1,992 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* adaptation of kernel's <linux/list.h>
*
*/
#ifndef __BR_LIST_H
#define __BR_LIST_H
#include <stddef.h>
#include <stdbool.h>
#include "rwonce.h"
#include "container-of.h"
/************ originally in <include/linux/types.h> */
struct list_head {
struct list_head *next, *prev;
};
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
/************ originally in <include/linux/poison.h> */
# define POISON_POINTER_DELTA 0
/* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
#define LIST_POISON1 ((void *) 0x100 + POISON_POINTER_DELTA)
#define LIST_POISON2 ((void *) 0x200 + POISON_POINTER_DELTA)
/*
* Circular doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
/**
* INIT_LIST_HEAD - Initialize a list_head structure
* @list: list_head structure to be initialized.
*
* Initializes the list_head to point to itself. If it is a list header,
* the result is an empty list.
*/
static inline void INIT_LIST_HEAD(struct list_head *list)
{
WRITE_ONCE(list->next, list);
list->prev = list;
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
WRITE_ONCE(prev->next, new);
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
WRITE_ONCE(prev->next, next);
}
/*
* Delete a list entry and clear the 'prev' pointer.
*
* This is a special-purpose list clearing method used in the networking code
* for lists allocated as per-cpu, where we don't want to incur the extra
* WRITE_ONCE() overhead of a regular list_del_init(). The code that uses this
* needs to check the node 'prev' pointer instead of calling list_empty().
*/
static inline void __list_del_clearprev(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->prev = NULL;
}
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty() on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void list_del(struct list_head *entry)
{
__list_del_entry(entry);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
/**
* list_replace - replace old entry by new one
* @old : the element to be replaced
* @new : the new element to insert
*
* If @old was empty, it will be overwritten.
*/
static inline void list_replace(struct list_head *old,
struct list_head *new)
{
new->next = old->next;
new->next->prev = new;
new->prev = old->prev;
new->prev->next = new;
}
/**
* list_replace_init - replace old entry by new one and initialize the old one
* @old : the element to be replaced
* @new : the new element to insert
*
* If @old was empty, it will be overwritten.
*/
static inline void list_replace_init(struct list_head *old,
struct list_head *new)
{
list_replace(old, new);
INIT_LIST_HEAD(old);
}
/**
* list_swap - replace entry1 with entry2 and re-add entry1 at entry2's position
* @entry1: the location to place entry2
* @entry2: the location to place entry1
*/
static inline void list_swap(struct list_head *entry1,
struct list_head *entry2)
{
struct list_head *pos = entry2->prev;
list_del(entry2);
list_replace(entry1, entry2);
if (pos == entry1)
pos = entry2;
list_add(entry1, pos);
}
/**
* list_del_init - deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
static inline void list_del_init(struct list_head *entry)
{
__list_del_entry(entry);
INIT_LIST_HEAD(entry);
}
/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del_entry(list);
list_add(list, head);
}
/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del_entry(list);
list_add_tail(list, head);
}
/**
* list_bulk_move_tail - move a subsection of a list to its tail
* @head: the head that will follow our entry
* @first: first entry to move
* @last: last entry to move, can be the same as first
*
* Move all entries between @first and including @last before @head.
* All three entries must belong to the same linked list.
*/
static inline void list_bulk_move_tail(struct list_head *head,
struct list_head *first,
struct list_head *last)
{
first->prev->next = last->next;
last->next->prev = first->prev;
head->prev->next = first;
first->prev = head->prev;
last->next = head;
head->prev = last;
}
/**
* list_is_first -- tests whether @list is the first entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_first(const struct list_head *list,
const struct list_head *head)
{
return list->prev == head;
}
/**
* list_is_last - tests whether @list is the last entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_last(const struct list_head *list,
const struct list_head *head)
{
return list->next == head;
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return READ_ONCE(head->next) == head;
}
/**
* list_rotate_left - rotate the list to the left
* @head: the head of the list
*/
static inline void list_rotate_left(struct list_head *head)
{
struct list_head *first;
if (!list_empty(head)) {
first = head->next;
list_move_tail(first, head);
}
}
/**
* list_rotate_to_front() - Rotate list to specific item.
* @list: The desired new front of the list.
* @head: The head of the list.
*
* Rotates list so that @list becomes the new front of the list.
*/
static inline void list_rotate_to_front(struct list_head *list,
struct list_head *head)
{
/*
* Deletes the list head from the list denoted by @head and
* places it as the tail of @list, this effectively rotates the
* list so that @list is at the front.
*/
list_move_tail(head, list);
}
/**
* list_is_singular - tests whether a list has just one entry.
* @head: the list to test.
*/
static inline int list_is_singular(const struct list_head *head)
{
return !list_empty(head) && (head->next == head->prev);
}
static inline void __list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
struct list_head *new_first = entry->next;
list->next = head->next;
list->next->prev = list;
list->prev = entry;
entry->next = list;
head->next = new_first;
new_first->prev = head;
}
/**
* list_cut_position - cut a list into two
* @list: a new list to add all removed entries
* @head: a list with entries
* @entry: an entry within head, could be the head itself
* and if so we won't cut the list
*
* This helper moves the initial part of @head, up to and
* including @entry, from @head to @list. You should
* pass on @entry an element you know is on @head. @list
* should be an empty list or a list you do not care about
* losing its data.
*
*/
static inline void list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
if (list_empty(head))
return;
if (list_is_singular(head) &&
(head->next != entry && head != entry))
return;
if (entry == head)
INIT_LIST_HEAD(list);
else
__list_cut_position(list, head, entry);
}
/**
* list_cut_before - cut a list into two, before given entry
* @list: a new list to add all removed entries
* @head: a list with entries
* @entry: an entry within head, could be the head itself
*
* This helper moves the initial part of @head, up to but
* excluding @entry, from @head to @list. You should pass
* in @entry an element you know is on @head. @list should
* be an empty list or a list you do not care about losing
* its data.
* If @entry == @head, all entries on @head are moved to
* @list.
*/
static inline void list_cut_before(struct list_head *list,
struct list_head *head,
struct list_head *entry)
{
if (head->next == entry) {
INIT_LIST_HEAD(list);
return;
}
list->next = head->next;
list->next->prev = list;
list->prev = entry->prev;
list->prev->next = list;
head->next = entry;
entry->prev = head;
}
static inline void __list_splice(const struct list_head *list,
struct list_head *prev,
struct list_head *next)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
first->prev = prev;
prev->next = first;
last->next = next;
next->prev = last;
}
/**
* list_splice - join two lists, this is designed for stacks
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice(const struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head, head->next);
}
/**
* list_splice_tail - join two lists, each list being a queue
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice_tail(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head->prev, head);
}
/**
* list_splice_init - join two lists and reinitialise the emptied list.
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head, head->next);
INIT_LIST_HEAD(list);
}
}
/**
* list_splice_tail_init - join two lists and reinitialise the emptied list
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* Each of the lists is a queue.
* The list at @list is reinitialised
*/
static inline void list_splice_tail_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head->prev, head);
INIT_LIST_HEAD(list);
}
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* list_last_entry - get the last element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
/**
* list_first_entry_or_null - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note that if the list is empty, it returns NULL.
*/
#define list_first_entry_or_null(ptr, type, member) ({ \
struct list_head *head__ = (ptr); \
struct list_head *pos__ = READ_ONCE(head__->next); \
pos__ != head__ ? list_entry(pos__, type, member) : NULL; \
})
/**
* list_next_entry - get the next element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_next_entry(pos, member) \
list_entry((pos)->member.next, __typeof__(*(pos)), member)
/**
* list_prev_entry - get the prev element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_prev_entry(pos, member) \
list_entry((pos)->member.prev, __typeof__(*(pos)), member)
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
/**
* list_for_each_continue - continue iteration over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*
* Continue to iterate over a list, continuing after the current position.
*/
#define list_for_each_continue(pos, head) \
for (pos = pos->next; pos != (head); pos = pos->next)
/**
* list_for_each_prev - iterate over a list backwards
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); pos = pos->prev)
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_prev_safe(pos, n, head) \
for (pos = (head)->prev, n = pos->prev; \
pos != (head); \
pos = n, n = pos->prev)
/**
* list_entry_is_head - test if the entry points to the head of the list
* @pos: the type * to cursor
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_entry_is_head(pos, head, member) \
(&pos->member == (head))
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, __typeof__(*pos), member); \
!list_entry_is_head(pos, head, member); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_reverse - iterate backwards over list of given type.
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_last_entry(head, __typeof__(*pos), member); \
!list_entry_is_head(pos, head, member); \
pos = list_prev_entry(pos, member))
/**
* list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
* @pos: the type * to use as a start point
* @head: the head of the list
* @member: the name of the list_head within the struct.
*
* Prepares a pos entry for use as a start point in list_for_each_entry_continue().
*/
#define list_prepare_entry(pos, head, member) \
((pos) ? : list_entry(head, __typeof__(*pos), member))
/**
* list_for_each_entry_continue - continue iteration over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Continue to iterate over list of given type, continuing after
* the current position.
*/
#define list_for_each_entry_continue(pos, head, member) \
for (pos = list_next_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_continue_reverse - iterate backwards from the given point
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Start to iterate over list of given type backwards, continuing after
* the current position.
*/
#define list_for_each_entry_continue_reverse(pos, head, member) \
for (pos = list_prev_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = list_prev_entry(pos, member))
/**
* list_for_each_entry_from - iterate over list of given type from the current point
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type, continuing from current position.
*/
#define list_for_each_entry_from(pos, head, member) \
for (; !list_entry_is_head(pos, head, member); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_from_reverse - iterate backwards over list of given type
* from the current point
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate backwards over list of given type, continuing from current position.
*/
#define list_for_each_entry_from_reverse(pos, head, member) \
for (; !list_entry_is_head(pos, head, member); \
pos = list_prev_entry(pos, member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, __typeof__(*pos), member), \
n = list_next_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_continue - continue list iteration safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type, continuing after current point,
* safe against removal of list entry.
*/
#define list_for_each_entry_safe_continue(pos, n, head, member) \
for (pos = list_next_entry(pos, member), \
n = list_next_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_from - iterate over list from current point safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type from current point, safe against
* removal of list entry.
*/
#define list_for_each_entry_safe_from(pos, n, head, member) \
for (n = list_next_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate backwards over list of given type, safe against removal
* of list entry.
*/
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
for (pos = list_last_entry(head, __typeof__(*pos), member), \
n = list_prev_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = n, n = list_prev_entry(n, member))
/**
* list_safe_reset_next - reset a stale list_for_each_entry_safe loop
* @pos: the loop cursor used in the list_for_each_entry_safe loop
* @n: temporary storage used in list_for_each_entry_safe
* @member: the name of the list_head within the struct.
*
* list_safe_reset_next is not safe to use in general if the list may be
* modified concurrently (eg. the lock is dropped in the loop body). An
* exception to this is if the cursor element (pos) is pinned in the list,
* and list_safe_reset_next is called after re-taking the lock and before
* completing the current iteration of the loop body.
*/
#define list_safe_reset_next(pos, n, member) \
n = list_next_entry(pos, member)
/*
* Double linked lists with a single pointer list head.
* Mostly useful for hash tables where the two pointer list head is
* too wasteful.
* You lose the ability to access the tail in O(1).
*/
#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
h->next = NULL;
h->pprev = NULL;
}
/**
* hlist_unhashed - Has node been removed from list and reinitialized?
* @h: Node to be checked
*
* Not that not all removal functions will leave a node in unhashed
* state. For example, hlist_nulls_del_init_rcu() does leave the
* node in unhashed state, but hlist_nulls_del() does not.
*/
static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
/**
* hlist_unhashed_lockless - Version of hlist_unhashed for lockless use
* @h: Node to be checked
*
* This variant of hlist_unhashed() must be used in lockless contexts
* to avoid potential load-tearing. The READ_ONCE() is paired with the
* various WRITE_ONCE() in hlist helpers that are defined below.
*/
static inline int hlist_unhashed_lockless(const struct hlist_node *h)
{
return !READ_ONCE(h->pprev);
}
/**
* hlist_empty - Is the specified hlist_head structure an empty hlist?
* @h: Structure to check.
*/
static inline int hlist_empty(const struct hlist_head *h)
{
return !READ_ONCE(h->first);
}
static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
WRITE_ONCE(*pprev, next);
if (next)
WRITE_ONCE(next->pprev, pprev);
}
/**
* hlist_del - Delete the specified hlist_node from its list
* @n: Node to delete.
*
* Note that this function leaves the node in hashed state. Use
* hlist_del_init() or similar instead to unhash @n.
*/
static inline void hlist_del(struct hlist_node *n)
{
__hlist_del(n);
n->next = LIST_POISON1;
n->pprev = LIST_POISON2;
}
/**
* hlist_del_init - Delete the specified hlist_node from its list and initialize
* @n: Node to delete.
*
* Note that this function leaves the node in unhashed state.
*/
static inline void hlist_del_init(struct hlist_node *n)
{
if (!hlist_unhashed(n)) {
__hlist_del(n);
INIT_HLIST_NODE(n);
}
}
/**
* hlist_add_head - add a new entry at the beginning of the hlist
* @n: new entry to be added
* @h: hlist head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
WRITE_ONCE(n->next, first);
if (first)
WRITE_ONCE(first->pprev, &n->next);
WRITE_ONCE(h->first, n);
WRITE_ONCE(n->pprev, &h->first);
}
/**
* hlist_add_before - add a new entry before the one specified
* @n: new entry to be added
* @next: hlist node to add it before, which must be non-NULL
*/
static inline void hlist_add_before(struct hlist_node *n,
struct hlist_node *next)
{
WRITE_ONCE(n->pprev, next->pprev);
WRITE_ONCE(n->next, next);
WRITE_ONCE(next->pprev, &n->next);
WRITE_ONCE(*(n->pprev), n);
}
/**
* hlist_add_behind - add a new entry after the one specified
* @n: new entry to be added
* @prev: hlist node to add it after, which must be non-NULL
*/
static inline void hlist_add_behind(struct hlist_node *n,
struct hlist_node *prev)
{
WRITE_ONCE(n->next, prev->next);
WRITE_ONCE(prev->next, n);
WRITE_ONCE(n->pprev, &prev->next);
if (n->next)
WRITE_ONCE(n->next->pprev, &n->next);
}
/**
* hlist_add_fake - create a fake hlist consisting of a single headless node
* @n: Node to make a fake list out of
*
* This makes @n appear to be its own predecessor on a headless hlist.
* The point of this is to allow things like hlist_del() to work correctly
* in cases where there is no list.
*/
static inline void hlist_add_fake(struct hlist_node *n)
{
n->pprev = &n->next;
}
/**
* hlist_fake: Is this node a fake hlist?
* @h: Node to check for being a self-referential fake hlist.
*/
static inline bool hlist_fake(struct hlist_node *h)
{
return h->pprev == &h->next;
}
/**
* hlist_is_singular_node - is node the only element of the specified hlist?
* @n: Node to check for singularity.
* @h: Header for potentially singular list.
*
* Check whether the node is the only node of the head without
* accessing head, thus avoiding unnecessary cache misses.
*/
static inline bool
hlist_is_singular_node(struct hlist_node *n, struct hlist_head *h)
{
return !n->next && n->pprev == &h->first;
}
/**
* hlist_move_list - Move an hlist
* @old: hlist_head for old list.
* @new: hlist_head for new list.
*
* Move a list from one list head to another. Fixup the pprev
* reference of the first entry if it exists.
*/
static inline void hlist_move_list(struct hlist_head *old,
struct hlist_head *new)
{
new->first = old->first;
if (new->first)
new->first->pprev = &new->first;
old->first = NULL;
}
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos ; pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
pos = n)
#define hlist_entry_safe(ptr, type, member) \
({ __typeof__(ptr) ____ptr = (ptr); \
____ptr ? hlist_entry(____ptr, type, member) : NULL; \
})
/**
* hlist_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry(pos, head, member) \
for (pos = hlist_entry_safe((head)->first, __typeof__(*(pos)), member); \
pos; \
pos = hlist_entry_safe((pos)->member.next, __typeof__(*(pos)), member))
/**
* hlist_for_each_entry_continue - iterate over a hlist continuing after current point
* @pos: the type * to use as a loop cursor.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_continue(pos, member) \
for (pos = hlist_entry_safe((pos)->member.next, __typeof__(*(pos)), member); \
pos; \
pos = hlist_entry_safe((pos)->member.next, __typeof__(*(pos)), member))
/**
* hlist_for_each_entry_from - iterate over a hlist continuing from current point
* @pos: the type * to use as a loop cursor.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_from(pos, member) \
for (; pos; \
pos = hlist_entry_safe((pos)->member.next, __typeof__(*(pos)), member))
/**
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: a &struct hlist_node to use as temporary storage
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_safe(pos, n, head, member) \
for (pos = hlist_entry_safe((head)->first, __typeof__(*pos), member); \
pos && ({ n = pos->member.next; 1; }); \
pos = hlist_entry_safe(n, __typeof__(*pos), member))
#endif /* __BR_LIST_H */

View File

@@ -1,46 +0,0 @@
#ifndef _LINUX_UNALIGNED_PACKED_STRUCT_H
#define _LINUX_UNALIGNED_PACKED_STRUCT_H
#include <linux/types.h>
struct __una_u16 { u16 x; } __packed;
struct __una_u32 { u32 x; } __packed;
struct __una_u64 { u64 x; } __packed;
static inline u16 __get_unaligned_cpu16(const void *p)
{
const struct __una_u16 *ptr = (const struct __una_u16 *)p;
return ptr->x;
}
static inline u32 __get_unaligned_cpu32(const void *p)
{
const struct __una_u32 *ptr = (const struct __una_u32 *)p;
return ptr->x;
}
static inline u64 __get_unaligned_cpu64(const void *p)
{
const struct __una_u64 *ptr = (const struct __una_u64 *)p;
return ptr->x;
}
static inline void __put_unaligned_cpu16(u16 val, void *p)
{
struct __una_u16 *ptr = (struct __una_u16 *)p;
ptr->x = val;
}
static inline void __put_unaligned_cpu32(u32 val, void *p)
{
struct __una_u32 *ptr = (struct __una_u32 *)p;
ptr->x = val;
}
static inline void __put_unaligned_cpu64(u64 val, void *p)
{
struct __una_u64 *ptr = (struct __una_u64 *)p;
ptr->x = val;
}
#endif /* _LINUX_UNALIGNED_PACKED_STRUCT_H */

View File

@@ -1,53 +0,0 @@
/* pjwhash-inline.h - PJW hash function, inline version.
*
* Copyright (C) 2021-2022 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>
*
*/
#ifndef _PJWHASH_INLINE_H
#define _PJWHASH_INLINE_H
#include "bits.h"
#define THREE_QUARTERS ((int) ((BITS_PER_INT * 3) / 4))
#define ONE_EIGHTH ((int) (BITS_PER_INT / 8))
#define HIGH_BITS ( ~((uint)(~0) >> ONE_EIGHTH ))
#ifndef _pjw_inline
#define _pjw_inline static inline
#endif
/**
* unsigned int pjwhash - PJW hash function
* @key: the key address.
* @length: the length of key.
*
* This hash was created by Peter Jay Weinberger (AT&T Bell Labs):
* https://en.wikipedia.org/wiki/PJW_hash_function
*
* Return: the PJW hash.
*/
_pjw_inline uint pjwhash(const void* key, uint length)
{
uint hash = 0, high;
const u8 *k = key;
for (uint i = 0; i < length; ++k, ++i) {
hash = (hash << ONE_EIGHTH) + *k;
high = hash & HIGH_BITS;
if (high != 0) {
hash ^= high >> THREE_QUARTERS;
hash &= ~high;
}
}
return hash;
}
#endif /* _PJWHASH_INLINE_H */

View File

@@ -1,30 +0,0 @@
/* pjwhash.h - PJW hash function, extern version.
*
* Copyright (C) 2021-2022 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>
*/
#ifndef _PJWHASH_H
#define _PJWHASH_H
#include "bits.h"
/**
* unsigned int pjwhash - PJW hash function
* @key: the key address.
* @length: the length of key.
*
* This hash was created by Peter Jay Weinberger (AT&T Bell Labs):
* https://en.wikipedia.org/wiki/PJW_hash_function
*
* Return: the PJW hash.
*/
extern uint pjwhash (const void* key, uint length);
#endif /* _PJWHASH_H */

View File

@@ -1,345 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* adaptation of kernel's <linux/plist.h>
*
*/
/*
* Descending-priority-sorted double-linked list
*
* (C) 2002-2003 Intel Corp
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>.
*
* 2001-2005 (c) MontaVista Software, Inc.
* Daniel Walker <dwalker@mvista.com>
*
* (C) 2005 Thomas Gleixner <tglx@linutronix.de>
*
* Simplifications of the original code by
* Oleg Nesterov <oleg@tv-sign.ru>
*
* Based on simple lists (include/linux/list.h).
*
* This is a priority-sorted list of nodes; each node has a
* priority from INT_MIN (highest) to INT_MAX (lowest).
*
* Addition is O(K), removal is O(1), change of priority of a node is
* O(K) and K is the number of RT priority levels used in the system.
* (1 <= K <= 99)
*
* This list is really a list of lists:
*
* - The tier 1 list is the prio_list, different priority nodes.
*
* - The tier 2 list is the node_list, serialized nodes.
*
* Simple ASCII art explanation:
*
* pl:prio_list (only for plist_node)
* nl:node_list
* HEAD| NODE(S)
* |
* ||------------------------------------|
* ||->|pl|<->|pl|<--------------->|pl|<-|
* | |10| |21| |21| |21| |40| (prio)
* | | | | | | | | | | |
* | | | | | | | | | | |
* |->|nl|<->|nl|<->|nl|<->|nl|<->|nl|<->|nl|<-|
* |-------------------------------------------|
*
* The nodes on the prio_list list are sorted by priority to simplify
* the insertion of new nodes. There are no nodes with duplicate
* priorites on the list.
*
* The nodes on the node_list are ordered by priority and can contain
* entries which have the same priority. Those entries are ordered
* FIFO
*
* Addition means: look for the prio_list node in the prio_list
* for the priority of the node and insert it before the node_list
* entry of the next prio_list node. If it is the first node of
* that priority, add it to the prio_list in the right position and
* insert it into the serialized node_list list
*
* Removal means remove it from the node_list and remove it from
* the prio_list if the node_list list_head is non empty. In case
* of removal from the prio_list it must be checked whether other
* entries of the same priority are on the list or not. If there
* is another entry of the same priority then this entry has to
* replace the removed entry on the prio_list. If the entry which
* is removed is the only entry of this priority then a simple
* remove from both list is sufficient.
*
* INT_MIN is the highest priority, 0 is the medium highest, INT_MAX
* is lowest priority.
*
* No locking is done, up to the caller.
*/
#ifndef _LINUX_PLIST_H_
#define _LINUX_PLIST_H_
#include "container-of.h"
#include "list.h"
//#include <types.h>
// #include <asm/bug.h>
struct plist_head {
struct list_head node_list;
};
struct plist_node {
int prio;
struct list_head prio_list;
struct list_head node_list;
};
/**
* PLIST_HEAD_INIT - static struct plist_head initializer
* @head: struct plist_head variable name
*/
#define PLIST_HEAD_INIT(head) \
{ \
.node_list = LIST_HEAD_INIT((head).node_list) \
}
/**
* PLIST_HEAD - declare and init plist_head
* @head: name for struct plist_head variable
*/
#define PLIST_HEAD(head) \
struct plist_head head = PLIST_HEAD_INIT(head)
/**
* PLIST_NODE_INIT - static struct plist_node initializer
* @node: struct plist_node variable name
* @__prio: initial node priority
*/
#define PLIST_NODE_INIT(node, __prio) \
{ \
.prio = (__prio), \
.prio_list = LIST_HEAD_INIT((node).prio_list), \
.node_list = LIST_HEAD_INIT((node).node_list), \
}
/**
* plist_head_init - dynamic struct plist_head initializer
* @head: &struct plist_head pointer
*/
static inline void
plist_head_init(struct plist_head *head)
{
INIT_LIST_HEAD(&head->node_list);
}
/**
* plist_node_init - Dynamic struct plist_node initializer
* @node: &struct plist_node pointer
* @prio: initial node priority
*/
static inline void plist_node_init(struct plist_node *node, int prio)
{
node->prio = prio;
INIT_LIST_HEAD(&node->prio_list);
INIT_LIST_HEAD(&node->node_list);
}
extern void plist_add(struct plist_node *node, struct plist_head *head);
extern void plist_del(struct plist_node *node, struct plist_head *head);
extern void plist_requeue(struct plist_node *node, struct plist_head *head);
/**
* plist_for_each - iterate over the plist
* @pos: the type * to use as a loop counter
* @head: the head for your list
*/
#define plist_for_each(pos, head) \
list_for_each_entry(pos, &(head)->node_list, node_list)
/**
* plist_for_each_reverse - iterate backwards over the plist
* @pos: the type * to use as a loop counter
* @head: the head for your list
*/
#define plist_for_each_reverse(pos, head) \
list_for_each_entry_reverse(pos, &(head)->node_list, node_list)
/**
* plist_for_each_continue - continue iteration over the plist
* @pos: the type * to use as a loop cursor
* @head: the head for your list
*
* Continue to iterate over plist, continuing after the current position.
*/
#define plist_for_each_continue(pos, head) \
list_for_each_entry_continue(pos, &(head)->node_list, node_list)
/**
* plist_for_each_continue_reverse - continue iteration over the plist
* @pos: the type * to use as a loop cursor
* @head: the head for your list
*
* Continue to iterate backwards over plist, continuing after the current
* position.
*/
#define plist_for_each_continue_reverse(pos, head) \
list_for_each_entry_continue_reverse(pos, &(head)->node_list, node_list)
/**
* plist_for_each_safe - iterate safely over a plist of given type
* @pos: the type * to use as a loop counter
* @n: another type * to use as temporary storage
* @head: the head for your list
*
* Iterate over a plist of given type, safe against removal of list entry.
*/
#define plist_for_each_safe(pos, n, head) \
list_for_each_entry_safe(pos, n, &(head)->node_list, node_list)
/**
* plist_for_each_safe_reverse - iterate backwards safely over a plist of given type
* @pos: the type * to use as a loop counter
* @n: another type * to use as temporary storage
* @head: the head for your list
*
* Iterate backwards over a plist of given type, safe against removal of list entry.
*/
#define plist_for_each_safe_reverse(pos, n, head) \
list_for_each_entry_safe_reverse(pos, n, &(head)->node_list, node_list)
/**
* plist_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop counter
* @head: the head for your list
* @mem: the name of the list_head within the struct
*/
#define plist_for_each_entry(pos, head, mem) \
list_for_each_entry(pos, &(head)->node_list, mem.node_list)
/**
* plist_for_each_entry_reverse - iterate backwards over list of given type
* @pos: the type * to use as a loop counter
* @head: the head for your list
* @mem: the name of the list_head within the struct
*/
#define plist_for_each_entry_reverse(pos, head, mem) \
list_for_each_entry_reverse(pos, &(head)->node_list, mem.node_list)
/**
* plist_for_each_entry_continue - continue iteration over list of given type
* @pos: the type * to use as a loop cursor
* @head: the head for your list
* @m: the name of the list_head within the struct
*
* Continue to iterate over list of given type, continuing after
* the current position.
*/
#define plist_for_each_entry_continue(pos, head, m) \
list_for_each_entry_continue(pos, &(head)->node_list, m.node_list)
/**
* plist_for_each_entry_safe - iterate safely over list of given type
* @pos: the type * to use as a loop counter
* @n: another type * to use as temporary storage
* @head: the head for your list
* @m: the name of the list_head within the struct
*
* Iterate over list of given type, safe against removal of list entry.
*/
#define plist_for_each_entry_safe(pos, n, head, m) \
list_for_each_entry_safe(pos, n, &(head)->node_list, m.node_list)
/**
* plist_head_empty - return !0 if a plist_head is empty
* @head: &struct plist_head pointer
*/
static inline int plist_head_empty(const struct plist_head *head)
{
return list_empty(&head->node_list);
}
/**
* plist_node_empty - return !0 if plist_node is not on a list
* @node: &struct plist_node pointer
*/
static inline int plist_node_empty(const struct plist_node *node)
{
return list_empty(&node->node_list);
}
/* All functions below assume the plist_head is not empty. */
/**
* plist_first_entry - get the struct for the first entry
* @head: the &struct plist_head pointer
* @type: the type of the struct this is embedded in
* @member: the name of the list_head within the struct
*/
#ifdef CONFIG_DEBUG_PLIST
# define plist_first_entry(head, type, member) \
({ \
WARN_ON(plist_head_empty(head)); \
container_of(plist_first(head), type, member); \
})
#else
# define plist_first_entry(head, type, member) \
container_of(plist_first(head), type, member)
#endif
/**
* plist_last_entry - get the struct for the last entry
* @head: the &struct plist_head pointer
* @type: the type of the struct this is embedded in
* @member: the name of the list_head within the struct
*/
#ifdef CONFIG_DEBUG_PLIST
# define plist_last_entry(head, type, member) \
({ \
WARN_ON(plist_head_empty(head)); \
container_of(plist_last(head), type, member); \
})
#else
# define plist_last_entry(head, type, member) \
container_of(plist_last(head), type, member)
#endif
/**
* plist_next - get the next entry in list
* @pos: the type * to cursor
*/
#define plist_next(pos) \
list_next_entry(pos, node_list)
/**
* plist_prev - get the prev entry in list
* @pos: the type * to cursor
*/
#define plist_prev(pos) \
list_prev_entry(pos, node_list)
/**
* plist_first - return the first node (and thus, highest priority)
* @head: the &struct plist_head pointer
*
* Assumes the plist is _not_ empty.
*/
static inline struct plist_node *plist_first(const struct plist_head *head)
{
return list_entry(head->node_list.next,
struct plist_node, node_list);
}
/**
* plist_last - return the last node (and thus, lowest priority)
* @head: the &struct plist_head pointer
*
* Assumes the plist is _not_ empty.
*/
static inline struct plist_node *plist_last(const struct plist_head *head)
{
return list_entry(head->node_list.prev,
struct plist_node, node_list);
}
#endif

View File

@@ -1,90 +0,0 @@
/* pool.h - A simple memory pool manager.
*
* Copyright (C) 2021-2022 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>
*
*/
#ifndef POOL_H
#define POOL_H
#include <stdint.h>
#include <stddef.h>
#include "list.h"
#include "bits.h"
#define POOL_NAME_LENGTH (16) /* max name length including trailing \0 */
typedef struct {
struct list_head list_blocks; /* list of allocated blocks in pool */
char data[]; /* objects block */
} block_t;
typedef struct {
char name[POOL_NAME_LENGTH]; /* pool name */
size_t eltsize; /* object size */
u32 available; /* current available elements */
u32 allocated; /* total objects allocated */
u32 growsize; /* number of objects per block allocated */
u32 nblocks; /* number of blocks allocated */
struct list_head list_available; /* available nodes */
struct list_head list_blocks; /* allocated blocks */
} pool_t;
/**
* pool_stats - display some pool statistics
* @pool: the pool address.
*/
void pool_stats(pool_t *pool);
/**
* pool_create - create a new memory pool
* @name: the name to give to the pool.
* @grow: the number of elements to add when no more available.
* @size: the size of an element in pool.
*
* The name will be truncated to 16 characters (including the final '\0').
*
* Return: The address of the created pool, or NULL if error.
*/
pool_t *pool_create(const char *name, u32 grow, size_t size);
/**
* pool_get() - Get an element from a pool.
* @pool: The pool address.
*
* Get an object from the pool.
*
* Return: The address of the object, or NULL if error.
*/
void *pool_get(pool_t *pool);
/**
* pool_add() - Add (free) an element to a pool.
* @pool: The pool address.
* @elt: The address of the object to add to the pool.
*
* The object will be available for further pool_get().
*
* Return: The current number of available elements in pool (including
* @elt).
*/
u32 pool_add(pool_t *pool, void *elt);
/**
* pool_destroy() - destroy a pool.
* @pool: The pool address.
*
* Attention: All memory is freed, but no check is done whether all pool
* elements have been released. Referencing any pool object after this call
* will likely imply some memory corruption.
*/
void pool_destroy(pool_t *pool);
#endif

View File

@@ -1,128 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* adaptation of kernel's <asm-generic/rwonce.h>
* See https://www.kernel.org/doc/Documentation/memory-barriers.txt
*/
/*
* Prevent the compiler from merging or refetching reads or writes. The
* compiler is also forbidden from reordering successive instances of
* READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some
* particular ordering. One way to make the compiler aware of ordering is to
* put the two invocations of READ_ONCE or WRITE_ONCE in different C
* statements.
*
* These two macros will also work on aggregate data types like structs or
* unions.
*
* Their two major use cases are: (1) Mediating communication between
* process-level code and irq/NMI handlers, all running on the same CPU,
* and (2) Ensuring that the compiler does not fold, spindle, or otherwise
* mutilate accesses that either do not require ordering or that interact
* with an explicit memory barrier or atomic instruction that provides the
* required ordering.
*/
#ifndef __BR_RWONCE_H
#define __BR_RWONCE_H
/************ originally in <include/linux/compiler_attributes.h> */
#if __has_attribute(__error__)
# define __compiletime_error(msg) __attribute__((__error__(msg)))
#else
# define __compiletime_error(msg)
#endif
/************ originally in <include/linux/compiler_types.h> */
/*
* __unqual_scalar_typeof(x) - Declare an unqualified scalar type, leaving
* non-scalar types unchanged.
*/
/*
* Prefer C11 _Generic for better compile-times and simpler code. Note: 'char'
* is not type-compatible with 'signed char', and we define a separate case.
*/
#define __scalar_type_to_expr_cases(type) \
unsigned type: (unsigned type)0, \
signed type: (signed type)0
#define __unqual_scalar_typeof(x) \
typeof(_Generic((x), \
char: (char)0, \
__scalar_type_to_expr_cases(char), \
__scalar_type_to_expr_cases(short), \
__scalar_type_to_expr_cases(int), \
__scalar_type_to_expr_cases(long), \
__scalar_type_to_expr_cases(long long), \
default: (x)))
/* Is this type a native word size -- useful for atomic operations */
#define __native_word(t) \
(sizeof(t) == sizeof(char) || sizeof(t) == sizeof(short) || \
sizeof(t) == sizeof(int) || sizeof(t) == sizeof(long))
#ifdef __OPTIMIZE__
# define __compiletime_assert(condition, msg, prefix, suffix) \
do { \
extern void prefix ## suffix(void) __compiletime_error(msg); \
if (!(condition)) \
prefix ## suffix(); \
} while (0)
#else
# define __compiletime_assert(condition, msg, prefix, suffix) do { } while (0)
#endif
#define _compiletime_assert(condition, msg, prefix, suffix) \
__compiletime_assert(condition, msg, prefix, suffix)
/**
* compiletime_assert - break build and emit msg if condition is false
* @condition: a compile-time constant condition to check
* @msg: a message to emit if condition is false
*
* In tradition of POSIX assert, this macro will break the build if the
* supplied condition is *false*, emitting the supplied error message if the
* compiler has support to do so.
*/
#define compiletime_assert(condition, msg) \
_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
#define compiletime_assert_atomic_type(t) \
compiletime_assert(__native_word(t), \
"Need native word sized stores/loads for atomicity.")
/************ originally in <asm-generic/rwonce.h> */
/*
* Yes, this permits 64-bit accesses on 32-bit architectures. These will
* actually be atomic in some cases (namely Armv7 + LPAE), but for others we
* rely on the access being split into 2x32-bit accesses for a 32-bit quantity
* (e.g. a virtual address) and a strong prevailing wind.
*/
#define compiletime_assert_rwonce_type(t) \
compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \
"Unsupported access size for {READ,WRITE}_ONCE().")
/*
* Use __READ_ONCE() instead of READ_ONCE() if you do not require any
* atomicity. Note that this may result in tears!
*/
#ifndef __READ_ONCE
#define __READ_ONCE(x) (*(const volatile __unqual_scalar_typeof(x) *)&(x))
#endif
#define READ_ONCE(x) \
({ \
compiletime_assert_rwonce_type(x); \
__READ_ONCE(x); \
})
#define __WRITE_ONCE(x, val) \
do { \
*(volatile typeof(x) *)&(x) = (val); \
} while (0)
#define WRITE_ONCE(x, val) \
do { \
compiletime_assert_rwonce_type(x); \
__WRITE_ONCE(x, val); \
} while (0)
#endif /* __BR_RWONCE_H */

View File

@@ -1,79 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LINUX_STRINGHASH_H
#define __LINUX_STRINGHASH_H
#include <linux/compiler.h> /* For __pure */
#include <linux/types.h> /* For u32, u64 */
#include <linux/hash.h>
/*
* Routines for hashing strings of bytes to a 32-bit hash value.
*
* These hash functions are NOT GUARANTEED STABLE between kernel
* versions, architectures, or even repeated boots of the same kernel.
* (E.g. they may depend on boot-time hardware detection or be
* deliberately randomized.)
*
* They are also not intended to be secure against collisions caused by
* malicious inputs; much slower hash functions are required for that.
*
* They are optimized for pathname components, meaning short strings.
* Even if a majority of files have longer names, the dynamic profile of
* pathname components skews short due to short directory names.
* (E.g. /usr/lib/libsesquipedalianism.so.3.141.)
*/
/*
* Version 1: one byte at a time. Example of use:
*
* unsigned long hash = init_name_hash;
* while (*p)
* hash = partial_name_hash(tolower(*p++), hash);
* hash = end_name_hash(hash);
*
* Although this is designed for bytes, fs/hfsplus/unicode.c
* abuses it to hash 16-bit values.
*/
/* Hash courtesy of the R5 hash in reiserfs modulo sign bits */
#define init_name_hash(salt) (unsigned long)(salt)
/* partial hash update function. Assume roughly 4 bits per character */
static inline unsigned long
partial_name_hash(unsigned long c, unsigned long prevhash)
{
return (prevhash + (c << 4) + (c >> 4)) * 11;
}
/*
* Finally: cut down the number of bits to a int value (and try to avoid
* losing bits). This also has the property (wanted by the dcache)
* that the msbits make a good hash table index.
*/
static inline unsigned int end_name_hash(unsigned long hash)
{
return hash_long(hash, 32);
}
/*
* Version 2: One word (32 or 64 bits) at a time.
* If CONFIG_DCACHE_WORD_ACCESS is defined (meaning <asm/word-at-a-time.h>
* exists, which describes major Linux platforms like x86 and ARM), then
* this computes a different hash function much faster.
*
* If not set, this falls back to a wrapper around the preceding.
*/
extern unsigned int __pure full_name_hash(const void *salt, const char *, unsigned int);
/*
* A hash_len is a u64 with the hash of a string in the low
* half and the length in the high half.
*/
#define hashlen_hash(hashlen) ((u32)(hashlen))
#define hashlen_len(hashlen) ((u32)((hashlen) >> 32))
#define hashlen_create(hash, len) ((u64)(len)<<32 | (u32)(hash))
/* Return the "hash_len" (hash and length) of a null-terminated string */
extern u64 __pure hashlen_string(const void *salt, const char *name);
#endif /* __LINUX_STRINGHASH_H */

View File

@@ -1,105 +0,0 @@
/* struct-group.h - mirrored structure macros.
*
* Copyright (C) 2021-2022 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>
*
* Some parts are taken from Linux's kernel <linux/stddef.h> and others, and are :
* SPDX-License-Identifier: GPL-2.0
*
*/
#ifndef _STRUCT_GROUP_H
#define _STRUCT_GROUP_H
/**
* __struct_group() - Create a mirrored named and anonyomous struct
*
* @TAG: The tag name for the named sub-struct (usually empty)
* @NAME: The identifier name of the mirrored sub-struct
* @ATTRS: Any struct attributes (usually empty)
* @MEMBERS: The member declarations for the mirrored structs
*
* Used to create an anonymous union of two structs with identical layout
* and size: one anonymous and one named. The former's members can be used
* normally without sub-struct naming, and the latter can be used to
* reason about the start, end, and size of the group of struct members.
* The named struct can also be explicitly tagged for layer reuse, as well
* as both having struct attributes appended.
*/
#define __struct_group(TAG, NAME, ATTRS, MEMBERS...) \
union { \
struct { MEMBERS } ATTRS; \
struct TAG { MEMBERS } ATTRS NAME; \
}
/**
* DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union
*
* @TYPE: The type of each flexible array element
* @NAME: The name of the flexible array member
*
* In order to have a flexible array member in a union or alone in a
* struct, it needs to be wrapped in an anonymous struct with at least 1
* named member, but that member can be empty.
*/
#define DECLARE_FLEX_ARRAY(TYPE, NAME) \
struct { \
struct { } __empty_ ## NAME; \
TYPE NAME[]; \
}
/**
* struct_group() - Wrap a set of declarations in a mirrored struct
*
* @NAME: The identifier name of the mirrored sub-struct
* @MEMBERS: The member declarations for the mirrored structs
*
* Used to create an anonymous union of two structs with identical
* layout and size: one anonymous and one named. The former can be
* used normally without sub-struct naming, and the latter can be
* used to reason about the start, end, and size of the group of
* struct members.
*/
#define struct_group(NAME, MEMBERS...) \
__struct_group(/* no tag */, NAME, /* no attrs */, MEMBERS)
/**
* struct_group_attr() - Create a struct_group() with trailing attributes
*
* @NAME: The identifier name of the mirrored sub-struct
* @ATTRS: Any struct attributes to apply
* @MEMBERS: The member declarations for the mirrored structs
*
* Used to create an anonymous union of two structs with identical
* layout and size: one anonymous and one named. The former can be
* used normally without sub-struct naming, and the latter can be
* used to reason about the start, end, and size of the group of
* struct members. Includes structure attributes argument.
*/
#define struct_group_attr(NAME, ATTRS, MEMBERS...) \
__struct_group(/* no tag */, NAME, ATTRS, MEMBERS)
/**
* struct_group_tagged() - Create a struct_group with a reusable tag
*
* @TAG: The tag name for the named sub-struct
* @NAME: The identifier name of the mirrored sub-struct
* @MEMBERS: The member declarations for the mirrored structs
*
* Used to create an anonymous union of two structs with identical
* layout and size: one anonymous and one named. The former can be
* used normally without sub-struct naming, and the latter can be
* used to reason about the start, end, and size of the group of
* struct members. Includes struct tag argument for the named copy,
* so the specified layout can be reused later.
*/
#define struct_group_tagged(TAG, NAME, MEMBERS...) \
__struct_group(TAG, NAME, /* no attrs */, MEMBERS)
#endif /* _STRUCT_GROUP_H */

View File

@@ -1,259 +0,0 @@
/*
* xxHash - Extremely Fast Hash algorithm
* Copyright (C) 2012-2016, Yann Collet.
*
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2 as published by the
* Free Software Foundation. This program is dual-licensed; you may select
* either version 2 of the GNU General Public License ("GPL") or BSD license
* ("BSD").
*
* You can contact the author at:
* - xxHash homepage: https://cyan4973.github.io/xxHash/
* - xxHash source repository: https://github.com/Cyan4973/xxHash
*/
/*
* Notice extracted from xxHash homepage:
*
* xxHash is an extremely fast Hash algorithm, running at RAM speed limits.
* It also successfully passes all tests from the SMHasher suite.
*
* Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2
* Duo @3GHz)
*
* Name Speed Q.Score Author
* xxHash 5.4 GB/s 10
* CrapWow 3.2 GB/s 2 Andrew
* MumurHash 3a 2.7 GB/s 10 Austin Appleby
* SpookyHash 2.0 GB/s 10 Bob Jenkins
* SBox 1.4 GB/s 9 Bret Mulvey
* Lookup3 1.2 GB/s 9 Bob Jenkins
* SuperFastHash 1.2 GB/s 1 Paul Hsieh
* CityHash64 1.05 GB/s 10 Pike & Alakuijala
* FNV 0.55 GB/s 5 Fowler, Noll, Vo
* CRC32 0.43 GB/s 9
* MD5-32 0.33 GB/s 10 Ronald L. Rivest
* SHA1-32 0.28 GB/s 10
*
* Q.Score is a measure of quality of the hash function.
* It depends on successfully passing SMHasher test set.
* 10 is a perfect score.
*
* A 64-bits version, named xxh64 offers much better speed,
* but for 64-bits applications only.
* Name Speed on 64 bits Speed on 32 bits
* xxh64 13.8 GB/s 1.9 GB/s
* xxh32 6.8 GB/s 6.0 GB/s
*/
#ifndef XXHASH_H
#define XXHASH_H
#include <linux/types.h>
/*-****************************
* Simple Hash Functions
*****************************/
/**
* xxh32() - calculate the 32-bit hash of the input with a given seed.
*
* @input: The data to hash.
* @length: The length of the data to hash.
* @seed: The seed can be used to alter the result predictably.
*
* Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s
*
* Return: The 32-bit hash of the data.
*/
uint32_t xxh32(const void *input, size_t length, uint32_t seed);
/**
* xxh64() - calculate the 64-bit hash of the input with a given seed.
*
* @input: The data to hash.
* @length: The length of the data to hash.
* @seed: The seed can be used to alter the result predictably.
*
* This function runs 2x faster on 64-bit systems, but slower on 32-bit systems.
*
* Return: The 64-bit hash of the data.
*/
uint64_t xxh64(const void *input, size_t length, uint64_t seed);
/**
* xxhash() - calculate wordsize hash of the input with a given seed
* @input: The data to hash.
* @length: The length of the data to hash.
* @seed: The seed can be used to alter the result predictably.
*
* If the hash does not need to be comparable between machines with
* different word sizes, this function will call whichever of xxh32()
* or xxh64() is faster.
*
* Return: wordsize hash of the data.
*/
static inline unsigned long xxhash(const void *input, size_t length,
uint64_t seed)
{
#if BITS_PER_LONG == 64
return xxh64(input, length, seed);
#else
return xxh32(input, length, seed);
#endif
}
/*-****************************
* Streaming Hash Functions
*****************************/
/*
* These definitions are only meant to allow allocation of XXH state
* statically, on stack, or in a struct for example.
* Do not use members directly.
*/
/**
* struct xxh32_state - private xxh32 state, do not use members directly
*/
struct xxh32_state {
uint32_t total_len_32;
uint32_t large_len;
uint32_t v1;
uint32_t v2;
uint32_t v3;
uint32_t v4;
uint32_t mem32[4];
uint32_t memsize;
};
/**
* struct xxh32_state - private xxh64 state, do not use members directly
*/
struct xxh64_state {
uint64_t total_len;
uint64_t v1;
uint64_t v2;
uint64_t v3;
uint64_t v4;
uint64_t mem64[4];
uint32_t memsize;
};
/**
* xxh32_reset() - reset the xxh32 state to start a new hashing operation
*
* @state: The xxh32 state to reset.
* @seed: Initialize the hash state with this seed.
*
* Call this function on any xxh32_state to prepare for a new hashing operation.
*/
void xxh32_reset(struct xxh32_state *state, uint32_t seed);
/**
* xxh32_update() - hash the data given and update the xxh32 state
*
* @state: The xxh32 state to update.
* @input: The data to hash.
* @length: The length of the data to hash.
*
* After calling xxh32_reset() call xxh32_update() as many times as necessary.
*
* Return: Zero on success, otherwise an error code.
*/
int xxh32_update(struct xxh32_state *state, const void *input, size_t length);
/**
* xxh32_digest() - produce the current xxh32 hash
*
* @state: Produce the current xxh32 hash of this state.
*
* A hash value can be produced at any time. It is still possible to continue
* inserting input into the hash state after a call to xxh32_digest(), and
* generate new hashes later on, by calling xxh32_digest() again.
*
* Return: The xxh32 hash stored in the state.
*/
uint32_t xxh32_digest(const struct xxh32_state *state);
/**
* xxh64_reset() - reset the xxh64 state to start a new hashing operation
*
* @state: The xxh64 state to reset.
* @seed: Initialize the hash state with this seed.
*/
void xxh64_reset(struct xxh64_state *state, uint64_t seed);
/**
* xxh64_update() - hash the data given and update the xxh64 state
* @state: The xxh64 state to update.
* @input: The data to hash.
* @length: The length of the data to hash.
*
* After calling xxh64_reset() call xxh64_update() as many times as necessary.
*
* Return: Zero on success, otherwise an error code.
*/
int xxh64_update(struct xxh64_state *state, const void *input, size_t length);
/**
* xxh64_digest() - produce the current xxh64 hash
*
* @state: Produce the current xxh64 hash of this state.
*
* A hash value can be produced at any time. It is still possible to continue
* inserting input into the hash state after a call to xxh64_digest(), and
* generate new hashes later on, by calling xxh64_digest() again.
*
* Return: The xxh64 hash stored in the state.
*/
uint64_t xxh64_digest(const struct xxh64_state *state);
/*-**************************
* Utils
***************************/
/**
* xxh32_copy_state() - copy the source state into the destination state
*
* @src: The source xxh32 state.
* @dst: The destination xxh32 state.
*/
void xxh32_copy_state(struct xxh32_state *dst, const struct xxh32_state *src);
/**
* xxh64_copy_state() - copy the source state into the destination state
*
* @src: The source xxh64 state.
* @dst: The destination xxh64 state.
*/
void xxh64_copy_state(struct xxh64_state *dst, const struct xxh64_state *src);
#endif /* XXHASH_H */

View File

@@ -1,118 +0,0 @@
/* debug.c - debug/log management
*
* Copyright (C) 2021-2022 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 <stdio.h>
#include <stdarg.h>
#include <time.h>
#ifndef DEBUG_DEBUG
#define DEBUG_DEBUG
#endif
#include "bits.h"
#include "debug.h"
#define NANOSEC 1000000000 /* nano sec in sec */
#define MILLISEC 1000000 /* milli sec in sec */
static long long timer_start; /* in nanosecond */
static u32 debug_level = 0;
static FILE *stream = NULL;
void debug_level_set(u32 level)
{
debug_level = level;
log(0, "debug level set to %u\n", level);
}
void debug_stream_set(FILE *_stream)
{
stream = _stream;
log(0, "stream set to %d\n", stream? fileno(stream): -1);
}
void debug_init(u32 level, FILE *_stream)
{
struct timespec timer;
debug_stream_set(_stream);
debug_level_set(level);
if (!clock_gettime(CLOCK_MONOTONIC, &timer)) {
timer_start = timer.tv_sec * NANOSEC + timer.tv_nsec;
}
else {
timer_start = 0;
}
log(0, "timer started.\n");
}
inline static long long timer_elapsed()
{
struct timespec timer;
clock_gettime(CLOCK_MONOTONIC, &timer);
return (timer.tv_sec * NANOSEC + timer.tv_nsec) - timer_start;
}
/* void debug - log function
* @timestamp : boolean
* @indent : indent level (2 spaces each)
* @src : source file/func name (or NULL)
* @line : line number
*/
void debug(u32 level, bool timestamp, u32 indent, const char *src,
u32 line, const char *fmt, ...)
{
if (!stream || level > debug_level)
return;
va_list ap;
if (indent)
fprintf(stream, "%*s", 2*(indent-1), "");
if (timestamp) {
long long diff = timer_elapsed();
fprintf(stream, "%lld.%03lld ", diff/NANOSEC, (diff/1000000)%1000);
fprintf(stream, "%010lld ", diff);
}
if (src) {
if (line)
fprintf(stream, "[%s:%u] ", src, line);
else
fprintf(stream, "[%s] ", src);
}
va_start(ap, fmt);
vfprintf(stream, fmt, ap);
va_end(ap);
}
#ifdef BIN_debug
#include <unistd.h>
int main()
{
int foo=1;
debug_init(5);
log(0, "log0=%d\n", foo++);
log(1, "log1=%d\n", foo++);
log(2, "log2=%d\n", foo++);
log_i(2, "log_i 2=%d\n", foo++);
log_i(5, "log_i 5=%d\n", foo++);
log_i(6, "log_i 6=%d\n", foo++);
log_it(4, "log_it 4=%d\n", foo++);
log_f(1, "log_f 5=%d\n", foo++);
}
#endif

View File

@@ -1,29 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* inspired from kernel's <fs/namei.h>
*/
#include "hash.h"
/* Return the hash of a string of known length */
unsigned int hash_string(const void *salt, const char *name, unsigned int len)
{
unsigned long hash = init_name_hash(salt);
while (len--)
hash = partial_name_hash((unsigned char)*name++, hash);
return end_name_hash(hash);
}
/* Return the "hash_len" (hash and length) of a null-terminated string */
u64 hashlen_string(const void *salt, const char *name)
{
unsigned long hash = init_name_hash(salt);
unsigned long len = 0, c;
c = (unsigned char)*name;
while (c) {
len++;
hash = partial_name_hash(c, hash);
c = (unsigned char)name[len];
}
return hashlen_create(end_name_hash(hash), len);
}

View File

@@ -1,173 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* adapted from Linux kernel lib/plist.c
*
* Descending-priority-sorted double-linked list
*
* (C) 2002-2003 Intel Corp
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>.
*
* 2001-2005 (c) MontaVista Software, Inc.
* Daniel Walker <dwalker@mvista.com>
*
* (C) 2005 Thomas Gleixner <tglx@linutronix.de>
*
* Simplifications of the original code by
* Oleg Nesterov <oleg@tv-sign.ru>
*
* Based on simple lists (include/linux/list.h).
*
* This file contains the add / del functions which are considered to
* be too large to inline. See include/linux/plist.h for further
* information.
*/
#include "plist.h"
#include "bug.h"
#ifdef DEBUG_PLIST
static struct plist_head test_head;
static void plist_check_prev_next(struct list_head *t, struct list_head *p,
struct list_head *n)
{
WARN(n->prev != p || p->next != n,
"top: %p, n: %p, p: %p\n"
"prev: %p, n: %p, p: %p\n"
"next: %p, n: %p, p: %p\n",
t, t->next, t->prev,
p, p->next, p->prev,
n, n->next, n->prev);
}
static void plist_check_list(struct list_head *top)
{
struct list_head *prev = top, *next = top->next;
plist_check_prev_next(top, prev, next);
while (next != top) {
prev = next;
next = prev->next;
plist_check_prev_next(top, prev, next);
}
}
static void plist_check_head(struct plist_head *head)
{
if (!plist_head_empty(head))
plist_check_list(&plist_first(head)->prio_list);
plist_check_list(&head->node_list);
}
#else
# define plist_check_head(h) do { } while (0)
#endif
/**
* plist_add - add @node to @head
*
* @node: &struct plist_node pointer
* @head: &struct plist_head pointer
*/
void plist_add(struct plist_node *node, struct plist_head *head)
{
struct plist_node *first, *iter, *prev = NULL;
struct list_head *node_next = &head->node_list;
plist_check_head(head);
WARN_ON(!plist_node_empty(node));
WARN_ON(!list_empty(&node->prio_list));
if (plist_head_empty(head))
goto ins_node;
first = iter = plist_first(head);
do {
if (node->prio < iter->prio) {
node_next = &iter->node_list;
break;
}
prev = iter;
iter = list_entry(iter->prio_list.next,
struct plist_node, prio_list);
} while (iter != first);
if (!prev || prev->prio != node->prio)
list_add_tail(&node->prio_list, &iter->prio_list);
ins_node:
list_add_tail(&node->node_list, node_next);
plist_check_head(head);
}
/**
* plist_del - Remove a @node from plist.
*
* @node: &struct plist_node pointer - entry to be removed
* @head: &struct plist_head pointer - list head
*/
void plist_del(struct plist_node *node, struct plist_head *head)
{
plist_check_head(head);
if (!list_empty(&node->prio_list)) {
if (node->node_list.next != &head->node_list) {
struct plist_node *next;
next = list_entry(node->node_list.next,
struct plist_node, node_list);
/* add the next plist_node into prio_list */
if (list_empty(&next->prio_list))
list_add(&next->prio_list, &node->prio_list);
}
list_del_init(&node->prio_list);
}
list_del_init(&node->node_list);
plist_check_head(head);
}
/**
* plist_requeue - Requeue @node at end of same-prio entries.
*
* This is essentially an optimized plist_del() followed by
* plist_add(). It moves an entry already in the plist to
* after any other same-priority entries.
*
* @node: &struct plist_node pointer - entry to be moved
* @head: &struct plist_head pointer - list head
*/
void plist_requeue(struct plist_node *node, struct plist_head *head)
{
struct plist_node *iter;
struct list_head *node_next = &head->node_list;
plist_check_head(head);
BUG_ON(plist_head_empty(head));
BUG_ON(plist_node_empty(node));
if (node == plist_last(head))
return;
iter = plist_next(node);
if (node->prio != iter->prio)
return;
plist_del(node, head);
plist_for_each_continue(iter, head) {
if (node->prio != iter->prio) {
node_next = &iter->node_list;
break;
}
}
list_add_tail(&node->node_list, node_next);
plist_check_head(head);
}

View File

@@ -1,219 +0,0 @@
/* pool.c - A simple pool manager.
*
* Copyright (C) 2021-2022 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 <stddef.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "list.h"
#include "pool.h"
#include "debug.h"
#include "bits.h"
void pool_stats(pool_t *pool)
{
if (pool) {
block_t *block;
log_f(1, "[%s] pool [%p]: blocks:%u avail:%u alloc:%u grow:%u eltsize:%zu\n",
pool->name, (void *)pool, pool->nblocks, pool->available,
pool->allocated, pool->growsize, pool->eltsize);
log(5, "\tblocks: ");
list_for_each_entry(block, &pool->list_blocks, list_blocks) {
log(5, "%p ", block);
}
log(5, "\n");
}
}
pool_t *pool_create(const char *name, u32 growsize, size_t eltsize)
{
pool_t *pool;
# ifdef DEBUG_POOL
log_f(1, "name=[%s] growsize=%u eltsize=%zu\n", name, growsize, eltsize);
# endif
/* we need at least sizeof(struct list_head) space in pool elements
*/
if (eltsize < sizeof (struct list_head)) {
# ifdef DEBUG_POOL
log_f(1, "[%s]: structure size too small (%zu < %zu), adjusting to %zu.\n",
name, eltsize, sizeof(struct list_head), sizeof(struct list_head));
# endif
eltsize = sizeof(struct list_head);
}
if ((pool = malloc(sizeof (*pool)))) {
strncpy(pool->name, name, POOL_NAME_LENGTH - 1);
pool->name[POOL_NAME_LENGTH - 1] = 0;
pool->growsize = growsize;
pool->eltsize = eltsize;
pool->available = 0;
pool->allocated = 0;
pool->nblocks = 0;
INIT_LIST_HEAD(&pool->list_available);
INIT_LIST_HEAD(&pool->list_blocks);
} else {
errno = ENOMEM;
}
return pool;
}
static u32 _pool_add(pool_t *pool, struct list_head *elt)
{
# ifdef DEBUG_POOL
log_f(6, "pool=%p &head=%p elt=%p off1=%zu off2=%zu\n",
(void *)pool, (void *)&pool->list_available, (void *)elt,
(void *)&pool->list_available - (void *)pool,
offsetof(pool_t, list_available));
# endif
list_add(elt, &pool->list_available);
return ++pool->available;
}
u32 pool_add(pool_t *pool, void *elt)
{
return _pool_add(pool, elt);
}
static struct list_head *_pool_get(pool_t *pool)
{
struct list_head *res = pool->list_available.next;
pool->available--;
list_del(res);
return res;
}
void *pool_get(pool_t *pool)
{
if (!pool)
return NULL;
if (!pool->available) {
block_t *block = malloc(sizeof(block_t) + pool->eltsize * pool->growsize);
if (!block) {
# ifdef DEBUG_POOL
log_f(1, "[%s]: failed block allocation\n", pool->name);
# endif
errno = ENOMEM;
return NULL;
}
/* maintain list of allocated blocks
*/
list_add(&block->list_blocks, &pool->list_blocks);
pool->nblocks++;
# ifdef DEBUG_POOL
log_f(1, "[%s]: growing pool from %u to %u elements. block=%p nblocks=%u\n",
pool->name,
pool->allocated,
pool->allocated + pool->growsize,
block,
pool->nblocks);
# endif
pool->allocated += pool->growsize;
for (u32 i = 0; i < pool->growsize; ++i) {
void *cur = block->data + i * pool->eltsize;
# ifdef DEBUG_POOL
log_f(7, "alloc=%p cur=%p\n", block, cur);
# endif
_pool_add(pool, (struct list_head *)cur);
}
}
/* this is the effective address of the object (and also the
* pool list_head address)
*/
return _pool_get(pool);
}
void pool_destroy(pool_t *pool)
{
block_t *block, *tmp;
if (!pool)
return;
/* release memory blocks */
# ifdef DEBUG_POOL
log_f(1, "[%s]: releasing %d blocks and main structure\n", pool->name, pool->nblocks);
log(5, "blocks:");
# endif
list_for_each_entry_safe(block, tmp, &pool->list_blocks, list_blocks) {
# ifdef DEBUG_POOL
log(5, " %p", block);
# endif
list_del(&block->list_blocks);
free(block);
}
# ifdef DEBUG_POOL
log(5, "\n");
# endif
free(pool);
}
#ifdef BIN_pool
struct d {
u16 data1;
char c;
struct list_head list;
};
static LIST_HEAD (head);
int main(int ac, char**av)
{
pool_t *pool;
int total;
int action=0;
u16 icur=0;
char ccur='z';
struct d *elt;
debug_init(3);
log_f(1, "%s: sizeof(d)=%lu sizeof(*d)=%lu off=%lu\n", *av, sizeof(elt),
sizeof(*elt), offsetof(struct d, list));
if ((pool = pool_create("dummy", 3, sizeof(*elt)))) {
pool_stats(pool);
for (int cur=1; cur<ac; ++cur) {
total = atoi(av[cur]);
if (action == 0) { /* add elt to list */
log_f(2, "adding %d elements\n", total);
for (int i = 0; i < total; ++i) {
elt = pool_get(pool);
elt->data1 = icur++;
elt->c = ccur--;
list_add(&elt->list, &head);
}
pool_stats(pool);
action = 1;
} else { /* remove one elt from list */
log_f(2, "deleting %d elements\n", total);
for (int i = 0; i < total; ++i) {
if (!list_empty(&head)) {
elt = list_last_entry(&head, struct d, list);
printf("elt=[%d, %c]\n", elt->data1, elt->c);
list_del(&elt->list);
pool_add(pool, elt);
}
}
pool_stats(pool);
action = 0;
}
}
}
pool_stats(pool);
pool_destroy(pool);
}
#endif

26
scripts/env.sh Executable file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
#
# env.sh - set environment for brchess developer.
#
# Copyright (C) 2023 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>
#
# USAGE: source env.sh [arg]
#
# This file will actually be sourced if it was never sourced in current bash
# environment.
if [[ ! -v _BRCHESS_ENV_ ]]; then
export _BRCHESS_ENV_=1 BRCHESS_ROOT BRLIB_DIR LD_LIBRARY_PATH
BRCHESS_ROOT=$(realpath -L "$(dirname "${BASH_SOURCE[0]}")/..")
BRLIB_DIR="$BRCHESS_ROOT/brlib/lib"
LD_LIBRARY_PATH=/mypath${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
#printf "R=%s L=%s LD=%s\n" "$BRCHESS_ROOT" "$BRLIB_DIR" "$LD_LIBRARY_PATH"
printf "Chess environment complete.\n"
fi

70
src/bitboard.h Normal file
View File

@@ -0,0 +1,70 @@
/* bitboard.h - bitboard definitions.
*
* 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 <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>
*
*/
#ifndef BITBOARD_H
#define BITBOARD_H
#include <bits.h>
#include "chessdefs.h"
#include "piece.h"
enum bb_square {
A1 = 1UL << 0, B1 = 1UL << 1, C1 = 1UL << 2, D1 = 1UL << 3,
E1 = 1UL << 4, F1 = 1UL << 5, G1 = 1UL << 6, H1 = 1UL << 7,
A2 = 1UL << 8, B2 = 1UL << 9, C2 = 1UL << 10, D2 = 1UL << 11,
E2 = 1UL << 12, F2 = 1UL << 13, G2 = 1UL << 14, H2 = 1UL << 15,
A3 = 1UL << 16, B3 = 1UL << 17, C3 = 1UL << 18, D3 = 1UL << 19,
E3 = 1UL << 20, F3 = 1UL << 21, G3 = 1UL << 22, H3 = 1UL << 23,
A4 = 1UL << 24, B4 = 1UL << 25, C4 = 1UL << 26, D4 = 1UL << 27,
E4 = 1UL << 28, F4 = 1UL << 29, G4 = 1UL << 30, H4 = 1UL << 31,
A5 = 1UL << 32, B5 = 1UL << 33, C5 = 1UL << 34, D5 = 1UL << 35,
E5 = 1UL << 36, F5 = 1UL << 37, G5 = 1UL << 38, H5 = 1UL << 39,
A6 = 1UL << 40, B6 = 1UL << 41, C6 = 1UL << 42, D6 = 1UL << 43,
E6 = 1UL << 44, F6 = 1UL << 45, G6 = 1UL << 46, H6 = 1UL << 47,
A7 = 1UL << 48, B7 = 1UL << 49, C7 = 1UL << 50, D7 = 1UL << 51,
E7 = 1UL << 52, F7 = 1UL << 53, G7 = 1UL << 54, H7 = 1UL << 55,
A8 = 1UL << 56, B8 = 1UL << 57, C8 = 1UL << 58, D8 = 1UL << 59,
E8 = 1UL << 60, F8 = 1UL << 61, G8 = 1UL << 62, H8 = 1UL << 63,
};
enum bb_files {
F_1 = A1 | A2 | A3 | A4 | A5 | A6 | A7 | A8,
F_2 = B1 | B2 | B3 | B4 | B5 | B6 | B7 | B8,
F_3 = C1 | C2 | C3 | C4 | C5 | C6 | C7 | C8,
F_4 = D1 | D2 | D3 | D4 | D5 | D6 | D7 | D8,
F_5 = E1 | E2 | E3 | E4 | E5 | E6 | E7 | E8,
F_6 = F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8,
F_7 = G1 | G2 | G3 | G4 | G5 | G6 | G7 | G8,
F_8 = H1 | H2 | H3 | H4 | H5 | H6 | H7 | H8,
};
enum bb_ranges {
R_1 = A1 | B1 | C1 | D1 | E1 | F1 | G1 | H1,
R_2 = A2 | B2 | C2 | D2 | E2 | F2 | G2 | H2,
R_3 = A3 | B3 | C3 | D3 | E3 | F3 | G3 | H3,
R_4 = A4 | B4 | C4 | D4 | E4 | F4 | G4 | H4,
R_5 = A5 | B5 | C5 | D5 | E5 | F5 | G5 | H5,
R_6 = A6 | B6 | C6 | D6 | E6 | F6 | G6 | H6,
R_7 = A7 | B7 | C7 | D7 | E7 | F7 | G7 | H7,
R_8 = A8 | B8 | C8 | D8 | E8 | F8 | G8 | H8,
};
#endif /* BOARD_H */

View File

@@ -77,32 +77,4 @@ enum x88_square {
x88_A8=0x70, x88_B8, x88_C8, x88_D8, x88_E8, x88_F8, x88_G8, x88_H8,
};
/* necessary not to become mad to set bitboards
*/
enum bb_square {
A1=(u64)1, B1=(u64)A1<<1, C1=(u64)B1<<1, D1=(u64)C1<<1,
E1=(u64)D1<<1, F1=(u64)E1<<1, G1=(u64)F1<<1, H1=(u64)G1<<1,
A2=(u64)A1<<8, B2=(u64)B1<<8, C2=(u64)C1<<8, D2=(u64)D1<<8,
E2=(u64)E1<<8, F2=(u64)F1<<8, G2=(u64)G1<<8, H2=(u64)H1<<8,
A3=(u64)A2<<8, B3=(u64)B2<<8, C3=(u64)C2<<8, D3=(u64)D2<<8,
E3=(u64)E2<<8, F3=(u64)F2<<8, G3=(u64)G2<<8, H3=(u64)H2<<8,
A4=(u64)A3<<8, B4=(u64)B3<<8, C4=(u64)C3<<8, D4=(u64)D3<<8,
E4=(u64)E3<<8, F4=(u64)F3<<8, G4=(u64)G3<<8, H4=(u64)H3<<8,
A5=(u64)A4<<8, B5=(u64)B4<<8, C5=(u64)C4<<8, D5=(u64)D4<<8,
E5=(u64)E4<<8, F5=(u64)F4<<8, G5=(u64)G4<<8, H5=(u64)H4<<8,
A6=(u64)A5<<8, B6=(u64)B5<<8, C6=(u64)C5<<8, D6=(u64)D5<<8,
E6=(u64)E5<<8, F6=(u64)F5<<8, G6=(u64)G5<<8, H6=(u64)H5<<8,
A7=(u64)A6<<8, B7=(u64)B6<<8, C7=(u64)C6<<8, D7=(u64)D6<<8,
E7=(u64)E6<<8, F7=(u64)F6<<8, G7=(u64)G6<<8, H7=(u64)H6<<8,
A8=(u64)A7<<8, B8=(u64)B7<<8, C8=(u64)C7<<8, D8=(u64)D7<<8,
E8=(u64)E7<<8, F8=(u64)F7<<8, G8=(u64)G7<<8, H8=(u64)H7<<8,
};
#endif /* BOARD_H */

View File

@@ -1,6 +1,6 @@
/* brchess.c - main loop.
*
* Copyright (C) 2021 Bruno Raoult ("br")
* Copyright (C) 2021-2023 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
@@ -29,12 +29,14 @@
#include "move.h"
#include "fen.h"
#include "eval.h"
#include "eval-simple.h"
#include "search.h"
typedef struct {
struct command {
char *name; /* User printable name */
int (*func)(pos_t *, char *); /* function doing the job */
char *doc; /* function doc */
} COMMAND;
};
/* readline example inspired by :
* - https://thoughtbot.com/blog/tab-completion-in-gnu-readline
@@ -45,7 +47,7 @@ char *commands_generator(const char *, int);
char *escape(const char *);
int quote_detector(char *, int);
int execute_line (pos_t *, char *line);
COMMAND *find_command (char *);
struct command *find_command (char *);
char *stripwhite (char *string);
/* The names of functions that actually do the manipulation. */
@@ -55,15 +57,19 @@ int do_init(pos_t *, char*);
int do_pos(pos_t *, char*);
int do_genmoves(pos_t *, char*);
int do_prmoves(pos_t *, char*);
int do_prmovepos(pos_t *pos, char *arg);
//int do_prmovepos(pos_t *pos, char *arg);
int do_prpieces(pos_t *pos, char *arg);
int do_memstats(pos_t *, char*);
int do_eval(pos_t *, char*);
int do_simple_eval(pos_t *, char*);
int do_move(pos_t *, char*);
int do_quit(pos_t *, char*);
int do_debug(pos_t *, char*);
int do_depth(pos_t *, char*);
int do_search(pos_t *, char*);
int do_pvs(pos_t *, char*);
COMMAND commands[] = {
struct command commands[] = {
{ "help", do_help, "Display this text" },
{ "?", do_help, "Synonym for 'help'" },
{ "fen", do_fen, "Set position to FEN" },
@@ -72,16 +78,21 @@ COMMAND commands[] = {
{ "quit", do_quit, "Quit" },
{ "genmove", do_genmoves, "Generate move list for " },
{ "prmoves", do_prmoves, "Print position move list" },
{ "prmovepos", do_prmovepos, "Print Nth move resulting position" },
// { "prmovepos", do_prmovepos, "Print Nth move resulting position" },
{ "prpieces", do_prpieces, "Print Pieces (from pieces lists)" },
{ "memstats", do_memstats, "Generate next move list" },
{ "eval", do_eval, "Eval current position" },
{ "simple-eval", do_simple_eval, "Simple eval current position" },
{ "do_move", do_move, "execute nth move on current position" },
{ "debug", do_debug, "Set log level to LEVEL" },
{ "depth", do_depth, "Set search depth to N" },
{ "search", do_search, "Search best move (negamax)" },
{ "pvs", do_pvs, "Search best move (Principal Variation Search)" },
{ NULL, (int(*)()) NULL, NULL }
};
static int done=0;
static int depth=1;
int brchess(pos_t *pos)
{
@@ -190,7 +201,7 @@ int quote_detector(char *line, int index)
int execute_line(pos_t *pos, char *line)
{
register int i;
COMMAND *command;
struct command *command;
char *word;
/* Isolate the command word. */
@@ -224,7 +235,7 @@ int execute_line(pos_t *pos, char *line)
/* Look up NAME as the name of a command, and return a pointer to that
command. Return a NULL pointer if NAME isn't a command name. */
COMMAND *find_command(char *name)
struct command *find_command(char *name)
{
register int i;
@@ -232,7 +243,7 @@ COMMAND *find_command(char *name)
if (strcmp(name, commands[i].name) == 0)
return &commands[i];
return (COMMAND *)NULL;
return (struct command *)NULL;
}
/* Strip whitespace from the start and end of STRING. Return a pointer
@@ -271,6 +282,13 @@ int do_eval(__unused pos_t *pos, __unused char *arg)
return 1;
}
int do_simple_eval(__unused pos_t *pos, __unused char *arg)
{
eval_t eval = eval_simple(pos);
printf("eval = %d centipawns\n", eval);
return 1;
}
int do_fen(pos_t *pos, char *arg)
{
fen2pos(pos, arg);
@@ -291,36 +309,38 @@ int do_pos(pos_t *pos, __unused char *arg)
int do_genmoves(pos_t *pos, __unused char *arg)
{
log_f(1, "%s\n", arg);
moves_gen(pos, OPPONENT(pos->turn), false);
moves_gen(pos, pos->turn, true);
moves_gen_all(pos);
return 1;
}
int do_prmoves(pos_t *pos, __unused char *arg)
{
log_f(1, "%s\n", arg);
moves_print(pos, M_PR_SEPARATE);
uint debug_level = debug_level_get();
debug_level_set(1);
moves_print(pos, M_PR_SEPARATE | M_PR_NUM | M_PR_LONG);
debug_level_set(debug_level);
return 1;
}
int do_prmovepos(pos_t *pos, char *arg)
{
struct list_head *p_cur, *tmp;
int movenum = atoi(arg), cur = 0; /* starts with 0 */
move_t *move;
log_f(1, "%s\n", arg);
list_for_each_safe(p_cur, tmp, &pos->moves[pos->turn]) {
move = list_entry(p_cur, move_t, list);
if (cur++ == movenum) {
pos_print(move->newpos);
break;
}
}
return 1;
}
/*
* int do_prmovepos(pos_t *pos, char *arg)
* {
* struct list_head *p_cur, *tmp;
* int movenum = atoi(arg), cur = 0; /\* starts with 0 *\/
* move_t *move;
*
* log_f(1, "%s\n", arg);
* list_for_each_safe(p_cur, tmp, &pos->moves[pos->turn]) {
* move = list_entry(p_cur, move_t, list);
* if (cur++ == movenum) {
* pos_print(move->newpos);
* break;
* }
* }
*
* return 1;
* }
*/
int do_prpieces(pos_t *pos, __unused char *arg)
{
@@ -339,6 +359,25 @@ int do_memstats(__unused pos_t *pos,__unused char *arg)
int do_move(__unused pos_t *pos, __unused char *arg)
{
int i = 1, nmove = atoi(arg);
move_t *move;
pos_t *newpos;
if (list_empty(&pos->moves[pos->turn])) {
log_f(1, "No moves list.\n");
return 0;
}
list_for_each_entry(move, &pos->moves[pos->turn], list) {
if (i == nmove)
goto doit;
i++;
}
log_f(1, "Invalid <%d> move, should be <1-%d>.\n", nmove, i);
return 0;
doit:
newpos = move_do(pos, move);
pos_print(newpos);
return 1;
}
@@ -357,7 +396,7 @@ int do_debug(__unused pos_t *pos, __unused char *arg)
not present. */
int do_help(__unused pos_t *pos, __unused char *arg)
{
register int i;
int i;
int printed = 0;
for (i = 0; commands[i].name; i++) {
@@ -387,7 +426,58 @@ int do_help(__unused pos_t *pos, __unused char *arg)
return 0;
}
#ifdef BIN_brchess
int do_depth(__unused pos_t *pos, char *arg)
{
depth = atoi(arg);
printf("depth = %d\n", depth);
return 1;
}
int do_search(pos_t *pos, __unused char *arg)
{
int debug_level = debug_level_get();
float timer1, timer2, nodes_sec;
timer1 = debug_timer_elapsed();
negamax(pos, depth, pos->turn == WHITE ? 1 : -1);
timer2 = debug_timer_elapsed();
nodes_sec = (float) pos->node_count / ((float) (timer2 - timer1) / (float)NANOSEC);
log(1, "best=");
debug_level_set(1);
move_print(0, pos->bestmove, 0);
debug_level_set(debug_level);
log(1, " negamax=%d\n", pos->bestmove->negamax);
printf("Depth:%d Nodes:%luK time:%.02fs (%.0f kn/s)\n", depth,
pos->node_count / 1000, (timer2 - timer1)/NANOSEC, nodes_sec/1000);
return 1;
}
int do_pvs(pos_t *pos, __unused char *arg)
{
int debug_level = debug_level_get();
float timer1, timer2, nodes_sec;
eval_t _pvs;
timer1 = debug_timer_elapsed();
moves_gen_eval_sort(pos);
_pvs = pvs(pos, depth, EVAL_MIN, EVAL_MAX, pos->turn == WHITE ? 1 : -1);
timer2 = debug_timer_elapsed();
nodes_sec = (float) pos->node_count / ((float) (timer2 - timer1) / (float)NANOSEC);
log(1, "best=");
if (pos->bestmove) {
debug_level_set(1);
move_print(0, pos->bestmove, 0);
debug_level_set(debug_level);
log(1, " pvs=%d stored=%d\n", _pvs, pos->bestmove->negamax);
} else {
log(1, "<no-best-move>");
}
printf("Depth:%d Nodes:%luK time:%.02fs (%.0f kn/s)\n", depth,
pos->node_count / 1000, (timer2 - timer1)/NANOSEC, nodes_sec/1000);
return 1;
}
/** main()
* options:
int brchess(pos_t *pos)
@@ -410,7 +500,8 @@ int main(int ac, char **av)
moves_pool_init();
pos_pool_init();
pos = pos_get();
debug_init(1, stderr);
debug_init(1, stderr, true);
eval_simple_init();
while ((opt = getopt(ac, av, "d:f:")) != -1) {
switch (opt) {
@@ -429,4 +520,3 @@ int main(int ac, char **av)
return brchess(pos);
}
#endif /* BIN_brchess */

View File

@@ -80,6 +80,12 @@ enum {
#define SET_BLACK(p) ((p) |= MASK_COLOR)
#define SET_COLOR(p, c) (!(c)? SET_WHITE(p): SET_BLACK(p))
/* 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)
/* square_t bits structure : rrrrffff
* ffff: file
* rrrr: rank
@@ -90,13 +96,19 @@ typedef unsigned char square_t;
*/
typedef unsigned char castle_t;
#define CASTLE_WK 1 /* 0x01 00000001 */
#define CASTLE_WK (1 << 0) /* 0x01 00000001 */
#define CASTLE_WQ (1 << 1) /* 0x02 00000010 */
#define CASTLE_BK (1 << 2) /* 0x04 00000100 */
#define CASTLE_BQ (1 << 3) /* 0x08 00001000 */
#define CASTLE_W 0x03 /* 00000011 W castle mask */
#define CASTLE_B 0x0C /* 00001100 B castle mask */
#define CASTLE_W (CASTLE_WK | CASTLE_WQ) /* 00000011 W castle mask */
#define CASTLE_B (CASTLE_BK | CASTLE_BQ) /* 00001100 B castle mask */
/* game phases
*/
#define OPENING 0
#define MIDDLEGAME 1
#define ENDGAME 2
/* bitboard
*/
@@ -112,5 +124,6 @@ typedef s32 eval_t;
typedef struct piece_list_s piece_list_t;
typedef struct board_s board_t;
typedef struct pos_s pos_t;
typedef struct move_s move_t;
#endif

199
src/eval-simple.c Normal file
View File

@@ -0,0 +1,199 @@
/* eval-simple.c - simple position evaluation.
*
* Copyright (C) 2023 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 <bits.h>
#include <debug.h>
#include "piece.h"
#include "eval-simple.h"
#include "position.h"
/*
* Tables are from https://www.chessprogramming.org/Simplified_Evaluation_Function
*
* Attention! Tables are black point of view (to be visually easier to read).
*/
static int mg_pawn[] = {
0, 0, 0, 0, 0, 0, 0, 0,
50, 50, 50, 50, 50, 50, 50, 50,
10, 10, 20, 30, 30, 20, 10, 10,
5, 5, 10, 25, 25, 10, 5, 5,
0, 0, 0, 20, 20, 0, 0, 0,
5, -5, -10, 0, 0, -10, -5, 5,
5, 10, 10, -20, -20, 10, 10, 5,
0, 0, 0, 0, 0, 0, 0, 0
};
static int mg_knight[] = {
-50, -40, -30, -30, -30, -30, -40, -50,
-40, -20, 0, 0, 0, 0, -20, -40,
-30, 0, 10, 15, 15, 10, 0, -30,
-30, 5, 15, 20, 20, 15, 5, -30,
-30, 0, 15, 20, 20, 15, 0, -30,
-30, 5, 10, 15, 15, 10, 5, -30,
-40, -20, 0, 5, 5, 0, -20, -40,
-50, -40, -30, -30, -30, -30, -40, -50
};
static int mg_bishop[] = {
-20, -10, -10, -10, -10, -10, -10, -20,
-10, 0, 0, 0, 0, 0, 0, -10,
-10, 0, 5, 10, 10, 5, 0, -10,
-10, 5, 5, 10, 10, 5, 5, -10,
-10, 0, 10, 10, 10, 10, 0, -10,
-10, 10, 10, 10, 10, 10, 10, -10,
-10, 5, 0, 0, 0, 0, 5, -10,
-20, -10, -10, -10, -10, -10, -10, -20
};
static int mg_rook[] = {
0, 0, 0, 0, 0, 0, 0, 0,
5, 10, 10, 10, 10, 10, 10, 5,
-5, 0, 0, 0, 0, 0, 0, -5,
-5, 0, 0, 0, 0, 0, 0, -5,
-5, 0, 0, 0, 0, 0, 0, -5,
-5, 0, 0, 0, 0, 0, 0, -5,
-5, 0, 0, 0, 0, 0, 0, -5,
0, 0, 0, 5, 5, 0, 0, 0
};
static int mg_queen[] = {
-20, -10, -10, -5, -5, -10, -10, -20,
-10, 0, 0, 0, 0, 0, 0, -10,
-10, 0, 5, 5, 5, 5, 0, -10,
-5, 0, 5, 5, 5, 5, 0, -5,
0, 0, 5, 5, 5, 5, 0, -5,
-10, 5, 5, 5, 5, 5, 0, -10,
-10, 0, 5, 0, 0, 0, 0, -10,
-20, -10, -10, -5, -5, -10, -10, -20
};
static int mg_king[] = {
-30, -40, -40, -50, -50, -40, -40, -30,
-30, -40, -40, -50, -50, -40, -40, -30,
-30, -40, -40, -50, -50, -40, -40, -30,
-30, -40, -40, -50, -50, -40, -40, -30,
-20, -30, -30, -40, -40, -30, -30, -20,
-10, -20, -20, -20, -20, -20, -20, -10,
20, 20, 0, 0, 0, 0, 20, 20,
20, 30, 10, 0, 0, 10, 30, 20
};
static int eg_king[] = {
-50, -40, -30, -20, -20, -30, -40, -50,
-30, -20, -10, 0, 0, -10, -20, -30,
-30, -10, 20, 30, 30, 20, -10, -30,
-30, -10, 30, 40, 40, 30, -10, -30,
-30, -10, 30, 40, 40, 30, -10, -30,
-30, -10, 20, 30, 30, 20, -10, -30,
-30, -30, 0, 0, 0, 0, -30, -30,
-50, -30, -30, -30, -30, -30, -30, -50
};
/* as pieces bitboard tables start at position 2; we make these tables
* bigger.
*/
static int *mg_tables[] = {
NULL,
NULL,
mg_pawn,
mg_knight,
mg_bishop,
mg_rook,
mg_queen,
mg_king
};
static int *eg_tables[] = {
NULL,
NULL,
mg_pawn,
mg_knight,
mg_bishop,
mg_rook,
mg_queen,
eg_king
};
/* to flip vertically a square, we need to XOR it with 56
*/
static int mg_table[2][6 + 2][64];
static int eg_table[2][6 + 2][64];
void eval_simple_init(void)
{
# ifdef DEBUG_EVAL
log_f(1, "initializing piece tables\n");
# endif
for (int piece = BB_PAWN; piece <= BB_KING; ++piece) {
for (int square = 0; square < 64; ++square) {
mg_table[WHITE][piece][square] = mg_tables[piece][FLIP_V(square)];
eg_table[WHITE][piece][square] = eg_tables[piece][FLIP_V(square)];
mg_table[BLACK][piece][square] = mg_tables[piece][square];
eg_table[BLACK][piece][square] = eg_tables[piece][square];
}
}
}
/**
* eval_simple() - simple and fast position evaluation
* @pos: &position to evaluate
*
* This function is normally used only during initialization,
* or when changing phase (middlegame <--> endgame), as the eval
* will be done increntally when doing moves.
*
* @return: the @pos evaluation in centipawns
*/
eval_t eval_simple(pos_t *pos)
{
eval_t eval[2] = { 0, 0 };
int eg = simple_is_endgame(pos);
int (*gg)[6 + 2][64]= eg? eg_table: mg_table;
pos->eval_simple_phase = ENDGAME;
# ifdef DEBUG_EVAL
log_f(5, "phase = %s.\n", eg? "endgame": "midgame");
# endif
for (int color = WHITE; color <= BLACK; ++color) {
for (uint piece = PAWN; piece <= KING; piece <<= 1) {
int bb = PIECETOBB(piece), cur;
u64 _t;
# ifdef DEBUG_EVAL
log_f(5, "p=%u bb=%d %s %s: count=%d val=%ld ", piece, bb, color? "black": "white",
P_SYM(piece), popcount64(pos->bb[color][bb]),
popcount64(pos->bb[color][bb]) * P_VALUE(piece));
# endif
eval[color] += popcount64(pos->bb[color][bb]) * P_LETTER(piece);
bit_for_each64_2(cur, _t, pos->bb[color][bb]) {
# ifdef DEBUG_EVAL
log(5, "sq=%d:%d ", cur, gg[color][bb][cur]);
# endif
eval[color] += gg[color][bb][cur];
}
# ifdef DEBUG_EVAL
log(5, "\n");
# endif
}
}
# ifdef DEBUG_EVAL
log_f(2, "eval:%d white:%d black:%d\n", eval[WHITE] - eval[BLACK],
eval[WHITE], eval[BLACK]);
# endif
return eval[WHITE] - eval[BLACK];
}

47
src/eval-simple.h Normal file
View File

@@ -0,0 +1,47 @@
/* eval-simple.h - simple 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 <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>
*
*/
#ifndef EVAL_SIMPLE_H
#define EVAL_SIMPLE_H
#include "chessdefs.h"
/* no queen on board */
#define simple_no_queen(p, c) \
( !(p)->bb[c][BB_QUEEN] )
#define simple_one_queen(p, c) \
( popcount64((p)->bb[c][BB_QUEEN]) == 1 )
#define simple_no_rook(p, c) \
(!(p)->bb[c][BB_ROOK])
#define simple_one_minor_piece(p, c) \
(popcount64((p)->bb[c][BB_KNIGHT] | (p)->bb[c][BB_BISHOP]) == 1)
#define simple_is_endgame(p) \
( (simple_no_queen(p, WHITE) || \
(simple_one_queen(p, WHITE) && \
simple_no_rook(p, WHITE) && \
simple_one_minor_piece(p, WHITE))) \
&& \
(simple_no_queen(p, BLACK) || \
(simple_one_queen(p, BLACK) && \
simple_no_rook(p, BLACK) && \
simple_one_minor_piece(p, BLACK))) )
void eval_simple_init(void);
eval_t eval_simple(pos_t *pos);
#endif /* EVAL_SIMPLE_H */

View File

@@ -1,6 +1,6 @@
/* eval.c - static position evaluation.
*
* Copyright (C) 2021 Bruno Raoult ("br")
* Copyright (C) 2021-2023 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
@@ -16,7 +16,9 @@
#include <list.h>
#include <debug.h>
#include "position.h"
#include "eval.h"
#include "eval-simple.h"
inline eval_t eval_material(pos_t *pos, bool color)
{
@@ -40,25 +42,28 @@ inline eval_t eval_material(pos_t *pos, bool color)
inline eval_t eval_mobility(pos_t *pos, bool color)
{
return popcount64(pos->controlled[color]);
return pos->mobility[color];
}
inline eval_t eval_square_control(pos_t *pos, bool color)
{
return pos->mobility[color];
return popcount64(pos->controlled[color]);
}
eval_t eval(pos_t *pos)
{
eval_t material[2] = {0}, control[2] = {0};
eval_t simple = 0, control[2] = {0};
if (pos->eval != EVAL_INVALID)
return pos->eval;
/* 1) pieces value */
material[WHITE] = eval_material(pos, WHITE);
material[BLACK] = eval_material(pos, BLACK);
//material[WHITE] = eval_material(pos, WHITE);
//material[BLACK] = eval_material(pos, BLACK);
simple = eval_simple(pos);
# ifdef DEBUG_EVAL
log_f(2, "material: W:%d B:%d diff=%d\n",
material[WHITE], material[BLACK], material[WHITE] - material[BLACK]);
log_f(2, "eval_simple=%d\n", simple);
# endif
/* 2) square control: 10 square controls diff = 1 pawn */
@@ -79,49 +84,12 @@ eval_t eval(pos_t *pos)
(pos->mobility[WHITE] - pos->mobility[BLACK]) * 10);
# endif
eval_t res = material[WHITE] - material[BLACK] +
eval_t res = simple +
(control[WHITE] - control[BLACK]) * 10 +
(pos->mobility[WHITE] - pos->mobility[BLACK]) * 10;
# ifdef DEBUG_EVAL
log_f(2, "eval: %d\n", res);
# endif
pos->eval = res;
return res;
}
#ifdef BIN_eval
#include "fen.h"
#include "move.h"
int main(int ac, char**av)
{
pos_t *pos;
eval_t res;
debug_init(5, stderr);
piece_pool_init();
moves_pool_init();
pos_pool_init();
pos = pos_get();
if (ac == 1) {
pos_startpos(pos);
} else {
fen2pos(pos, av[1]);
}
pos_print(pos);
pos_pieces_print(pos);
moves_gen(pos, OPPONENT(pos->turn), false);
//moves_print(pos, M_PR_SEPARATE);
//pos_print(pos);
//pos_pieces_print(pos);
moves_gen(pos, pos->turn, true);
pos_print(pos);
moves_print(pos, M_PR_SEPARATE);
res = eval(pos);
printf("eval=%d centipawns)\n", res);
}
#endif

View File

@@ -14,7 +14,19 @@
#ifndef EVAL_H
#define EVAL_H
#include "position.h"
#include <limits.h>
#include "chessdefs.h"
#include "piece.h"
/* max pieces eval is KING_VALUE + 9*QUEEN_VALUE + 2*ROOK_VALUE + 2*BISHOP_VALUE
* + 2*KNIGHT_VALUE which around 30000.
* We are on secure side with -50000/+50000
*/
#define EVAL_MAX (50000)
#define EVAL_MIN (-EVAL_MAX)
#define EVAL_INVALID INT_MIN
#define EVAL_MATE EVAL_MAX
eval_t eval_material(pos_t *pos, bool color);
eval_t eval_mobility(pos_t *pos, bool color);

View File

@@ -173,21 +173,3 @@ pos_t *fen2pos(pos_t *pos, char *fen)
# endif
return pos;
}
#ifdef BIN_fen
int main(int ac, char**av)
{
pos_t *pos;
debug_init(5, stderr);
piece_pool_init();
pos_pool_init();
pos = pos_get();
if (ac == 1) {
pos_startpos(pos);
} else {
fen2pos(pos, av[1]);
}
pos_print(pos);
}
#endif

View File

@@ -16,12 +16,16 @@
#include <br.h>
#include <list.h>
#include <list_sort.h>
#include <debug.h>
#include "chessdefs.h"
#include "board.h"
#include "bitboard.h"
#include "piece.h"
#include "move.h"
#include "eval.h"
#include "eval-simple.h"
static pool_t *moves_pool;
@@ -37,6 +41,7 @@ static struct vector {
[KING] = { 8, 0, { -1, -16, 1, 16, -15, -17, 15, 17 }}
};
/* squares needed to be empty & not controlled by opponent for castle.
* For black castle, same values 7 rows higher (>> 7*8)
*/
@@ -62,47 +67,69 @@ void moves_pool_stats()
pool_stats(moves_pool);
}
int move_print(move_t *move, move_flags_t flags)
/**
* move_print() - print a move
* @movenum: move number
* @move: &move to display
* @flags: options to display
*
* Possible flags are:
* M_PR_CAPT: print move if capture
* M_PR_NCAPT: print move if non capture
* M_PR_NUM: print also move number
* M_PR_LONG: print long notation
* M_PR_NL: print a newline after move
* M_PR_EVAL: print move eval
*
* @return: 0 if nothing printed, 1 otherwise
*/
int move_print(int movenum, move_t *move, move_flags_t flags)
{
if (flags & M_PR_CAPT && !(move->flags & M_CAPTURE)) {
if ((flags & M_PR_CAPT) && !(move->flags & M_CAPTURE)) {
# ifdef DEBUG_MOVE
log_i(9, "skipping capture & %#04x\n", move->flags);
# endif
return 0;
}
if (flags & M_PR_NCAPT && move->flags & M_CAPTURE) {
if ((flags & M_PR_NCAPT) && (move->flags & M_CAPTURE)) {
# ifdef DEBUG_MOVE
log_i(9, "skipping !capture & %#04x\n", move->flags);
# endif
return 0;
}
if (flags & M_PR_NUM)
log(1, "%d:", movenum);
if (move->flags & M_CASTLE_K) {
printf("O-O");
log(1, "O-O");
goto end;
} else if (move->flags & M_CASTLE_Q) {
printf("O-O-O");
log(1, "O-O-O");
goto end;
} else {
printf("%s%c%c", P_SYM(move->piece),
log(1, "%s%c%c", P_SYM(move->piece),
FILE2C(F88(move->from)),
RANK2C(R88(move->from)));
if (move->taken) {
printf("x");
if (move->flags & M_CAPTURE) {
log(1, "x");
if (flags & M_PR_LONG)
printf("%s", P_SYM(move->taken));
log(1, "%s", P_SYM(move->capture));
} else {
printf("-");
log(1, "-");
}
printf("%c%c",
log(1, "%c%c",
FILE2C(F88(move->to)),
RANK2C(R88(move->to)));
if (flags & M_PR_LONG && move->flags & M_EN_PASSANT)
printf("e.p.");
log(1, "e.p.");
if (move->promotion)
printf("=%s", P_SYM(move->promotion));
log(1, "=%s", P_SYM(move->promotion));
if (flags & M_PR_EVAL)
log(1, "[ev:%d] ", move->eval);
end:
printf(" ");
log(1, " ");
}
if (flags & M_PR_NL)
log(1, "\n");
return 1;
}
@@ -110,39 +137,41 @@ void moves_print(pos_t *pos, move_flags_t flags)
{
struct list_head *p_cur, *tmp;
move_t *move;
move_flags_t details = flags & M_PR_LONG;
int movenum;
for (int color=WHITE; color <= BLACK; ++color) {
int verif = 0;
printf("%s pseudo-moves:\n\t", color == WHITE? "White": "Black");
log(1, "%s pseudo-moves:\n\t", color == WHITE? "White": "Black");
if (! (flags & M_PR_SEPARATE)) {
movenum = 1;
list_for_each_safe(p_cur, tmp, &pos->moves[color]) {
move = list_entry(p_cur, move_t, list);
verif += move_print(move, details);
verif += move_print(movenum++, move, flags);
}
} else {
printf("captures: ");
log(1, "captures: ");
movenum = 1;
list_for_each_safe(p_cur, tmp, &pos->moves[color]) {
move = list_entry(p_cur, move_t, list);
verif += move_print(move, details | M_PR_CAPT);
verif += move_print(movenum++, move, flags | M_PR_CAPT);
}
printf("\n\tothers : ");
movenum = 1;
log(1, "\n\tothers : ");
list_for_each_safe(p_cur, tmp, &pos->moves[color]) {
move = list_entry(p_cur, move_t, list);
verif += move_print(move, details | M_PR_NCAPT);
verif += move_print(movenum++, move, flags | M_PR_NCAPT);
}
}
printf("\n\tTotal moves = %d mobility=%u\n", verif, pos->mobility[color]);
log(1, "\n\tTotal moves = %d mobility=%u\n", verif, pos->mobility[color]);
}
}
static move_t *move_add(pos_t *pos, piece_t piece, square_t from,
square_t to)
{
//pos_t *newpos;
board_t *board = pos->board;
move_t *move;
int color = COLOR(piece);
int color = COLOR(pos->board[from].piece);
# ifdef DEBUG_MOVE
log_i(3, "piece_color=%d turn=%d from=%c%c to=%c%c\n",
@@ -152,8 +181,6 @@ static move_t *move_add(pos_t *pos, piece_t piece, square_t from,
FILE2C(F88(to)),
RANK2C(R88(to)));
# endif
//if (COLOR(piece) != pos->turn)
// return NULL;
/* invalid position if opponent king is attacked
*/
if (board[to].piece & KING) {
@@ -167,44 +194,33 @@ static move_t *move_add(pos_t *pos, piece_t piece, square_t from,
return NULL;
list_add(&move->list, &pos->moves[color]);
move->piece = piece;
move->piece = piece | color;
move->from = from;
move->to = to;
move->taken = board[to].piece;
move->capture = board[to].piece;
if (PIECE(move->capture) == KING)
pos->check[color]++;
move->flags = M_NORMAL;
move->newpos = pos_dup(pos);
//newpos = move->newpos;
//SET_COLOR(newpos->turn, OPPONENT(move->piece));
//newpos->turn = OPPONENT(newpos->turn);
if (move->taken) {
if (move->capture)
move->flags |= M_CAPTURE;
/* remove taken piece from new position piece list
* this does not apply for en passant
*/
//piece_del(&newpos->board[to].s_piece->list);
/* remove occupied bitboard */
//newpos->occupied[OPPONENT(COLOR(piece))] ^= SQ88_2_BB(to);
}
/* always make "to" the piece square in new position */
//newpos->board[to] = newpos->board[from];
/* fix dest square */
//newpos->board[to].s_piece->square = to;
/* replace old occupied bitboard by new one */
//newpos->occupied[COLOR(piece)] ^= SQ88_2_BB(from);
//newpos->occupied[COLOR(piece)] |= SQ88_2_BB(to);
/* always make "from" square empty */
//newpos->board[from].piece = 0;
//newpos->board[from].s_piece = NULL;
move->pos = NULL;
# ifdef DEBUG_MOVE
log_i(3, "added move from %c%c to %c%c\n",
log_i(3, "added %s %s move from %c%c to %c%c\n",
COLOR(move->piece)? "black": "white",
P_NAME(PIECE(move->piece)),
FILE2C(F88(move->from)), RANK2C(R88(move->from)),
FILE2C(F88(move->to)), RANK2C(R88(move->to)));
# endif
return move;
}
/**
* move_del() - delete a move from list.
* @ptr: move &list_head
*
* Remove the move whose 'list' element address is @ptr.
*/
void move_del(struct list_head *ptr)
{
move_t *move = list_entry(ptr, move_t, list);
@@ -215,15 +231,19 @@ void move_del(struct list_head *ptr)
FILE2C(F88(move->to)), RANK2C(R88(move->to)));
# endif
/* TODO: remove move->pos if non null */
if (move->newpos) {
pos_clear(move->newpos);
}
if (move->pos)
pos_del(move->pos);
list_del(ptr);
pool_add(moves_pool, move);
return;
}
/**
* move_del() - delete all position moves.
* @ppos: &position.
*
* Remove all generated moves from @pos structure.
*/
int moves_del(pos_t *pos)
{
struct list_head *p_cur, *tmp, *head;
@@ -238,7 +258,7 @@ int moves_del(pos_t *pos)
}
}
# ifdef DEBUG_PIECE
log_f(3, "removed=%d\n", count);
log_f(3, "%d moves removed\n", count);
# endif
return count;
}
@@ -250,34 +270,28 @@ static move_t *move_pawn_add(pos_t *pos, piece_t piece, square_t from,
move_t *move;
piece_t promote;
unsigned char color = COLOR(piece);
pos_t *newpos;
//if (color != pos->turn)
// return NULL;
if (R88(from) == rank7) { /* promotion */
for (promote = QUEEN; promote > PAWN; promote >>= 1) {
if ((move = move_add(pos, piece, from, to))) {
move->flags |= M_PROMOTION;
move->promotion = promote | color;
/* fix piece on board and piece list */
newpos = move->newpos;
newpos->board[to].piece = promote|color;
newpos->board[to].s_piece->piece = piece|color;
newpos->board[to].s_piece->value = piece_details[PIECE(piece)].value;
//piece_del(&newpos->board[from].s_piece);
}
}
} else {
move = move_add(pos, piece, from, to);
newpos = move->newpos;
}
return move;
}
/* pawn moves. We do not test for valid destination square here,
* assuming position is valid. Is that correct ?
/**
* pseudo_moves_pawn() - generate moves for pawn.
* @pos: &position
* @ppiece: &piece_list pawn structure pointer
* @doit: add move to moves list
*
* Calculate all possible moves for @ppiece pawn.
* If @doit is true, add moves to @pos' moves list.
*/
int pseudo_moves_pawn(pos_t *pos, piece_list_t *ppiece, bool doit)
{
@@ -323,7 +337,6 @@ int pseudo_moves_pawn(pos_t *pos, piece_list_t *ppiece, bool doit)
# ifdef DEBUG_MOVE
log_i(4, "pushing pawn %#04x\n", square);
# endif
//log_f(4, "pawn move mobility\n");
pos->mobility[color]++;
count++;
if (doit)
@@ -356,16 +369,10 @@ int pseudo_moves_pawn(pos_t *pos, piece_list_t *ppiece, bool doit)
# endif
if (sq_file == ep_file - 1 || sq_file == ep_file + 1) {
square_t t_square = SQ88(ep_file, rank5); /* taken pawn square */
piece_t taken = board[t_square].piece;
piece_t captured = board[t_square].piece;
move = move_pawn_add(pos, piece | color , square, pos->en_passant, rank7);
move->flags |= M_EN_PASSANT | M_CAPTURE;
move->taken = taken;
/* remove taken pawn from board */
piece_del(&move->newpos->board[t_square].s_piece->list);
move->newpos->board[t_square].piece = 0;
move->newpos->board[t_square].s_piece = NULL;
move->capture = captured;
pos->mobility[color]++;
count++;
}
@@ -382,6 +389,8 @@ int pseudo_moves_pawn(pos_t *pos, piece_list_t *ppiece, bool doit)
continue;
pos->controlled[color] |= SQ88_2_BB(new);
if (board[new].piece && COLOR(board[new].piece) != color) {
if (PIECE(board[new].piece) == KING)
pos->check[color]++;
//log_f(2, "pawn capture mobility\n");
pos->mobility[color]++;
count++;
@@ -403,9 +412,21 @@ int pseudo_moves_pawn(pos_t *pos, piece_list_t *ppiece, bool doit)
return count;
}
int pseudo_moves_castle(pos_t *pos, bool color, bool doit)
/**
* pseudo_moves_castle() - generate castle moves.
* @pos: &position
* @color: side for which to generate moves
* @doit: add move to moves list
* @do_king: count king moves in mobility
*
* Calculate the possible castle moves for @color side.
* If @doit is true, add moves to @pos' moves list.
* If @do_king is true, account king moves (incl. castle) to mobility.
*
* @return: The number of possible king moves.
*/
int pseudo_moves_castle(pos_t *pos, bool color, bool doit, bool do_king)
{
//unsigned char color = pos->turn;
board_t *board = pos->board;
unsigned char rank1, castle_K, castle_Q;
move_t *move = NULL;
@@ -413,7 +434,7 @@ int pseudo_moves_castle(pos_t *pos, bool color, bool doit)
struct can_castle *can_castle;
bitboard_t controlled;
bitboard_t occupied = pos->occupied[WHITE] | pos->occupied[BLACK];
pos_t *newpos;
//pos_t *newpos;
# ifdef DEBUG_MOVE
log_f(2, "pos:%p turn:%s color:%s\n",
@@ -448,22 +469,15 @@ int pseudo_moves_castle(pos_t *pos, bool color, bool doit)
# endif
goto next;
}
pos->mobility[color]++;
count++;
if (do_king) {
pos->mobility[color]++;
count++;
}
if (doit) {
move = move_add(pos, board[SQ88(4, rank1)].piece,
SQ88(4, rank1), SQ88(6, rank1));
if (move) {
newpos = move->newpos;
if (move)
move->flags |= M_CASTLE_K;
/* move King rook to column F */
newpos->board[SQ88(5, rank1)] = newpos->board[SQ88(7, rank1)];
SETF88(newpos->board[SQ88(5, rank1)].s_piece->square, 5);
newpos->board[SQ88(7, rank1)].piece = 0;
newpos->board[SQ88(7, rank1)].s_piece = NULL;
}
}
}
@@ -481,29 +495,33 @@ next:
# endif
goto end;
}
pos->mobility[color]++;
count++;
if (do_king) {
pos->mobility[color]++;
count++;
}
if (doit) {
move = move_add(pos, board[SQ88(4, rank1)].piece,
SQ88(4, rank1), SQ88(2, rank1));
if (move) {
newpos = move->newpos;
if (move)
move->flags |= M_CASTLE_Q;
/* move King rook to column F */
newpos->board[SQ88(3, rank1)] = newpos->board[SQ88(0, rank1)];
SETF88(newpos->board[SQ88(3, rank1)].s_piece->square, 3);
newpos->board[SQ88(0, rank1)].piece = 0;
newpos->board[SQ88(0, rank1)].s_piece = NULL;
}
}
}
end:
return count;
}
/* general rule moves for non pawn pieces
/**
* pseudo_moves_gen() - general move generation for non pawn pieces
* @pos: &position
* @ppiece: &piece_list structure pointer
* @doit: add move to moves list
* @do_king: count king moves
*
* Calculate all possible moves for @ppiece.
* If @doit is true, add moves to @pos' moves list.
* If @do_king is true, account king moves (incl. castle) to mobility.
*/
int pseudo_moves_gen(pos_t *pos, piece_list_t *ppiece, bool doit)
int pseudo_moves_gen(pos_t *pos, piece_list_t *ppiece, bool doit, bool do_king)
{
piece_t piece = PIECE(ppiece->piece);
unsigned char color = COLOR(ppiece->piece);
@@ -569,13 +587,12 @@ int pseudo_moves_gen(pos_t *pos, piece_list_t *ppiece, bool doit)
}
/* we are sure the move is valid : we create move */
//log_f(2, "piece mobility\n");
pos->mobility[color]++;
count++;
if (doit) {
//move = move_add(pos, ppiece->piece, square, new);
move_add(pos, ppiece->piece, square, new);
if (piece != KING || do_king) {
pos->mobility[color]++;
count++;
}
if (doit)
move_add(pos, ppiece->piece, square, new);
if (board[new].piece) { /* stopper move */
break;
}
@@ -596,7 +613,18 @@ int pseudo_moves_gen(pos_t *pos, piece_list_t *ppiece, bool doit)
return count;
}
int moves_gen(pos_t *pos, bool color, bool doit)
/**
* moves_gen() - move generation for one color
* @pos: &position
* @color: side
* @doit: add move to moves list
* @do_king: count king moves
*
* Calculate all possible moves for @color.
* If @doit is true, add moves to @pos' moves list.
* If @do_king is true, account king moves (incl. castle) to mobility.
*/
int moves_gen(pos_t *pos, bool color, bool doit, bool do_king)
{
struct list_head *p_cur, *tmp, *piece_list;
piece_list_t *piece;
@@ -605,15 +633,19 @@ int moves_gen(pos_t *pos, bool color, bool doit)
# ifdef DEBUG_MOVE
log_f(2, "color:%s doit:%d\n", color? "Black": "White", doit);
# endif
piece_list = &pos->pieces[color];
/* do not generate moves if already done for color */
if (!list_empty(&pos->moves[color]))
doit = false;
piece_list = &pos->pieces[color];
pos->mobility[color] = 0;
pos->controlled[color] = 0;
count += pseudo_moves_castle(pos, color, doit);
count += pseudo_moves_castle(pos, color, doit, do_king);
list_for_each_safe(p_cur, tmp, piece_list) {
piece = list_entry(p_cur, piece_list_t, list);
if (PIECE(piece->piece) != PAWN)
count += pseudo_moves_gen(pos, piece, doit);
count += pseudo_moves_gen(pos, piece, doit, do_king);
else
count += pseudo_moves_pawn(pos, piece, doit);
@@ -622,75 +654,231 @@ int moves_gen(pos_t *pos, bool color, bool doit)
return count;
}
/* note: for now, a new pos is generated */
struct pos *move_do(pos_t *pos, move_t *move)
/**
* moves_gen_king_moves() - adjust king mobility
* @pos: &position
* @color: king color
* @doit: add move to moves list
*
* Compute the number of king moves (incl. castle), after opponent controlled
* are known.
* If @doit is true, add moves to @pos' moves list.
*
* @return: The number of possible king moves.
*/
int moves_gen_king_moves(pos_t *pos, bool color, bool doit)
{
int count = 0;
piece_list_t *king = list_first_entry(&pos->pieces[color], piece_list_t, list);
count = pseudo_moves_castle(pos, king, doit, true);
count += pseudo_moves_gen(pos, king, doit, true);
return count;
}
static int moves_cmp_eval(__unused void *data, const struct list_head *h1, const struct list_head *h2)
{
move_t *m1 = list_entry(h1, move_t, list);
move_t *m2 = list_entry(h2, move_t, list);
return m2->eval_simple - m1->eval_simple;
}
/**
* moves_sort() sort - sort moves list, best eval first.
* @pos: &position.
*/
void moves_sort(pos_t *pos)
{
list_sort(NULL, &pos->moves[pos->turn], moves_cmp_eval);
}
/**
* moves_gen_all_eval_sort() - calculate/generate/sort moves for side to play.
* @pos: &position
*
* Generate positions for each move for player to move.
* For each of them generate opponents moves, calculate eval, and sort the moves list.
*/
void moves_gen_eval_sort(pos_t *pos)
{
move_t *move;
pos_t *newpos;
moves_gen_all(pos);
list_for_each_entry(move, &pos->moves[pos->turn], list) {
newpos = move_do(pos, move);
move->pos = newpos;
//move_print(0, move, 0);
move->eval_simple = eval_simple(newpos);
newpos->eval_simple = move->eval_simple;
}
moves_sort(pos);
//moves_print(pos, 0);
}
/**
* moves_gen_all() - calculate all moves, and generate moves for side to play.
* @pos: &position
*
* Compute pseudo moves for both sides, and generate moves for player to move.
*/
void moves_gen_all(pos_t *pos)
{
//log_f(1, "turn=%d opponent=%d\n", pos->turn, OPPONENT(pos->turn));
if (!pos->moves_generated) {
if (!pos->moves_counted) {}
moves_gen(pos, OPPONENT(pos->turn), false, false);
moves_gen(pos, pos->turn, true, true);
if (!pos->moves_counted)
moves_gen_king_moves(pos, OPPONENT(pos->turn), false);
pos->moves_counted = true;
pos->moves_generated = true;
}
}
/**
* moves_gen_all_nomoves() - calculate number of moves for each player.
* @pos: &position
*/
void moves_gen_all_nomoves(pos_t *pos)
{
//log_f(1, "turn=%d opponent=%d\n", pos->turn, OPPONENT(pos->turn));
if (!pos->moves_counted) {
moves_gen(pos, OPPONENT(pos->turn), false, false);
moves_gen(pos, pos->turn, false, true);
moves_gen_king_moves(pos, OPPONENT(pos->turn), false);
pos->moves_counted = true;
}
}
/**
* move_do() - execute move in a duplicated position.
* @pos: &pos_t struct on which move will be applied
* @move: &move_t struct to apply
*
* @return: &new position
*/
pos_t *move_do(pos_t *pos, move_t *move)
{
# ifdef DEBUG_MOVE
log_f(3, "++++++++++");
move_print(move, 0);
//log(1, "new move: ");
//move_print(0, move, M_PR_NL | M_PR_LONG);
# endif
pos_t *newpos = pos_dup(pos);
piece_t piece = move->piece;
int color = COLOR(piece);
pos_t *new = pos_dup(pos);
piece_t piece = PIECE(move->piece), newpiece = piece, captured = move->capture;
int color = COLOR(move->piece);
square_t from = move->from, to = move->to;
u64 bb_from = SQ88_2_BB(from), bb_to = SQ88_2_BB(to);
/* todo: en passant
*/
SET_COLOR(pos->turn, OPPONENT(color)); /* pos color */
if (move->taken || PIECE(piece) == PAWN) /* 50 moves */
newpos->clock_50 = 0;
if (move->capture || piece == PAWN) /* 50 moves */
new->clock_50 = 0;
else
newpos->clock_50++;
if (move->taken) { /* */
piece_del(&newpos->board[to].s_piece->list);
newpos->occupied[OPPONENT(color)] ^= SQ88_2_BB(to);
new->clock_50++;
if (move->flags & M_CAPTURE) { /* capture */
if (move->flags & M_EN_PASSANT) {
uchar ep_file = F88(pos->en_passant);
square_t ep_grab = color == WHITE ? SQ88(ep_file, 4): SQ88(ep_file, 3);
u64 bb_ep_grab = SQ88_2_BB(ep_grab);
log_f(5, "en-passant=%d,%d\n", ep_file, color == WHITE ? 4 : 3);
piece_del(&new->board[ep_grab].s_piece->list);
new->board[ep_grab].piece = 0;
new->occupied[OPPONENT(color)] &= ~bb_ep_grab;
new->bb[OPPONENT(color)][BB_PAWN] &= ~bb_ep_grab;
} else {
piece_del(&new->board[to].s_piece->list);
new->board[to].piece = 0;
new->occupied[OPPONENT(color)] &= ~bb_to;
new->bb[OPPONENT(color)][PIECETOBB(captured)] &= ~bb_to;
}
} else if (move->flags & M_CASTLE_Q) {
uchar row = R88(from);
square_t rook_from = SQ88(0, row);
square_t rook_to = SQ88(3, row);
u64 bb_rook_from = SQ88_2_BB(rook_from);
u64 bb_rook_to = SQ88_2_BB(rook_to);
new->board[rook_to] = new->board[rook_from];
new->board[rook_to].s_piece->square = rook_to;
new->occupied[color] &= ~bb_rook_from;
new->occupied[color] |= bb_rook_to;
new->bb[color][PIECETOBB(BB_ROOK)] &= ~bb_rook_from;
new->bb[color][PIECETOBB(BB_ROOK)] |= bb_rook_to;
new->board[rook_from].piece = 0;
new->board[rook_from].s_piece = NULL;
//new->castle &= color == WHITE? ~CASTLE_W: ~CASTLE_B;
} else if (move->flags & M_CASTLE_K) {
uchar row = R88(from);
square_t rook_from = SQ88(7, row);
square_t rook_to = SQ88(5, row);
u64 bb_rook_from = SQ88_2_BB(rook_from);
u64 bb_rook_to = SQ88_2_BB(rook_to);
new->board[rook_to] = new->board[rook_from];
new->board[rook_to].s_piece->square = rook_to;
new->occupied[color] &= ~bb_rook_from;
new->occupied[color] |= bb_rook_to;
new->bb[color][PIECETOBB(BB_ROOK)] &= ~bb_rook_from;
new->bb[color][PIECETOBB(BB_ROOK)] |= bb_rook_to;
new->board[rook_from].piece = 0;
new->board[rook_from].s_piece = NULL;
// new->castle &= color == WHITE? ~CASTLE_W: ~CASTLE_B;
}
newpos->board[to] = newpos->board[from];
new->board[to] = new->board[from];
/* fix dest square */
newpos->board[to].s_piece->square = to;
new->board[to].s_piece->square = to;
if (move->flags & M_PROMOTION) {
log_f(5, "promotion to %s\n", P_SYM(move->promotion));
log_f(5, "newpiece=%#x p=%#x\n", move->promotion, PIECE(move->promotion));
newpiece = PIECE(move->promotion);
new->board[to].piece = move->promotion;
new->board[to].s_piece->piece = move->promotion;
}
/* replace old occupied bitboard by new one */
newpos->occupied[color] ^= SQ88_2_BB(from);
newpos->occupied[color] |= SQ88_2_BB(to);
new->occupied[color] &= ~bb_from;
new->occupied[color] |= bb_to;
new->bb[color][PIECETOBB(piece)] &= ~bb_from;
new->bb[color][PIECETOBB(newpiece)] |= bb_to;
if (move->flags & M_PROMOTION) {
log_f(5, "promotion color=%d bbpiece=%d\n", color, PIECETOBB(newpiece));
//bitboard_print(new->bb[color][PIECETOBB(newpiece)]);
}
/* set en_passant */
new->en_passant = 0;
if (piece == PAWN) {
if (R88(from) == 1 && R88(to) == 3)
pos->en_passant = SQ88(F88(from), 2);
else if (R88(from) == 6 && R88(to) == 4)
pos->en_passant = SQ88(F88(from), 5);
}
/* always make "from" square empty */
newpos->board[from].piece = 0;
newpos->board[from].s_piece = NULL;
new->board[from].piece = 0;
new->board[from].s_piece = NULL;
return 0;
//printf("old turn=%d ", color);
SET_COLOR(new->turn, OPPONENT(color)); /* pos color */
//printf("new turn=%d\n", new->turn);
//fflush(stdout);
/* adjust castling flags */
if ((bb_from | bb_to) & (A1 | E1))
new->castle &= ~CASTLE_WQ;
else if ((bb_from | bb_to) & (E1 | H1))
new->castle &= ~CASTLE_WK;
else if ((bb_from | bb_to) & (A8 | E8))
new->castle &= ~CASTLE_BQ;
else if ((bb_from | bb_to) & (E8 | H8))
new->castle &= ~CASTLE_BK;
return new;
}
void move_undo(pos_t *pos, __unused move_t *move)
{
pos_clear(pos);
pos_del(pos);
}
#ifdef BIN_move
#include "fen.h"
int main(int ac, char**av)
{
pos_t *pos;
debug_init(5, stderr);
piece_pool_init();
moves_pool_init();
pos_pool_init();
pos = pos_get();
if (ac == 1) {
fen2pos(pos, "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2");
//pos_startpos(pos);
} else {
fen2pos(pos, av[1]);
}
//printf("turn = %d opponent = %d\n", pos->turn, OPPONENT(pos->turn));
moves_gen(pos, WHITE, false);
moves_gen(pos, BLACK, false);
pos_print(pos);
pos_pieces_print(pos);
moves_print(pos, M_PR_SEPARATE);
//bitboard_print2(castle_squares[0].controlled, castle_squares[1].controlled);
//bitboard_print2(castle_squares[0].occupied, castle_squares[1].occupied);
}
#endif /* BIN_move */

View File

@@ -32,35 +32,48 @@ typedef unsigned char move_flags_t;
/* moves_print flags
*/
#define M_PR_SEPARATE 0x40 /* separate capture/non capture */
#define M_PR_LONG 0x80
#define M_PR_CAPT 0x01
#define M_PR_NCAPT 0x02
#define M_PR_NUM 0x04
#define M_PR_NL 0x08
#define M_PR_EVAL 0x20 /* separate captures */
#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 taken; /* removed piece */
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 */
struct pos_s *newpos; /* resulting position */
} move_t;
pool_t *moves_pool_init();
void moves_pool_stats();
int move_print(move_t *move, move_flags_t flags);
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);
int pseudo_moves_castle(pos_t *pos, bool color, bool doit);
int pseudo_moves_gen(pos_t *pos, piece_list_t *piece, bool doit);
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 moves_gen(pos_t *pos, bool color, 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);
struct pos *move_do(pos_t *pos, move_t *move);
void moves_sort(pos_t *pos);
void moves_gen_eval_sort(pos_t *pos);
void moves_gen_all(pos_t *pos);
void moves_gen_all_nomoves(pos_t *pos);
pos_t *move_do(pos_t *pos, move_t *move);
void move_undo(pos_t *pos, move_t *move);
#endif /* MODE_H */
#endif /* MOVE_H */

View File

@@ -21,6 +21,7 @@
#include "chessdefs.h"
#include "piece.h"
#include "board.h"
#include "bitboard.h"
#include "position.h"
static pool_t *pieces_pool;
@@ -74,8 +75,11 @@ piece_list_t *piece_add(pos_t *pos, piece_t piece, square_t square)
P_NAME(piece), FILE2C(F88(square)), RANK2C(R88(square)));
# endif
if ((new = pool_get(pieces_pool))) {
list_add_tail(&new->list, &pos->pieces[color]);
//color? &pos->pieces_black: &pos->pieces_white);
/* first piece is always king */
if (PIECE(piece) == KING)
list_add(&new->list, &pos->pieces[color]);
else
list_add_tail(&new->list, &pos->pieces[color]);
new->piece = piece;
new->square = square;
new->castle = 0;
@@ -112,54 +116,3 @@ int pieces_del(pos_t *pos, short color)
# endif
return count;
}
#ifdef BIN_piece
#include "fen.h"
int main(int ac, char**av)
{
pos_t *pos;
printf("zobi\n");fflush(stdout);
debug_init(6, stderr);
log_f(5, "kfsjdhg\n");
pos_pool_init();
pos = pos_get();
piece_pool_init();
if (ac == 1) {
printf("zoba\n");fflush(stdout);
pos_startpos(pos);
} else {
fen2pos(pos, av[1]);
}
pos_print(pos);
pos_pieces_print(pos);
printf("0x1c = 11100 = C1-E1:\n");
bitboard_print(0x1c);
printf("0x70 = 111 = A1-C1\n");
bitboard_print(0x70);
printf("0x0e = 1110 = B1-D1\n");
bitboard_print(0x0e);
printf("0x60 = 1100000 = F1-G1\n");
bitboard_print(0x60);
printf("A1:\n");
bitboard_print(A1);
printf("1:\n");
bitboard_print(1L);
printf("H1:\n");
bitboard_print(H1);
printf("C1:\n");
bitboard_print(C1);
printf("D1:\n");
bitboard_print(D1);
printf("C1|D1:\n");
bitboard_print(C1|D1);
printf("H8:\n");
bitboard_print(H8);
}
#endif

View File

@@ -22,6 +22,7 @@
#include "move.h"
#include "fen.h"
#include "piece.h"
#include "eval.h"
static pool_t *pos_pool;
@@ -54,6 +55,10 @@ inline void bitboard_print2(bitboard_t bb1, bitboard_t bb2)
BYTE2BIN(bb2>>i));
}
/**
* pos_pieces_print() - Print position pieces
* @pos: &position
*/
void pos_pieces_print(pos_t *pos)
{
printf("White pieces (%d): \t", popcount64(pos->occupied[WHITE]));
@@ -62,16 +67,31 @@ void pos_pieces_print(pos_t *pos)
piece_list_print(&pos->pieces[BLACK]);
}
/* void pos_print - Print position on stdout.
* @pos: Position address (pos_t * )
*
* Return: None.
/**
* pos_bitboards_print() - Print position bitboards
* @pos: &position
*/
void pos_bitboards_print(pos_t *pos)
{
printf("Bitboards occupied :\n");
bitboard_print2(pos->occupied[WHITE], pos->occupied[BLACK]);
printf("Bitboards controlled :\n");
bitboard_print2(pos->controlled[WHITE], pos->controlled[BLACK]);
}
/**
* pos_print() - Print position on stdout.
* @pos: &position
*/
void pos_print(pos_t *pos)
{
int rank, file;
piece_t piece;
board_t *board = pos->board;
piece_list_t *wk = list_first_entry(&pos->pieces[WHITE], piece_list_t, list),
*bk = list_first_entry(&pos->pieces[BLACK], piece_list_t, list);
printf(" +---+---+---+---+---+---+---+---+\n");
for (rank = 7; rank >= 0; --rank) {
@@ -85,10 +105,10 @@ void pos_print(pos_t *pos)
printf(" A B C D E F G H\n\n");
printf("Turn: %s.\n", IS_WHITE(pos->turn) ? "white" : "black");
printf("Kings: W:%c%c B:%c%c\n",
FILE2C(F88(pos->king[WHITE])),
RANK2C(R88(pos->king[WHITE])),
FILE2C(F88(pos->king[BLACK])),
RANK2C(R88(pos->king[BLACK])));
FILE2C(F88(wk->square)),
RANK2C(R88(wk->square)),
FILE2C(F88(bk->square)),
RANK2C(R88(bk->square)));
printf("Possible en-passant: [%#x] ", pos->en_passant);
if (pos->en_passant == 0)
printf("None.\n");
@@ -116,10 +136,52 @@ void pos_print(pos_t *pos)
popcount64(pos->controlled[BLACK]));
printf("Mobility: W:%u B:%u\n", pos->mobility[WHITE],
pos->mobility[BLACK]);
printf("Bitboards occupied :\n");
bitboard_print2(pos->occupied[WHITE], pos->occupied[BLACK]);
printf("Bitboards controlled :\n");
bitboard_print2(pos->controlled[WHITE], pos->controlled[BLACK]);
}
/**
* pos_check() - extensive position consistenci check.
* @pos: &position
*/
void pos_check(pos_t *pos)
{
int rank, file;
piece_t piece;
board_t *board = pos->board;
/* check that board and bitboard reflect same information */
for (rank = 7; rank >= 0; --rank) {
for (file = 0; file < 8; ++file) {
piece_list_t *ppiece;
printf("checking %c%c ", file+'a', rank+'1');
piece = board[SQ88(file, rank)].piece;
ppiece= board[SQ88(file, rank)].s_piece;
printf("piece=%s ", P_CSYM(piece));
if (ppiece)
printf("ppiece=%s/sq=%#x ", P_CSYM(ppiece->piece), ppiece->square);
switch(PIECE(piece)) {
case PAWN:
printf("pawn" );
break;
case KNIGHT:
printf("knight ");
break;
case BISHOP:
printf("bishop ");
break;
case ROOK:
printf("rook ");
break;
case QUEEN:
printf("queen ");
break;
case KING:
printf("king ");
break;
}
printf("\n");
}
}
}
pos_t *pos_clear(pos_t *pos)
@@ -138,13 +200,12 @@ pos_t *pos_clear(pos_t *pos)
}
SET_WHITE(pos->turn);
pos->node_count = 0;
pos->castle = 0;
pos->clock_50 = 0;
pos->curmove = 0;
pos->eval = 0;
pos->en_passant = 0;
pos->king[WHITE] = 0;
pos->king[BLACK] = 0;
pos->occupied[WHITE] = 0;
pos->occupied[BLACK] = 0;
for (int color=0; color<2; ++color)
@@ -154,6 +215,8 @@ pos_t *pos_clear(pos_t *pos)
pos->controlled[BLACK] = 0;
pos->mobility[WHITE] = 0;
pos->mobility[BLACK] = 0;
pos->moves_generated = false;
pos->moves_counted = false;
/* remove pieces / moves */
pieces_del(pos, WHITE);
pieces_del(pos, BLACK);
@@ -162,6 +225,18 @@ pos_t *pos_clear(pos_t *pos)
return pos;
}
/**
* pos_del() - delete a position.
* @pos: &position.
*/
void pos_del(pos_t *pos)
{
pieces_del(pos, WHITE);
pieces_del(pos, BLACK);
moves_del(pos);
pool_add(pos_pool, pos);
}
pos_t *pos_startpos(pos_t *pos)
{
static char *startfen="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
@@ -185,10 +260,26 @@ pos_t *pos_get()
return pos;
}
/* TODO: merge with pos_get - NULL for init, non null for duplicate */
/**
* pos_dup() - duplicate a position.
* @pos: &position to duplicate.
*
* New position is the same as source one (with duplicated pieces list),
* except:
* - moves list is empty
* - bestmove is NULL
* - nodecount is set to zero
* - eval is set to EVAL_INVALID
* - moves_generated ans moves_counted are unset
* - check is set to zero
*
* @return: The new position.
*
* TODO: merge with pos_get - NULL for init, non null for duplicate
*/
pos_t *pos_dup(pos_t *pos)
{
struct list_head *p_cur, *tmp, *piece_list;
struct list_head *p_cur, *piece_list;
piece_list_t *oldpiece;
board_t *board;
pos_t *new = pool_get(pos_pool);
@@ -196,22 +287,24 @@ pos_t *pos_dup(pos_t *pos)
if (new) {
board = new->board;
*new = *pos;
INIT_LIST_HEAD(&new->pieces[WHITE]);
INIT_LIST_HEAD(&new->pieces[BLACK]);
INIT_LIST_HEAD(&new->moves[WHITE]);
INIT_LIST_HEAD(&new->moves[BLACK]);
/* duplicate piece list */
for (int color=0; color<2; ++color) {
for (int color = 0; color < 2; ++color) {
INIT_LIST_HEAD(&new->pieces[color]);
INIT_LIST_HEAD(&new->moves[color]);
/* duplicate piece list */
piece_list = &pos->pieces[color]; /* white/black piece list */
list_for_each_safe(p_cur, tmp, piece_list) {
list_for_each(p_cur, piece_list) {
oldpiece = list_entry(p_cur, piece_list_t, list);
board[oldpiece->square].s_piece =
piece_add(new, oldpiece->piece, oldpiece->square);
}
}
new->bestmove = NULL;
new->node_count = 0;
new->eval = EVAL_INVALID;
new->moves_generated = false;
new->moves_counted = false;
new->check[WHITE] = new->check[BLACK] = 0;
}
return new;
}

View File

@@ -15,42 +15,51 @@
#define POSITION_H
#include <stdint.h>
#include "board.h"
#include <pool.h>
#include <list.h>
#include <bits.h>
#include "board.h"
#include "chessdefs.h"
typedef struct pos_s {
u64 node_count; /* evaluated nodes */
piece_t turn; /* we use only color bit */
castle_t castle;
u16 clock_50;
u16 curmove;
eval_t eval;
int check[2];
int eval_simple_phase;
eval_t eval_simple;
move_t *bestmove;
bool moves_generated;
bool moves_counted;
board_t board[BOARDSIZE];
square_t en_passant;
square_t king[2]; /* obsolete by bb array */
bitboard_t bb[2][BB_END]; /* use: pieces[BLACK][BB_PAWN] */
bitboard_t occupied[2]; /* OR of bb[COLOR][x] */
bitboard_t controlled[2];
u16 mobility[2];
struct list_head pieces[2];
struct list_head pieces[2]; /* pieces list, King is first */
struct list_head moves[2];
} pos_t;
void bitboard_print(bitboard_t bb);
void bitboard_print2(bitboard_t bb1, bitboard_t bb2);
void pos_pieces_print(pos_t *pos);
void pos_bitboards_print(pos_t *pos);
void pos_print(pos_t *pos);
pos_t *pos_clear(pos_t *pos);
void pos_del(pos_t *pos);
pos_t *pos_startpos(pos_t *pos);
pos_t *pos_create();
pool_t *pos_pool_init();
void pos_pool_stats();
pos_t *pos_get();
pos_t *pos_dup(pos_t *pos);
void pos_check(pos_t *pos);
#endif /* POSITION_H */

237
src/search.c Normal file
View File

@@ -0,0 +1,237 @@
/* search.c - search good moves.
*
* Copyright (C) 2023 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 <br.h>
#include <list.h>
#include "debug.h"
#include "move.h"
#include "eval.h"
#include "search.h"
/**
* negamax() - search position negamax.
* @pos: &position to search
* @depth: Wanted depth.
* @color: 1 for white, -1 for black.
*
* Calculate the negamax value of @pos. This is an extensive search, with
* absolutely no cutoff.
*
* @return: The @pos negamax evaluation.
*/
eval_t negamax(pos_t *pos, int depth, int color)
{
move_t *move;
pos_t *newpos;
eval_t best = EVAL_MIN, score;
pos->node_count++;
if (depth == 0) {
moves_gen_all_nomoves(pos);
score = eval(pos) * color;
return score;
}
moves_gen_all(pos);
list_for_each_entry(move, &pos->moves[pos->turn], list) {
newpos = move_do(pos, move);
score = -negamax(newpos, depth - 1, -color);
pos->node_count += newpos->node_count;
move->negamax = score;
if (score > best) {
best = score;
pos->bestmove = move;
}
move_undo(newpos, move);
}
return best;
}
/**
* pvs() - Principal Variation Search.
* @pos: &position to search
* @depth: wanted depth.
* @alpha: alpha value.
* @beta: beta value.
* @color: 1 for white, -1 for black.
*
* Calculate the PVS value of @pos.
* See https://en.wikipedia.org/wiki/Principal_variation_search
*
* Moves list should be first generated and evaluated/sorted.
*
* @return: The @pos PVS evaluation.
*/
eval_t pvs(pos_t *pos, int depth, int alpha, int beta, int color)
{
move_t *move;
pos_t *newpos;
eval_t score = EVAL_INVALID;
bool firstchild = true;
pos->node_count++;
if (depth == 0) {
//return quiesce(p, alpha, beta); /* leaf node */
moves_gen_all_nomoves(pos);
score = eval(pos) * color;
log_f(2, "Terminal: depth=%d ", depth);
log_f(2, "score=%d alpha=%d beta=%d\n", score, alpha, beta);
return score;
}
moves_gen_all(pos);
//moves_print(pos, M_PR_EVAL);
/* do the full search for first child */
//move = list_first_entry_or_null(&pos->moves[pos->turn], move_t, list);
list_for_each_entry(move, &pos->moves[pos->turn], list) {
newpos = move_do(pos, move);
log(2, "%.*s", 5 - depth, " ");
if (firstchild) { /* first child */
score = -pvs(newpos, depth - 1, -beta, -alpha, -color);
log_f(2, "First child depth=%d move=", depth);
//move_print(0, move, 0);
log(2, "score=%d alpha=%d beta=%d\n", score, alpha, beta);
pos->bestmove = move;
} else {
/* search with a null window */
score = -pvs(newpos, depth - 1, -alpha - 1, -alpha, -color);
log_f(2, "Other child depth=%d move=", depth);
//move_print(0, move, 0);
log_f(2, "score=%d alpha=%d beta=%d ", score, alpha, beta);
/* for fail-soft: if (score > alpha && score < beta) */
if (score > alpha) {
/* if failed high, do a full re-search */
log_f(2, "doing full search.");
score = -pvs(newpos, depth - 1, -beta, -alpha, -color);
}
log(2, "\n");
}
pos->node_count += newpos->node_count;
move_undo(newpos, move);
if (score >= beta) { /* fail-hard hard beta cut-off */
log(2, "%.*s", 5 - depth, " ");
log_f(2, "depth=%d score=%d alpha=%d beta=%d beta cut-off.\n",
depth, score, alpha, beta);
return beta;
}
if (score > alpha) {
log(2, "%.*s", 5 - depth, " ");
log_f(2, "depth=%d setting new alpha from %d to %d\n",
depth, alpha, score);
alpha = score;
pos->bestmove = move;
}
move->pos = NULL;
move->negamax = score;
firstchild = false;
}
return alpha;
}
/*
* int negascout (pos_t *pos, int depth, int alpha, int beta )
* { /\* compute minimax value of position p *\/
* move_t *move;
* pos_t *newpos;
* eval_t best = EVAL_MIN, score;
*
* int a, b, t, i;
*
* if (depth == 0) {
* //return quiesce(p, alpha, beta); /\* leaf node *\/
* moves_gen_all_nomoves(pos);
* score = eval(pos) * color;
* return score;
* }
* moves_gen_all(pos);
* a = alpha;
* b = beta;
* list_for_each_entry(move, &pos->moves[pos->turn], list) {
* log(1, "%.*s", 5 - depth, " ");
* newpos = move_do(pos, move);
* // for ( i = 1; i <= w; i++ ) {
* t = -negascout (newpos, depth - 1, -b, -alpha);
* if ( (t > a) && (t < beta) && (i > 1) )
* t = -NegaScout ( p_i, -beta, -alpha ); /\* re-search *\/
* alpha = max( alpha, t );
* if ( alpha >= beta )
* return alpha; /\* cut-off *\/
* b = alpha + 1; /\* set new null window *\/
* }
* return alpha;
* }
*/
/*
* int quiesce(pos_t *pos, int alpha, int beta)
* {
* int stand_pat = eval(pos);
*
* if( stand_pat >= beta )
* return beta;
* if( alpha < stand_pat )
* alpha = stand_pat;
*
* /\*
* * until( every_capture_has_been_examined ) {
* * MakeCapture();
* * score = -Quiesce( -beta, -alpha );
* * TakeBackMove();
* *
* * if( score >= beta )
* * return beta;
* * if( score > alpha )
* * alpha = score;
* * }
* *\/
* return alpha;
* }
*/
/**
* ab_negamax() - search position negamax with alpha-beta cutoff.
* @pos: &position to search
* @depth: Wanted depth.
* @color: 1 for white, -1 for black.
*
* Calculate the negamax value of @pos, with alpha-beta pruning.
*
* @return: The @pos negamax evaluation.
*/
/*int ab_negamax(pos_t *pos, int alpha, int beta, int depth)
{
move_t *move;
pos_t *newpos;
eval_t best = EVAL_MIN, score;
if(depth == 0) {
//return quiesce( alpha, beta );
moves_gen_all_nomoves(pos);
score = eval(pos) * color;
return score;
}
for ( all moves) {
score = -alphaBeta( -beta, -alpha, depthleft - 1 );
if( score >= beta )
return beta; // fail hard beta-cutoff
if( score > alpha )
alpha = score; // alpha acts like max in MiniMax
}
return alpha;
}
*/

View File

@@ -1,6 +1,6 @@
/* pjwhash.c - PJW hash function.
/* search.h - search for perfect move.
*
* Copyright (C) 2021-2022 Bruno Raoult ("br")
* Copyright (C) 2021 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
@@ -11,10 +11,12 @@
*
*/
#define _pjw_inline extern
#ifndef SEARCH_H
#define SEARCH_H
//#include "bits.h"
//extern unsigned int pjwhash (const void* key, uint length);
#include "position.h"
#include "pjwhash.h"
#include "pjwhash-inline.h"
eval_t negamax(pos_t *pos, int depth, int color);
eval_t pvs(pos_t *pos, int depth, int alpha, int beta, int color);
#endif /* SEARCH_H */

35
test/eval.c Normal file
View File

@@ -0,0 +1,35 @@
#include "debug.h"
#include "../src/position.h"
#include "../src/eval.h"
#include "../src/fen.h"
#include "../src/move.h"
int main(int ac, char**av)
{
pos_t *pos;
eval_t res;
debug_init(5, stderr, true);
piece_pool_init();
moves_pool_init();
pos_pool_init();
pos = pos_get();
if (ac == 1) {
pos_startpos(pos);
} else {
fen2pos(pos, av[1]);
}
pos_print(pos);
pos_pieces_print(pos);
moves_gen_all(pos);
pos_print(pos);
moves_print(pos, M_PR_SEPARATE);
res = eval(pos);
printf("eval=%d centipawns)\n", res);
}

20
test/fen.c Normal file
View File

@@ -0,0 +1,20 @@
#include "debug.h"
#include "pool.h"
#include "../src/position.h"
#include "../src/fen.h"
int main(int ac, char**av)
{
pos_t *pos;
debug_init(5, stderr, true);
piece_pool_init();
pos_pool_init();
pos = pos_get();
if (ac == 1) {
pos_startpos(pos);
} else {
fen2pos(pos, av[1]);
}
pos_print(pos);
}

31
test/move.c Normal file
View File

@@ -0,0 +1,31 @@
#include <stdio.h>
#include "debug.h"
#include "../src/fen.h"
#include "../src/move.h"
int main(int ac, char**av)
{
pos_t *pos;
debug_init(5, stderr, true);
piece_pool_init();
moves_pool_init();
pos_pool_init();
pos = pos_get();
if (ac == 1) {
fen2pos(pos, "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2");
//pos_startpos(pos);
} else {
fen2pos(pos, av[1]);
}
//printf("turn = %d opponent = %d\n", pos->turn, OPPONENT(pos->turn));
moves_gen_all(pos);
pos_print(pos);
pos_pieces_print(pos);
moves_print(pos, M_PR_SEPARATE);
//bitboard_print2(castle_squares[0].controlled, castle_squares[1].controlled);
//bitboard_print2(castle_squares[0].occupied, castle_squares[1].occupied);
}

55
test/piece.c Normal file
View File

@@ -0,0 +1,55 @@
#include <stdio.h>
#include "debug.h"
#include "../src/fen.h"
#include "../src/position.h"
#include "../src/bitboard.h"
int main(int ac, char**av)
{
pos_t *pos;
printf("zobi\n");fflush(stdout);
debug_init(6, stderr, true);
log_f(5, "kfsjdhg\n");
pos_pool_init();
pos = pos_get();
piece_pool_init();
if (ac == 1) {
printf("zoba\n");fflush(stdout);
pos_startpos(pos);
} else {
fen2pos(pos, av[1]);
}
pos_print(pos);
pos_pieces_print(pos);
printf("0x1c = 11100 = C1-E1:\n");
bitboard_print(0x1c);
printf("0x70 = 111 = A1-C1\n");
bitboard_print(0x70);
printf("0x0e = 1110 = B1-D1\n");
bitboard_print(0x0e);
printf("0x60 = 1100000 = F1-G1\n");
bitboard_print(0x60);
printf("A1:\n");
bitboard_print(A1);
printf("1:\n");
bitboard_print(1L);
printf("H1:\n");
bitboard_print(H1);
printf("C1:\n");
bitboard_print(C1);
printf("D1:\n");
bitboard_print(D1);
printf("C1|D1:\n");
bitboard_print(C1|D1);
printf("H8:\n");
bitboard_print(H8);
}