commit 8f954a0404ce9be9d8d5cb9bb59257b6cb5b83f3
parent 227d847754d734b8d663443e463161e4857fac6d
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date: Sat, 29 Jul 2023 21:13:58 +0200
Restructure project for better modularity
Diffstat:
27 files changed, 1128 insertions(+), 1052 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -13,17 +13,4 @@ set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
-
-file(GLOB sources "src/*.c")
-add_executable(engine ${sources})
-
-target_link_libraries(engine "cul")
-
-set_target_properties(engine PROPERTIES
- VERSION ${PROJECT_VERSION}
- SOVERSION ${PROJECT_VERSION_MAJOR}
-)
-
-target_include_directories(engine
- PRIVATE "${PROJECT_SOURCE_DIR}/include"
-)
+add_subdirectory(src)
diff --git a/include/attack.h b/include/attack.h
@@ -1,17 +0,0 @@
-#ifndef ATTACK_H
-#define ATTACK_H
-
-#include "utils.h"
-
-void init_leapers_attacks(void);
-void init_sliders_attacks(void);
-
-U64 get_wpawn_attacks(Square square, U64 occupancy);
-U64 get_bpawn_attacks(Square square, U64 occupancy);
-U64 get_knight_attacks(Square square, U64 occupancy);
-U64 get_king_attacks(Square square, U64 occupancy);
-U64 get_bishop_attacks(Square square, U64 occupancy);
-U64 get_rook_attacks(Square square, U64 occupancy);
-U64 get_queen_attacks(Square square, U64 occupancy);
-
-#endif
diff --git a/include/utils.h b/include/utils.h
@@ -1,86 +0,0 @@
-#ifndef __UTILS_H
-#define __UTILS_H
-
-// useful macros
-#define MAX(a, b) ((a > b) ? a : b)
-#define MIN(a, b) ((a < b) ? a : b)
-
-// define number types
-typedef unsigned long long U64; // define bitboard data type
-#define C64(constantU64) constantU64##ULL // define shorthand for constants
-
-typedef unsigned int U32;
-#define C32(constantU32) constantU32##U
-
-// useful bit patterns
-extern const U64 universe;
-extern const U64 notAFile;
-extern const U64 notHFile;
-
-// useful bit functions
-#define bit_get(bitboard, square) (((bitboard) >> (square)) & C64(1))
-#define bit_set(bitboard, square) ((bitboard) |= C64(1) << (square))
-#define bit_pop(bitboard, square) ((bitboard) &= ~(C64(1) << (square)))
-int bit_count(U64 bitboard);
-int bit_lsb_index(U64 bitboard);
-
-#define bitboard_for_each_bit(var, bb) \
- for (var = bit_lsb_index(bb); bb; bit_pop(bb, var), var = bit_lsb_index(bb))
-
-void bitboard_print(U64 bitboard);
-
-// squares
-// clang-format off
-enum enumSquare {
- a1, b1, c1, d1, e1, f1, g1, h1,
- a2, b2, c2, d2, e2, f2, g2, h2,
- a3, b3, c3, d3, e3, f3, g3, h3,
- a4, b4, c4, d4, e4, f4, g4, h4,
- a5, b5, c5, d5, e5, f5, g5, h5,
- a6, b6, c6, d6, e6, f6, g6, h6,
- a7, b7, c7, d7, e7, f7, g7, h7,
- a8, b8, c8, d8, e8, f8, g8, h8, no_sq
-};
-// clang-format on
-typedef enum enumSquare Square;
-
-extern const char *square_to_coordinates[];
-Square coordinates_to_square(char *cord);
-
-// board moving
-typedef U64 (*direction_f)(U64);
-U64 soutOne(U64 b);
-U64 nortOne(U64 b);
-U64 eastOne(U64 b);
-U64 westOne(U64 b);
-U64 soEaOne(U64 b);
-U64 soWeOne(U64 b);
-U64 noEaOne(U64 b);
-U64 noWeOne(U64 b);
-
-// board rotation
-U64 rotateLeft(U64 x, int s);
-U64 rotateRight(U64 x, int s);
-
-// enum types for color and piece type
-enum enumColor {
- WHITE = 0,
- BLACK
-};
-enum enumPiece {
- PAWN = 0,
- KNIGHT,
- BISHOP,
- ROOK,
- QUEEN,
- KING
-};
-
-typedef enum enumColor eColor;
-typedef enum enumPiece ePiece;
-
-int get_time_ms(void);
-
-typedef U64 (*attack_f)(Square square, U64 occupancy);
-
-#endif
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
@@ -0,0 +1,9 @@
+file(GLOB files "*")
+foreach(file in ${files})
+ if(IS_DIRECTORY "${file}" AND EXISTS "${file}/CMakeLists.txt")
+ get_filename_component(mod ${file} NAME)
+ message(STATUS "Found target ${mod}")
+ add_subdirectory(${mod})
+ endif()
+endforeach()
+
diff --git a/src/attack.c b/src/attack.c
@@ -1,366 +0,0 @@
-#include "string.h"
-
-#include "attack.h"
-#include "utils.h"
-
-#define UNUSED(x) (void)(x)
-
-// Magic constants
-
-// clang-format off
-const int bishop_relevant_bits[64] = {
- 6, 5, 5, 5, 5, 5, 5, 6,
- 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 7, 7, 7, 7, 5, 5,
- 5, 5, 7, 9, 9, 7, 5, 5,
- 5, 5, 7, 9, 9, 7, 5, 5,
- 5, 5, 7, 7, 7, 7, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5,
- 6, 5, 5, 5, 5, 5, 5, 6,
-};
-
-const U64 bishop_magic_numbers[64] = {
- C64(0x40040844404084), C64(0x2004208a004208), C64(0x10190041080202),
- C64(0x108060845042010), C64(0x581104180800210), C64(0x2112080446200010),
- C64(0x1080820820060210), C64(0x3c0808410220200), C64(0x4050404440404),
- C64(0x21001420088), C64(0x24d0080801082102), C64(0x1020a0a020400),
- C64(0x40308200402), C64(0x4011002100800), C64(0x401484104104005),
- C64(0x801010402020200), C64(0x400210c3880100), C64(0x404022024108200),
- C64(0x810018200204102), C64(0x4002801a02003), C64(0x85040820080400),
- C64(0x810102c808880400), C64(0xe900410884800), C64(0x8002020480840102),
- C64(0x220200865090201), C64(0x2010100a02021202), C64(0x152048408022401),
- C64(0x20080002081110), C64(0x4001001021004000), C64(0x800040400a011002),
- C64(0xe4004081011002), C64(0x1c004001012080), C64(0x8004200962a00220),
- C64(0x8422100208500202), C64(0x2000402200300c08), C64(0x8646020080080080),
- C64(0x80020a0200100808), C64(0x2010004880111000), C64(0x623000a080011400),
- C64(0x42008c0340209202), C64(0x209188240001000), C64(0x400408a884001800),
- C64(0x110400a6080400), C64(0x1840060a44020800), C64(0x90080104000041),
- C64(0x201011000808101), C64(0x1a2208080504f080), C64(0x8012020600211212),
- C64(0x500861011240000), C64(0x180806108200800), C64(0x4000020e01040044),
- C64(0x300000261044000a), C64(0x802241102020002), C64(0x20906061210001),
- C64(0x5a84841004010310), C64(0x4010801011c04), C64(0xa010109502200),
- C64(0x4a02012000), C64(0x500201010098b028), C64(0x8040002811040900),
- C64(0x28000010020204), C64(0x6000020202d0240), C64(0x8918844842082200),
- C64(0x4010011029020020),
-};
-
-const int rook_relevant_bits[64] = {
- 12, 11, 11, 11, 11, 11, 11, 12,
- 11, 10, 10, 10, 10, 10, 10, 11,
- 11, 10, 10, 10, 10, 10, 10, 11,
- 11, 10, 10, 10, 10, 10, 10, 11,
- 11, 10, 10, 10, 10, 10, 10, 11,
- 11, 10, 10, 10, 10, 10, 10, 11,
- 11, 10, 10, 10, 10, 10, 10, 11,
- 12, 11, 11, 11, 11, 11, 11, 12,
-};
-
-const U64 rook_magic_numbers[64] = {
- C64(0x8a80104000800020), C64(0x140002000100040), C64(0x2801880a0017001),
- C64(0x100081001000420), C64(0x200020010080420), C64(0x3001c0002010008),
- C64(0x8480008002000100), C64(0x2080088004402900), C64(0x800098204000),
- C64(0x2024401000200040), C64(0x100802000801000), C64(0x120800800801000),
- C64(0x208808088000400), C64(0x2802200800400), C64(0x2200800100020080),
- C64(0x801000060821100), C64(0x80044006422000), C64(0x100808020004000),
- C64(0x12108a0010204200), C64(0x140848010000802), C64(0x481828014002800),
- C64(0x8094004002004100), C64(0x4010040010010802), C64(0x20008806104),
- C64(0x100400080208000), C64(0x2040002120081000), C64(0x21200680100081),
- C64(0x20100080080080), C64(0x2000a00200410), C64(0x20080800400),
- C64(0x80088400100102), C64(0x80004600042881), C64(0x4040008040800020),
- C64(0x440003000200801), C64(0x4200011004500), C64(0x188020010100100),
- C64(0x14800401802800), C64(0x2080040080800200), C64(0x124080204001001),
- C64(0x200046502000484), C64(0x480400080088020), C64(0x1000422010034000),
- C64(0x30200100110040), C64(0x100021010009), C64(0x2002080100110004),
- C64(0x202008004008002), C64(0x20020004010100), C64(0x2048440040820001),
- C64(0x101002200408200), C64(0x40802000401080), C64(0x4008142004410100),
- C64(0x2060820c0120200), C64(0x1001004080100), C64(0x20c020080040080),
- C64(0x2935610830022400), C64(0x44440041009200), C64(0x280001040802101),
- C64(0x2100190040002085), C64(0x80c0084100102001), C64(0x4024081001000421),
- C64(0x20030a0244872), C64(0x12001008414402), C64(0x2006104900a0804),
- C64(0x1004081002402),
-};
-
-
-// Utility functions
-
-int hash(U64 key, U64 magic, int relevant_bits) {
- return (key * magic) >> (64 - relevant_bits);
-}
-
-// pseudo random numbers
-
-U32 state = C32(1804289383);
-
-U32 get_random_U32_number() {
- U32 number = state;
-
- number ^= number << 13;
- number ^= number >> 17;
- number ^= number << 5;
-
- return state = number;
-}
-
-U64 get_random_U64_number() {
- U64 n1, n2, n3, n4;
-
- n1 = (U64)(get_random_U32_number()) & C64(0xFFFF);
- n2 = (U64)(get_random_U32_number()) & C64(0xFFFF);
- n3 = (U64)(get_random_U32_number()) & C64(0xFFFF);
- n4 = (U64)(get_random_U32_number()) & C64(0xFFFF);
-
- return n1 | (n2 << 16) | (n3 << 32) | (n4 << 48);
-}
-
-// pawn attack table [side][square]
-U64 pawn_attacks[2][64];
-
-// knight attack table [square]
-U64 knight_attacks[64];
-
-// king attack table [square]
-U64 king_attacks[64];
-
-// bishop attack mask
-U64 bishop_masks[64];
-
-// rook attack mask
-U64 rook_masks[64];
-
-// bishop attack table [square][occupancies]
-U64 bishop_attacks[64][512]; // 256 K
-
-// rook attack table [square][occupancies]
-U64 rook_attacks[64][4096]; // 2048K
-
-U64 get_wpawn_attacks(Square square, U64 occupancy){
- UNUSED(occupancy);
- return pawn_attacks[WHITE][square];
-}
-
-U64 get_bpawn_attacks(Square square, U64 occupancy){
- UNUSED(occupancy);
- return pawn_attacks[BLACK][square];
-}
-
-U64 get_knight_attacks(Square square, U64 occupancy){
- UNUSED(occupancy);
- return knight_attacks[square];
-}
-
-U64 get_king_attacks(Square square, U64 occupancy){
- UNUSED(occupancy);
- return king_attacks[square];
-}
-
-U64 get_bishop_attacks(Square square, U64 occupancy){
- occupancy &= bishop_masks[square];
- occupancy = hash(occupancy, bishop_magic_numbers[square],
- bishop_relevant_bits[square]);
- return bishop_attacks[square][occupancy];
-}
-
-U64 get_rook_attacks(Square square, U64 occupancy){
- occupancy &= rook_masks[square];
- occupancy =
- hash(occupancy, rook_magic_numbers[square], rook_relevant_bits[square]);
- return rook_attacks[square][occupancy];
-}
-
-U64 get_queen_attacks(Square square, U64 occupancy){
- return (get_bishop_attacks(square, occupancy) |
- get_rook_attacks(square, occupancy));
-}
-
-
-// generate pawn attack
-U64 mask_pawn_attacks(int side, Square square) {
- U64 bitboard = C64(0);
-
- bit_set(bitboard, square);
- if (side == WHITE)
- return noWeOne(bitboard) | noEaOne(bitboard);
- else
- return soWeOne(bitboard) | soEaOne(bitboard);
-}
-
-U64 mask_knight_attacks(Square square) {
- U64 bitboard = C64(0), attacks = C64(0), tmp;
-
- bit_set(bitboard, square);
- tmp = nortOne(nortOne(bitboard));
- attacks |= westOne(tmp) | eastOne(tmp);
- tmp = soutOne(soutOne(bitboard));
- attacks |= westOne(tmp) | eastOne(tmp);
- tmp = westOne(westOne(bitboard));
- attacks |= soutOne(tmp) | nortOne(tmp);
- tmp = eastOne(eastOne(bitboard));
- attacks |= soutOne(tmp) | nortOne(tmp);
-
- return attacks;
-}
-
-U64 mask_king_attacks(Square square) {
- U64 bitboard = C64(0), attacks = C64(0);
-
- bit_set(bitboard, square);
- attacks |= westOne(bitboard) | eastOne(bitboard);
- attacks |= soutOne(bitboard) | nortOne(bitboard);
- attacks |= soutOne(bitboard) | nortOne(bitboard);
- attacks |= soEaOne(bitboard) | noEaOne(bitboard);
- attacks |= soWeOne(bitboard) | noWeOne(bitboard);
-
- return attacks;
-}
-
-U64 mask_slide_attacks(Square square, U64 block, const direction_f dir[4],
- int len[4]) {
- U64 bitboard = C64(0), attacks = C64(0), tmp;
- int i, j;
-
- bit_set(bitboard, square);
- for (i = 0; i < 4; i++) {
- for (j = 0, tmp = bitboard; j < len[i]; j++) {
- attacks |= tmp = (dir[i])(tmp);
- if (tmp & block)
- break;
- }
- }
- return attacks;
-}
-
-const direction_f bishop_direction[4] = {noEaOne, noWeOne, soEaOne, soWeOne};
-const direction_f rook_direction[4] = {westOne, soutOne, eastOne, nortOne};
-
-U64 mask_bishop_attacks(Square square) {
- int tr = square / 8, tf = square % 8;
- int len[4] = {MIN(7 - tf, 7 - tr) - 1, MIN(tf, 7 - tr) - 1,
- MIN(7 - tf, tr) - 1, MIN(tf, tr) - 1};
- return mask_slide_attacks(square, C64(0), bishop_direction, len);
-}
-
-U64 mask_rook_attacks(Square square) {
- int tr = square / 8, tf = square % 8;
- int len[4] = {tf - 1, tr - 1, 6 - tf, 6 - tr};
-
- return mask_slide_attacks(square, C64(0), rook_direction, len);
-}
-
-U64 bishop_attacks_on_the_fly(Square square, U64 block) {
- int tr = square / 8, tf = square % 8;
- int len[4] = {MIN(7 - tf, 7 - tr), MIN(tf, 7 - tr), MIN(7 - tf, tr),
- MIN(tf, tr)};
-
- return mask_slide_attacks(square, block, bishop_direction, len);
-}
-
-U64 rook_attacks_on_the_fly(Square square, U64 block) {
- int tr = square / 8, tf = square % 8;
- int len[4] = {tf, tr, 7 - tf, 7 - tr};
-
- return mask_slide_attacks(square, block, rook_direction, len);
-}
-
-void init_leapers_attacks(void) {
- for (Square square = 0; square < 64; square++) {
- pawn_attacks[WHITE][square] = mask_pawn_attacks(WHITE, square);
- pawn_attacks[BLACK][square] = mask_pawn_attacks(BLACK, square);
- knight_attacks[square] = mask_knight_attacks(square);
- king_attacks[square] = mask_king_attacks(square);
- }
-}
-
-U64 set_occupancy(int index, int bits_in_mask, U64 attack_mask) {
- U64 occupancy = C64(0);
-
- for (int count = 0; count < bits_in_mask; count++) {
- Square square = bit_lsb_index(attack_mask);
- bit_pop(attack_mask, square);
-
- if (index & (1 << count))
- bit_set(occupancy, square);
- }
-
- return occupancy;
-}
-
-void init_sliders_attacks_internal(int bishop) {
- for (Square square = 0; square < 64; square++) {
- U64 attack_mask;
-
- if (bishop) {
- bishop_masks[square] = mask_bishop_attacks(square);
- attack_mask = bishop_masks[square];
- } else {
- rook_masks[square] = mask_rook_attacks(square);
- attack_mask = rook_masks[square];
- }
-
- int relevant_bits = bit_count(attack_mask);
- int occupancy_indicies = 1 << relevant_bits;
-
- for (int index = 0; index < occupancy_indicies; index++) {
- U64 occupancy = set_occupancy(index, relevant_bits, attack_mask);
- if (bishop) {
- int magic_index = (occupancy * bishop_magic_numbers[square]) >>
- (64 - bishop_relevant_bits[square]);
- bishop_attacks[square][magic_index] =
- bishop_attacks_on_the_fly(square, occupancy);
- } else {
- int magic_index = hash(occupancy, rook_magic_numbers[square],
- rook_relevant_bits[square]);
- rook_attacks[square][magic_index] =
- rook_attacks_on_the_fly(square, occupancy);
- }
- }
- }
-}
-
-void init_sliders_attacks(void) {
- init_sliders_attacks_internal(0);
- init_sliders_attacks_internal(1);
-}
-
-// magic numbers
-
-U64 generate_magic_number() {
- return get_random_U64_number() & get_random_U64_number() &
- get_random_U64_number();
-}
-
-U64 find_magic_number(Square square, int relevant_bits, int bishop) {
- U64 occupancies[4096], attacks[4096], used_attacks[4096];
- U64 attack_mask =
- bishop ? mask_bishop_attacks(square) : mask_rook_attacks(square);
- int occupancy_indicies = 1 << relevant_bits;
-
- for (int index = 0; index < occupancy_indicies; index++) {
- occupancies[index] = set_occupancy(index, relevant_bits, attack_mask);
- attacks[index] = bishop
- ? bishop_attacks_on_the_fly(square, occupancies[index])
- : rook_attacks_on_the_fly(square, occupancies[index]);
- }
-
- for (int random_count = 0; random_count < 100000000; random_count++) {
- U64 magic_number = generate_magic_number();
- if (bit_count((attack_mask * magic_number) & C64(0xFF00000000000000)) < 6)
- continue;
-
- memset(used_attacks, C64(0), sizeof(used_attacks));
- int index, fail;
-
- for (index = 0, fail = 0; !fail && index < occupancy_indicies; index++) {
- int magic_index = hash(occupancies[index], magic_number, relevant_bits);
-
- if (used_attacks[magic_index] == C64(0))
- used_attacks[magic_index] = attacks[index];
- else if (used_attacks[magic_index] != attacks[index])
- fail = 1;
- }
-
- if (!fail)
- return magic_number;
- }
-
- return C64(0);
-}
diff --git a/src/attack/CMakeLists.txt b/src/attack/CMakeLists.txt
@@ -0,0 +1,7 @@
+add_library(attack OBJECT
+ attack.c
+)
+
+target_include_directories(attack
+ PUBLIC "${PROJECT_SOURCE_DIR}/src/include"
+)
diff --git a/src/attack/attack.c b/src/attack/attack.c
@@ -0,0 +1,371 @@
+#include "string.h"
+
+#include "attack.h"
+#include "utils.h"
+
+#define UNUSED(x) (void)(x)
+
+// Magic constants
+
+// clang-format off
+const int bishop_relevant_bits[64] = {
+ 6, 5, 5, 5, 5, 5, 5, 6,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 7, 7, 7, 7, 5, 5,
+ 5, 5, 7, 9, 9, 7, 5, 5,
+ 5, 5, 7, 9, 9, 7, 5, 5,
+ 5, 5, 7, 7, 7, 7, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 5, 5, 5, 5, 5, 5, 6,
+};
+
+const U64 bishop_magic_numbers[64] = {
+ C64(0x40040844404084), C64(0x2004208a004208), C64(0x10190041080202),
+ C64(0x108060845042010), C64(0x581104180800210), C64(0x2112080446200010),
+ C64(0x1080820820060210), C64(0x3c0808410220200), C64(0x4050404440404),
+ C64(0x21001420088), C64(0x24d0080801082102), C64(0x1020a0a020400),
+ C64(0x40308200402), C64(0x4011002100800), C64(0x401484104104005),
+ C64(0x801010402020200), C64(0x400210c3880100), C64(0x404022024108200),
+ C64(0x810018200204102), C64(0x4002801a02003), C64(0x85040820080400),
+ C64(0x810102c808880400), C64(0xe900410884800), C64(0x8002020480840102),
+ C64(0x220200865090201), C64(0x2010100a02021202), C64(0x152048408022401),
+ C64(0x20080002081110), C64(0x4001001021004000), C64(0x800040400a011002),
+ C64(0xe4004081011002), C64(0x1c004001012080), C64(0x8004200962a00220),
+ C64(0x8422100208500202), C64(0x2000402200300c08), C64(0x8646020080080080),
+ C64(0x80020a0200100808), C64(0x2010004880111000), C64(0x623000a080011400),
+ C64(0x42008c0340209202), C64(0x209188240001000), C64(0x400408a884001800),
+ C64(0x110400a6080400), C64(0x1840060a44020800), C64(0x90080104000041),
+ C64(0x201011000808101), C64(0x1a2208080504f080), C64(0x8012020600211212),
+ C64(0x500861011240000), C64(0x180806108200800), C64(0x4000020e01040044),
+ C64(0x300000261044000a), C64(0x802241102020002), C64(0x20906061210001),
+ C64(0x5a84841004010310), C64(0x4010801011c04), C64(0xa010109502200),
+ C64(0x4a02012000), C64(0x500201010098b028), C64(0x8040002811040900),
+ C64(0x28000010020204), C64(0x6000020202d0240), C64(0x8918844842082200),
+ C64(0x4010011029020020),
+};
+
+const int rook_relevant_bits[64] = {
+ 12, 11, 11, 11, 11, 11, 11, 12,
+ 11, 10, 10, 10, 10, 10, 10, 11,
+ 11, 10, 10, 10, 10, 10, 10, 11,
+ 11, 10, 10, 10, 10, 10, 10, 11,
+ 11, 10, 10, 10, 10, 10, 10, 11,
+ 11, 10, 10, 10, 10, 10, 10, 11,
+ 11, 10, 10, 10, 10, 10, 10, 11,
+ 12, 11, 11, 11, 11, 11, 11, 12,
+};
+
+const U64 rook_magic_numbers[64] = {
+ C64(0x8a80104000800020), C64(0x140002000100040), C64(0x2801880a0017001),
+ C64(0x100081001000420), C64(0x200020010080420), C64(0x3001c0002010008),
+ C64(0x8480008002000100), C64(0x2080088004402900), C64(0x800098204000),
+ C64(0x2024401000200040), C64(0x100802000801000), C64(0x120800800801000),
+ C64(0x208808088000400), C64(0x2802200800400), C64(0x2200800100020080),
+ C64(0x801000060821100), C64(0x80044006422000), C64(0x100808020004000),
+ C64(0x12108a0010204200), C64(0x140848010000802), C64(0x481828014002800),
+ C64(0x8094004002004100), C64(0x4010040010010802), C64(0x20008806104),
+ C64(0x100400080208000), C64(0x2040002120081000), C64(0x21200680100081),
+ C64(0x20100080080080), C64(0x2000a00200410), C64(0x20080800400),
+ C64(0x80088400100102), C64(0x80004600042881), C64(0x4040008040800020),
+ C64(0x440003000200801), C64(0x4200011004500), C64(0x188020010100100),
+ C64(0x14800401802800), C64(0x2080040080800200), C64(0x124080204001001),
+ C64(0x200046502000484), C64(0x480400080088020), C64(0x1000422010034000),
+ C64(0x30200100110040), C64(0x100021010009), C64(0x2002080100110004),
+ C64(0x202008004008002), C64(0x20020004010100), C64(0x2048440040820001),
+ C64(0x101002200408200), C64(0x40802000401080), C64(0x4008142004410100),
+ C64(0x2060820c0120200), C64(0x1001004080100), C64(0x20c020080040080),
+ C64(0x2935610830022400), C64(0x44440041009200), C64(0x280001040802101),
+ C64(0x2100190040002085), C64(0x80c0084100102001), C64(0x4024081001000421),
+ C64(0x20030a0244872), C64(0x12001008414402), C64(0x2006104900a0804),
+ C64(0x1004081002402),
+};
+
+
+// Utility functions
+
+int hash(U64 key, U64 magic, int relevant_bits) {
+ return (key * magic) >> (64 - relevant_bits);
+}
+
+// pseudo random numbers
+
+U32 state = C32(1804289383);
+
+U32 get_random_U32_number() {
+ U32 number = state;
+
+ number ^= number << 13;
+ number ^= number >> 17;
+ number ^= number << 5;
+
+ return state = number;
+}
+
+U64 get_random_U64_number() {
+ U64 n1, n2, n3, n4;
+
+ n1 = (U64)(get_random_U32_number()) & C64(0xFFFF);
+ n2 = (U64)(get_random_U32_number()) & C64(0xFFFF);
+ n3 = (U64)(get_random_U32_number()) & C64(0xFFFF);
+ n4 = (U64)(get_random_U32_number()) & C64(0xFFFF);
+
+ return n1 | (n2 << 16) | (n3 << 32) | (n4 << 48);
+}
+
+// pawn attack table [side][square]
+U64 pawn_attacks[2][64];
+
+// knight attack table [square]
+U64 knight_attacks[64];
+
+// king attack table [square]
+U64 king_attacks[64];
+
+// bishop attack mask
+U64 bishop_masks[64];
+
+// rook attack mask
+U64 rook_masks[64];
+
+// bishop attack table [square][occupancies]
+U64 bishop_attacks[64][512]; // 256 K
+
+// rook attack table [square][occupancies]
+U64 rook_attacks[64][4096]; // 2048K
+
+U64 get_wpawn_attacks(Square square, U64 occupancy){
+ UNUSED(occupancy);
+ return pawn_attacks[WHITE][square];
+}
+
+U64 get_bpawn_attacks(Square square, U64 occupancy){
+ UNUSED(occupancy);
+ return pawn_attacks[BLACK][square];
+}
+
+U64 get_knight_attacks(Square square, U64 occupancy){
+ UNUSED(occupancy);
+ return knight_attacks[square];
+}
+
+U64 get_king_attacks(Square square, U64 occupancy){
+ UNUSED(occupancy);
+ return king_attacks[square];
+}
+
+U64 get_bishop_attacks(Square square, U64 occupancy){
+ occupancy &= bishop_masks[square];
+ occupancy = hash(occupancy, bishop_magic_numbers[square],
+ bishop_relevant_bits[square]);
+ return bishop_attacks[square][occupancy];
+}
+
+U64 get_rook_attacks(Square square, U64 occupancy){
+ occupancy &= rook_masks[square];
+ occupancy =
+ hash(occupancy, rook_magic_numbers[square], rook_relevant_bits[square]);
+ return rook_attacks[square][occupancy];
+}
+
+U64 get_queen_attacks(Square square, U64 occupancy){
+ return (get_bishop_attacks(square, occupancy) |
+ get_rook_attacks(square, occupancy));
+}
+
+
+// generate pawn attack
+U64 mask_pawn_attacks(int side, Square square) {
+ U64 bitboard = C64(0);
+
+ bit_set(bitboard, square);
+ if (side == WHITE)
+ return noWeOne(bitboard) | noEaOne(bitboard);
+ else
+ return soWeOne(bitboard) | soEaOne(bitboard);
+}
+
+U64 mask_knight_attacks(Square square) {
+ U64 bitboard = C64(0), attacks = C64(0), tmp;
+
+ bit_set(bitboard, square);
+ tmp = nortOne(nortOne(bitboard));
+ attacks |= westOne(tmp) | eastOne(tmp);
+ tmp = soutOne(soutOne(bitboard));
+ attacks |= westOne(tmp) | eastOne(tmp);
+ tmp = westOne(westOne(bitboard));
+ attacks |= soutOne(tmp) | nortOne(tmp);
+ tmp = eastOne(eastOne(bitboard));
+ attacks |= soutOne(tmp) | nortOne(tmp);
+
+ return attacks;
+}
+
+U64 mask_king_attacks(Square square) {
+ U64 bitboard = C64(0), attacks = C64(0);
+
+ bit_set(bitboard, square);
+ attacks |= westOne(bitboard) | eastOne(bitboard);
+ attacks |= soutOne(bitboard) | nortOne(bitboard);
+ attacks |= soutOne(bitboard) | nortOne(bitboard);
+ attacks |= soEaOne(bitboard) | noEaOne(bitboard);
+ attacks |= soWeOne(bitboard) | noWeOne(bitboard);
+
+ return attacks;
+}
+
+U64 mask_slide_attacks(Square square, U64 block, const direction_f dir[4],
+ int len[4]) {
+ U64 bitboard = C64(0), attacks = C64(0), tmp;
+ int i, j;
+
+ bit_set(bitboard, square);
+ for (i = 0; i < 4; i++) {
+ for (j = 0, tmp = bitboard; j < len[i]; j++) {
+ attacks |= tmp = (dir[i])(tmp);
+ if (tmp & block)
+ break;
+ }
+ }
+ return attacks;
+}
+
+const direction_f bishop_direction[4] = {noEaOne, noWeOne, soEaOne, soWeOne};
+const direction_f rook_direction[4] = {westOne, soutOne, eastOne, nortOne};
+
+U64 mask_bishop_attacks(Square square) {
+ int tr = square / 8, tf = square % 8;
+ int len[4] = {MIN(7 - tf, 7 - tr) - 1, MIN(tf, 7 - tr) - 1,
+ MIN(7 - tf, tr) - 1, MIN(tf, tr) - 1};
+ return mask_slide_attacks(square, C64(0), bishop_direction, len);
+}
+
+U64 mask_rook_attacks(Square square) {
+ int tr = square / 8, tf = square % 8;
+ int len[4] = {tf - 1, tr - 1, 6 - tf, 6 - tr};
+
+ return mask_slide_attacks(square, C64(0), rook_direction, len);
+}
+
+U64 bishop_attacks_on_the_fly(Square square, U64 block) {
+ int tr = square / 8, tf = square % 8;
+ int len[4] = {MIN(7 - tf, 7 - tr), MIN(tf, 7 - tr), MIN(7 - tf, tr),
+ MIN(tf, tr)};
+
+ return mask_slide_attacks(square, block, bishop_direction, len);
+}
+
+U64 rook_attacks_on_the_fly(Square square, U64 block) {
+ int tr = square / 8, tf = square % 8;
+ int len[4] = {tf, tr, 7 - tf, 7 - tr};
+
+ return mask_slide_attacks(square, block, rook_direction, len);
+}
+
+void init_leapers_attacks(void) {
+ for (Square square = 0; square < 64; square++) {
+ pawn_attacks[WHITE][square] = mask_pawn_attacks(WHITE, square);
+ pawn_attacks[BLACK][square] = mask_pawn_attacks(BLACK, square);
+ knight_attacks[square] = mask_knight_attacks(square);
+ king_attacks[square] = mask_king_attacks(square);
+ }
+}
+
+U64 set_occupancy(int index, int bits_in_mask, U64 attack_mask) {
+ U64 occupancy = C64(0);
+
+ for (int count = 0; count < bits_in_mask; count++) {
+ Square square = bit_lsb_index(attack_mask);
+ bit_pop(attack_mask, square);
+
+ if (index & (1 << count))
+ bit_set(occupancy, square);
+ }
+
+ return occupancy;
+}
+
+void init_sliders_attacks_internal(int bishop) {
+ for (Square square = 0; square < 64; square++) {
+ U64 attack_mask;
+
+ if (bishop) {
+ bishop_masks[square] = mask_bishop_attacks(square);
+ attack_mask = bishop_masks[square];
+ } else {
+ rook_masks[square] = mask_rook_attacks(square);
+ attack_mask = rook_masks[square];
+ }
+
+ int relevant_bits = bit_count(attack_mask);
+ int occupancy_indicies = 1 << relevant_bits;
+
+ for (int index = 0; index < occupancy_indicies; index++) {
+ U64 occupancy = set_occupancy(index, relevant_bits, attack_mask);
+ if (bishop) {
+ int magic_index = (occupancy * bishop_magic_numbers[square]) >>
+ (64 - bishop_relevant_bits[square]);
+ bishop_attacks[square][magic_index] =
+ bishop_attacks_on_the_fly(square, occupancy);
+ } else {
+ int magic_index = hash(occupancy, rook_magic_numbers[square],
+ rook_relevant_bits[square]);
+ rook_attacks[square][magic_index] =
+ rook_attacks_on_the_fly(square, occupancy);
+ }
+ }
+ }
+}
+
+void init_sliders_attacks(void) {
+ init_sliders_attacks_internal(0);
+ init_sliders_attacks_internal(1);
+}
+
+// magic numbers
+
+U64 generate_magic_number() {
+ return get_random_U64_number() & get_random_U64_number() &
+ get_random_U64_number();
+}
+
+U64 find_magic_number(Square square, int relevant_bits, int bishop) {
+ U64 occupancies[4096], attacks[4096], used_attacks[4096];
+ U64 attack_mask =
+ bishop ? mask_bishop_attacks(square) : mask_rook_attacks(square);
+ int occupancy_indicies = 1 << relevant_bits;
+
+ for (int index = 0; index < occupancy_indicies; index++) {
+ occupancies[index] = set_occupancy(index, relevant_bits, attack_mask);
+ attacks[index] = bishop
+ ? bishop_attacks_on_the_fly(square, occupancies[index])
+ : rook_attacks_on_the_fly(square, occupancies[index]);
+ }
+
+ for (int random_count = 0; random_count < 100000000; random_count++) {
+ U64 magic_number = generate_magic_number();
+ if (bit_count((attack_mask * magic_number) & C64(0xFF00000000000000)) < 6)
+ continue;
+
+ memset(used_attacks, C64(0), sizeof(used_attacks));
+ int index, fail;
+
+ for (index = 0, fail = 0; !fail && index < occupancy_indicies; index++) {
+ int magic_index = hash(occupancies[index], magic_number, relevant_bits);
+
+ if (used_attacks[magic_index] == C64(0))
+ used_attacks[magic_index] = attacks[index];
+ else if (used_attacks[magic_index] != attacks[index])
+ fail = 1;
+ }
+
+ if (!fail)
+ return magic_number;
+ }
+
+ return C64(0);
+}
+
+void init_attacks(void) {
+ init_leapers_attacks();
+ init_sliders_attacks();
+}
diff --git a/src/board/CMakeLists.txt b/src/board/CMakeLists.txt
@@ -0,0 +1,7 @@
+add_library(board OBJECT
+ board.c
+)
+
+target_include_directories(board
+ PUBLIC "${PROJECT_SOURCE_DIR}/src/include"
+)
diff --git a/src/board.c b/src/board/board.c
diff --git a/src/engine.c b/src/engine.c
@@ -1,442 +0,0 @@
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "board.h"
-#include "attack.h"
-#include "moves.h"
-#include "perft.h"
-#include "score.h"
-#include "utils.h"
-
-#include <cul/assert.h>
-#include <cul/mem.h>
-
-/* DEBUGGING */
-
-// FEN debug positions
-#define empty_board "8/8/8/8/8/8/8/8 w - - "
-#define start_position \
- "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 "
-#define tricky_position \
- "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1 "
-#define killer_position \
- "rnbqkb1r/pp1p1pPp/8/2p1pP2/1P1P4/3P3P/P1P1P3/RNBQKBNR w KQkq e6 0 1"
-#define cmk_position \
- "r2q1rk1/ppp2ppp/2n1bn2/2b1p3/3pP3/3P1NPP/PPP1NPB1/R1BQ1RK1 b - - 0 9 "
-
-#define MAX_PLY 64
-
-typedef struct Stats_T *Stats_T;
-struct Stats_T {
- long nodes;
- int ply;
- int pv_length[MAX_PLY];
- Move pv_table[MAX_PLY][MAX_PLY];
- Move killer_moves[2][MAX_PLY];
- U32 history_moves[16][64];
-};
-
-Stats_T Stats_new(void) {
- Stats_T p;
- NEW0(p);
- return p;
-}
-
-void Stats_free(Stats_T *p) { FREE(*p); }
-
-int move_score(Stats_T stats, Move move) {
- if (move_capture(move)) {
- return Score_capture(Piece_piece(move_piece(move)),
- Piece_piece(move_piece_capture(move)));
- } else {
- if (!move_cmp(stats->killer_moves[0][stats->ply], move))
- return 9000;
- else if (!move_cmp(stats->killer_moves[1][stats->ply], move))
- return 8000;
- else
- return stats->history_moves[Piece_index(move_piece(move))]
- [move_target(move)];
- }
-
- return 0;
-}
-
-void move_list_sort(Stats_T stats, MoveList list) {
- int score[list->count];
- for (int i = 0; i < list->count; i++)
- score[i] = move_score(stats, list->moves[i]);
-
- for (int i = 0; i < list->count; i++)
- for (int j = i + 1; j < list->count; j++)
- if (score[i] < score[j]) {
- Move t = list->moves[i];
- list->moves[i] = list->moves[j];
- list->moves[j] = t;
-
- int s = score[i];
- score[i] = score[j];
- score[j] = s;
- }
-}
-
-/* SEARCHING */
-
-int evaluate(Board board) {
- Square square;
- eColor side = board_side(board);
- U64 occupancy = board_colorBB(board, side);
-
- int score = 0;
- for (int i = 0; i < 6; i++) {
- U64 bitboard = board_pieceBB(board, i);
- bitboard_for_each_bit(square, bitboard) {
- if (bit_get(occupancy, square)) {
- score += Score_value(i);
- score += Score_position(i, side, square);
- } else {
- score -= Score_value(i);
- score -= Score_position(i, !side, square);
- }
- }
- }
-
- return score;
-}
-
-int quiescence(Stats_T stats, Board board, int alpha, int beta) {
- MoveList moves;
- Board copy;
-
- int eval = evaluate(board);
- stats->nodes++;
-
- if (eval >= beta) {
- return beta;
- }
-
- if (eval > alpha) {
- alpha = eval;
- }
-
- copy = board_new();
- moves = move_list_generate(NULL, board);
- move_list_sort(stats, moves);
-
- for (int i = 0; i < move_list_size(moves); i++) {
- board_copy(board, copy);
-
- if (move_make(move_list_move(moves, i), copy, 1) == 0) continue;
-
- stats->ply++;
- int score = -quiescence(stats, copy, -beta, -alpha);
- stats->ply--;
-
- if (score >= beta) {
- move_list_free(&moves);
- board_free(©);
- return beta;
- }
-
- if (score > alpha) {
- alpha = score;
- }
- }
-
- move_list_free(&moves);
- board_free(©);
- return alpha;
-}
-
-int negamax(Stats_T stats, Board board, int alpha, int beta, int depth) {
- MoveList list;
- Board copy;
- int isCheck = 0;
- int ply = stats->ply;
-
- stats->pv_length[ply] = ply;
-
- if (depth == 0) return quiescence(stats, board, alpha, beta);
-
- if (ply > MAX_PLY - 1) return evaluate(board);
-
- stats->nodes++;
-
- copy = board_new();
- list = move_list_generate(NULL, board);
- isCheck = board_isCheck(board);
-
- if (isCheck) depth++;
-
- int legal_moves = 0;
- move_list_sort(stats, list);
- for (int i = 0; i < move_list_size(list); i++) {
- Move move = move_list_move(list, i);
-
- board_copy(board, copy);
- if (move_make(move, copy, 0) == 0) {
- continue;
- }
-
- stats->ply++;
- int score = -negamax(stats, copy, -beta, -alpha, depth - 1);
- stats->ply--;
- legal_moves++;
-
- if (score >= beta) {
- if (!move_capture(move)) {
- stats->killer_moves[1][ply] = stats->killer_moves[0][ply];
- stats->killer_moves[0][ply] = move;
- }
- move_list_free(&list);
- board_free(©);
- return beta;
- }
-
- if (score > alpha) {
- if (!move_capture(move))
- stats->history_moves[Piece_index(move_piece(move))]
- [move_target(move)] += depth;
- alpha = score;
-
- stats->pv_table[ply][ply] = move;
- for (int i = stats->ply + 1; i < stats->pv_length[ply + 1]; i++)
- stats->pv_table[ply][i] = stats->pv_table[ply + 1][i];
- stats->pv_length[ply] = stats->pv_length[ply + 1];
- }
- }
-
- if (legal_moves == 0) {
- if (isCheck)
- return -49000 + stats->ply;
- else
- return 0;
- }
-
- move_list_free(&list);
- board_free(©);
- return alpha;
-}
-
-void move_print_UCI(Move move) {
- printf("%s%s", square_to_coordinates[move_source(move)],
- square_to_coordinates[move_target(move)]);
- if (move_promote(move)) printf(" %c", Piece_asci(move_piece_promote(move)));
-}
-
-void search_position(Board board, int depth) {
- Stats_T stats = Stats_new();
-
- for (int crnt = 1; crnt <= depth; crnt++) {
- int score = negamax(stats, board, -50000, 50000, crnt);
-
- printf("info score cp %d depth %d nodes %ld pv ", score, crnt,
- stats->nodes);
-
- for (int i = 0; i < stats->pv_length[0]; i++) {
- move_print_UCI(stats->pv_table[0][i]);
- printf(" ");
- }
- printf("\n");
- }
-
- printf("bestmove ");
- move_print_UCI(stats->pv_table[0][0]);
- printf("\n");
-
- Stats_free(&stats);
-}
-
-void print_info(void) {
- printf("id name Stellar\n");
- printf("id author Dimitrije Dobrota\n");
- printf("uciok\n");
-}
-
-typedef struct Instruction_T *Instruction_T;
-struct Instruction_T {
- char *command;
- char *token;
- char *crnt;
-};
-
-char *Instruction_token_next(Instruction_T self);
-
-Instruction_T Instruction_new(char *command) {
- Instruction_T p;
- NEW0(p);
- p->command = ALLOC(strlen(command) + 1);
- p->token = ALLOC(strlen(command) + 1);
- strcpy(p->command, command);
- p->crnt = command;
- Instruction_token_next(p);
- return p;
-}
-
-void Instruction_free(Instruction_T *p) {
- FREE((*p)->command);
- FREE((*p)->token);
- FREE(*p);
-}
-
-char *Instruction_token(Instruction_T self) { return self->token; }
-char *Instruction_token_n(Instruction_T self, int n) {
- while (isspace(*self->crnt) && *self->crnt != '\0')
- self->crnt++;
-
- if (*self->crnt == '\0') {
- *self->token = '\0';
- return NULL;
- }
-
- char *p = self->token;
- while (n--) {
- while (!isspace(*self->crnt) && *self->crnt != '\0' &&
- *self->crnt != ';')
- *p++ = *self->crnt++;
- if (*self->crnt == '\0') {
- p++;
- break;
- }
- self->crnt++;
- *p++ = ' ';
- }
- *--p = '\0';
-
- return self->token;
-}
-
-char *Instruction_token_next(Instruction_T self) {
- return Instruction_token_n(self, 1);
-}
-
-Move parse_move(Board board, char *move_string) {
- Move result = {0};
- MoveList moves;
- Square source, target;
-
- source = coordinates_to_square(move_string);
- target = coordinates_to_square(move_string + 2);
-
- moves = move_list_generate(NULL, board);
- for (int i = 0; i < moves->count; i++) {
- Move move = moves->moves[i];
- if (move_source(move) == source && move_target(move) == target) {
- if (move_string[4]) {
- if (tolower(Piece_code(move_piece_promote(move))) !=
- move_string[4])
- continue;
- }
- result = move;
- break;
- }
- }
-
- move_list_free(&moves);
- return result;
-}
-
-Board Instruction_parse(Instruction_T self, Board board) {
- char *token = Instruction_token(self);
-
- if (!board) board = board_new();
-
- do {
- if (strcmp(token, "ucinewgame") == 0) {
- board = board_fromFEN(board, start_position);
- continue;
- }
-
- if (strcmp(token, "quit") == 0) return NULL;
-
- if (strcmp(token, "position") == 0) {
- token = Instruction_token_next(self);
- if (token && strcmp(token, "startpos") == 0) {
- board = board_fromFEN(board, start_position);
- } else if (token && strcmp(token, "fen") == 0) {
- token = Instruction_token_n(self, 6);
- board = board_fromFEN(board, token);
- } else {
- printf("Unknown argument after position\n");
- }
- // board_print(board);
- continue;
- }
-
- if (strcmp(token, "moves") == 0) {
- while ((token = Instruction_token_next(self))) {
- Move move = parse_move(board, token);
- if (!move_cmp(move, (Move){0})) {
- move_make(move, board, 0);
- } else {
- printf("Invalid move %s!\n", token);
- }
- }
- // board_print(board);
- return board;
- }
-
- if (strcmp(token, "go") == 0) {
- token = Instruction_token_next(self);
- int depth = 5;
- if (token && strcmp(token, "depth") == 0) {
- token = Instruction_token_next(self);
- depth = atoi(token);
- } else {
- printf("Unknown argument after go\n");
- }
- search_position(board, depth);
- continue;
- }
-
- if (strcmp(token, "isready") == 0) {
- printf("readyok\n");
- continue;
- }
-
- if (strcmp(token, "uci") == 0) {
- print_info();
- continue;
- }
-
- } while ((token = Instruction_token_next(self)));
-
- return board;
-}
-
-void uci_loop(void) {
- Board board = NULL;
- Instruction_T instruction;
- char input[200000];
-
- setbuf(stdin, NULL);
- setbuf(stdout, NULL);
-
- print_info();
- while (1) {
- memset(input, 0, sizeof(input));
- fflush(stdout);
- if (!fgets(input, sizeof(input), stdin)) continue;
-
- instruction = Instruction_new(input);
- if (!(board = Instruction_parse(instruction, board))) break;
- Instruction_free(&instruction);
- }
-
- Instruction_free(&instruction);
- board_free(&board);
-}
-
-/* MAIN */
-
-void init_all() {
- init_leapers_attacks();
- init_sliders_attacks();
-}
-
-int main(void) {
- init_all();
- uci_loop();
- return 0;
-}
diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt
@@ -0,0 +1,17 @@
+add_executable(engine engine.c)
+
+target_link_libraries(engine
+ PRIVATE attack
+ PRIVATE board
+ PRIVATE moves
+ PRIVATE score
+ PRIVATE utils
+)
+
+target_link_libraries(engine PRIVATE "cul")
+
+set_target_properties(engine PROPERTIES
+ VERSION ${PROJECT_VERSION}
+ SOVERSION ${PROJECT_VERSION_MAJOR}
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
+)
diff --git a/src/engine/engine.c b/src/engine/engine.c
@@ -0,0 +1,425 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "attack.h"
+#include "board.h"
+#include "moves.h"
+#include "perft.h"
+#include "score.h"
+#include "utils.h"
+
+#include <cul/assert.h>
+#include <cul/mem.h>
+
+
+#define MAX_PLY 64
+
+typedef struct Stats_T *Stats_T;
+struct Stats_T {
+ long nodes;
+ int ply;
+ int pv_length[MAX_PLY];
+ Move pv_table[MAX_PLY][MAX_PLY];
+ Move killer_moves[2][MAX_PLY];
+ U32 history_moves[16][64];
+};
+
+Stats_T Stats_new(void) {
+ Stats_T p;
+ NEW0(p);
+ return p;
+}
+
+void Stats_free(Stats_T *p) { FREE(*p); }
+
+int move_score(Stats_T stats, Move move) {
+ if (move_capture(move)) {
+ return Score_capture(Piece_piece(move_piece(move)),
+ Piece_piece(move_piece_capture(move)));
+ } else {
+ if (!move_cmp(stats->killer_moves[0][stats->ply], move))
+ return 9000;
+ else if (!move_cmp(stats->killer_moves[1][stats->ply], move))
+ return 8000;
+ else
+ return stats->history_moves[Piece_index(move_piece(move))]
+ [move_target(move)];
+ }
+
+ return 0;
+}
+
+void move_list_sort(Stats_T stats, MoveList list) {
+ int score[list->count];
+ for (int i = 0; i < list->count; i++)
+ score[i] = move_score(stats, list->moves[i]);
+
+ for (int i = 0; i < list->count; i++)
+ for (int j = i + 1; j < list->count; j++)
+ if (score[i] < score[j]) {
+ Move t = list->moves[i];
+ list->moves[i] = list->moves[j];
+ list->moves[j] = t;
+
+ int s = score[i];
+ score[i] = score[j];
+ score[j] = s;
+ }
+}
+
+/* SEARCHING */
+
+int evaluate(Board board) {
+ Square square;
+ eColor side = board_side(board);
+ U64 occupancy = board_colorBB(board, side);
+
+ int score = 0;
+ for (int i = 0; i < 6; i++) {
+ U64 bitboard = board_pieceBB(board, i);
+ bitboard_for_each_bit(square, bitboard) {
+ if (bit_get(occupancy, square)) {
+ score += Score_value(i);
+ score += Score_position(i, side, square);
+ } else {
+ score -= Score_value(i);
+ score -= Score_position(i, !side, square);
+ }
+ }
+ }
+
+ return score;
+}
+
+int quiescence(Stats_T stats, Board board, int alpha, int beta) {
+ MoveList moves;
+ Board copy;
+
+ int eval = evaluate(board);
+ stats->nodes++;
+
+ if (eval >= beta) {
+ return beta;
+ }
+
+ if (eval > alpha) {
+ alpha = eval;
+ }
+
+ copy = board_new();
+ moves = move_list_generate(NULL, board);
+ move_list_sort(stats, moves);
+
+ for (int i = 0; i < move_list_size(moves); i++) {
+ board_copy(board, copy);
+
+ if (move_make(move_list_move(moves, i), copy, 1) == 0) continue;
+
+ stats->ply++;
+ int score = -quiescence(stats, copy, -beta, -alpha);
+ stats->ply--;
+
+ if (score >= beta) {
+ move_list_free(&moves);
+ board_free(©);
+ return beta;
+ }
+
+ if (score > alpha) {
+ alpha = score;
+ }
+ }
+
+ move_list_free(&moves);
+ board_free(©);
+ return alpha;
+}
+
+int negamax(Stats_T stats, Board board, int alpha, int beta, int depth) {
+ MoveList list;
+ Board copy;
+ int isCheck = 0;
+ int ply = stats->ply;
+
+ stats->pv_length[ply] = ply;
+
+ if (depth == 0) return quiescence(stats, board, alpha, beta);
+
+ if (ply > MAX_PLY - 1) return evaluate(board);
+
+ stats->nodes++;
+
+ copy = board_new();
+ list = move_list_generate(NULL, board);
+ isCheck = board_isCheck(board);
+
+ if (isCheck) depth++;
+
+ int legal_moves = 0;
+ move_list_sort(stats, list);
+ for (int i = 0; i < move_list_size(list); i++) {
+ Move move = move_list_move(list, i);
+
+ board_copy(board, copy);
+ if (move_make(move, copy, 0) == 0) {
+ continue;
+ }
+
+ stats->ply++;
+ int score = -negamax(stats, copy, -beta, -alpha, depth - 1);
+ stats->ply--;
+ legal_moves++;
+
+ if (score >= beta) {
+ if (!move_capture(move)) {
+ stats->killer_moves[1][ply] = stats->killer_moves[0][ply];
+ stats->killer_moves[0][ply] = move;
+ }
+ move_list_free(&list);
+ board_free(©);
+ return beta;
+ }
+
+ if (score > alpha) {
+ if (!move_capture(move))
+ stats->history_moves[Piece_index(move_piece(move))]
+ [move_target(move)] += depth;
+ alpha = score;
+
+ stats->pv_table[ply][ply] = move;
+ for (int i = stats->ply + 1; i < stats->pv_length[ply + 1]; i++)
+ stats->pv_table[ply][i] = stats->pv_table[ply + 1][i];
+ stats->pv_length[ply] = stats->pv_length[ply + 1];
+ }
+ }
+
+ if (legal_moves == 0) {
+ if (isCheck)
+ return -49000 + stats->ply;
+ else
+ return 0;
+ }
+
+ move_list_free(&list);
+ board_free(©);
+ return alpha;
+}
+
+void move_print_UCI(Move move) {
+ printf("%s%s", square_to_coordinates[move_source(move)],
+ square_to_coordinates[move_target(move)]);
+ if (move_promote(move)) printf(" %c", Piece_asci(move_piece_promote(move)));
+}
+
+void search_position(Board board, int depth) {
+ Stats_T stats = Stats_new();
+
+ for (int crnt = 1; crnt <= depth; crnt++) {
+ int score = negamax(stats, board, -50000, 50000, crnt);
+
+ printf("info score cp %d depth %d nodes %ld pv ", score, crnt,
+ stats->nodes);
+
+ for (int i = 0; i < stats->pv_length[0]; i++) {
+ move_print_UCI(stats->pv_table[0][i]);
+ printf(" ");
+ }
+ printf("\n");
+ }
+
+ printf("bestmove ");
+ move_print_UCI(stats->pv_table[0][0]);
+ printf("\n");
+
+ Stats_free(&stats);
+}
+
+void print_info(void) {
+ printf("id name Stellar\n");
+ printf("id author Dimitrije Dobrota\n");
+ printf("uciok\n");
+}
+
+typedef struct Instruction_T *Instruction_T;
+struct Instruction_T {
+ char *command;
+ char *token;
+ char *crnt;
+};
+
+char *Instruction_token_next(Instruction_T self);
+
+Instruction_T Instruction_new(char *command) {
+ Instruction_T p;
+ NEW0(p);
+ p->command = ALLOC(strlen(command) + 1);
+ p->token = ALLOC(strlen(command) + 1);
+ strcpy(p->command, command);
+ p->crnt = command;
+ Instruction_token_next(p);
+ return p;
+}
+
+void Instruction_free(Instruction_T *p) {
+ FREE((*p)->command);
+ FREE((*p)->token);
+ FREE(*p);
+}
+
+char *Instruction_token(Instruction_T self) { return self->token; }
+char *Instruction_token_n(Instruction_T self, int n) {
+ while (isspace(*self->crnt) && *self->crnt != '\0')
+ self->crnt++;
+
+ if (*self->crnt == '\0') {
+ *self->token = '\0';
+ return NULL;
+ }
+
+ char *p = self->token;
+ while (n--) {
+ while (!isspace(*self->crnt) && *self->crnt != '\0' &&
+ *self->crnt != ';')
+ *p++ = *self->crnt++;
+ if (*self->crnt == '\0') {
+ p++;
+ break;
+ }
+ self->crnt++;
+ *p++ = ' ';
+ }
+ *--p = '\0';
+
+ return self->token;
+}
+
+char *Instruction_token_next(Instruction_T self) {
+ return Instruction_token_n(self, 1);
+}
+
+Move parse_move(Board board, char *move_string) {
+ Move result = {0};
+ MoveList moves;
+ Square source, target;
+
+ source = coordinates_to_square(move_string);
+ target = coordinates_to_square(move_string + 2);
+
+ moves = move_list_generate(NULL, board);
+ for (int i = 0; i < moves->count; i++) {
+ Move move = moves->moves[i];
+ if (move_source(move) == source && move_target(move) == target) {
+ if (move_string[4]) {
+ if (tolower(Piece_code(move_piece_promote(move))) !=
+ move_string[4])
+ continue;
+ }
+ result = move;
+ break;
+ }
+ }
+
+ move_list_free(&moves);
+ return result;
+}
+
+Board Instruction_parse(Instruction_T self, Board board) {
+ char *token = Instruction_token(self);
+
+ if (!board) board = board_new();
+
+ do {
+ if (strcmp(token, "ucinewgame") == 0) {
+ board = board_fromFEN(board, start_position);
+ continue;
+ }
+
+ if (strcmp(token, "quit") == 0) return NULL;
+
+ if (strcmp(token, "position") == 0) {
+ token = Instruction_token_next(self);
+ if (token && strcmp(token, "startpos") == 0) {
+ board = board_fromFEN(board, start_position);
+ } else if (token && strcmp(token, "fen") == 0) {
+ token = Instruction_token_n(self, 6);
+ board = board_fromFEN(board, token);
+ } else {
+ printf("Unknown argument after position\n");
+ }
+ // board_print(board);
+ continue;
+ }
+
+ if (strcmp(token, "moves") == 0) {
+ while ((token = Instruction_token_next(self))) {
+ Move move = parse_move(board, token);
+ if (!move_cmp(move, (Move){0})) {
+ move_make(move, board, 0);
+ } else {
+ printf("Invalid move %s!\n", token);
+ }
+ }
+ // board_print(board);
+ return board;
+ }
+
+ if (strcmp(token, "go") == 0) {
+ token = Instruction_token_next(self);
+ int depth = 5;
+ if (token && strcmp(token, "depth") == 0) {
+ token = Instruction_token_next(self);
+ depth = atoi(token);
+ } else {
+ printf("Unknown argument after go\n");
+ }
+ search_position(board, depth);
+ continue;
+ }
+
+ if (strcmp(token, "isready") == 0) {
+ printf("readyok\n");
+ continue;
+ }
+
+ if (strcmp(token, "uci") == 0) {
+ print_info();
+ continue;
+ }
+
+ } while ((token = Instruction_token_next(self)));
+
+ return board;
+}
+
+void uci_loop(void) {
+ Board board = NULL;
+ Instruction_T instruction;
+ char input[200000];
+
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ print_info();
+ while (1) {
+ memset(input, 0, sizeof(input));
+ fflush(stdout);
+ if (!fgets(input, sizeof(input), stdin)) continue;
+
+ instruction = Instruction_new(input);
+ if (!(board = Instruction_parse(instruction, board))) break;
+ Instruction_free(&instruction);
+ }
+
+ Instruction_free(&instruction);
+ board_free(&board);
+}
+
+/* MAIN */
+
+int main(void) {
+ init_attacks();
+ uci_loop();
+ return 0;
+}
diff --git a/src/include/attack.h b/src/include/attack.h
@@ -0,0 +1,18 @@
+#ifndef ATTACK_H
+#define ATTACK_H
+
+#include "utils.h"
+
+void init_attacks(void);
+void init_leapers_attacks(void);
+void init_sliders_attacks(void);
+
+U64 get_wpawn_attacks(Square square, U64 occupancy);
+U64 get_bpawn_attacks(Square square, U64 occupancy);
+U64 get_knight_attacks(Square square, U64 occupancy);
+U64 get_king_attacks(Square square, U64 occupancy);
+U64 get_bishop_attacks(Square square, U64 occupancy);
+U64 get_rook_attacks(Square square, U64 occupancy);
+U64 get_queen_attacks(Square square, U64 occupancy);
+
+#endif
diff --git a/include/board.h b/src/include/board.h
diff --git a/include/moves.h b/src/include/moves.h
diff --git a/include/perft.h b/src/include/perft.h
diff --git a/include/score.h b/src/include/score.h
diff --git a/src/include/utils.h b/src/include/utils.h
@@ -0,0 +1,90 @@
+#ifndef __UTILS_H
+#define __UTILS_H
+
+// useful macros
+#define MAX(a, b) ((a > b) ? a : b)
+#define MIN(a, b) ((a < b) ? a : b)
+
+// define number types
+typedef unsigned long long U64; // define bitboard data type
+#define C64(constantU64) constantU64##ULL // define shorthand for constants
+
+typedef unsigned int U32;
+#define C32(constantU32) constantU32##U
+
+// useful bit patterns
+extern const U64 universe;
+extern const U64 notAFile;
+extern const U64 notHFile;
+
+// useful bit functions
+#define bit_get(bitboard, square) (((bitboard) >> (square)) & C64(1))
+#define bit_set(bitboard, square) ((bitboard) |= C64(1) << (square))
+#define bit_pop(bitboard, square) ((bitboard) &= ~(C64(1) << (square)))
+int bit_count(U64 bitboard);
+int bit_lsb_index(U64 bitboard);
+
+#define bitboard_for_each_bit(var, bb) \
+ for (var = bit_lsb_index(bb); bb; bit_pop(bb, var), var = bit_lsb_index(bb))
+
+void bitboard_print(U64 bitboard);
+
+// squares
+// clang-format off
+enum enumSquare {
+ a1, b1, c1, d1, e1, f1, g1, h1,
+ a2, b2, c2, d2, e2, f2, g2, h2,
+ a3, b3, c3, d3, e3, f3, g3, h3,
+ a4, b4, c4, d4, e4, f4, g4, h4,
+ a5, b5, c5, d5, e5, f5, g5, h5,
+ a6, b6, c6, d6, e6, f6, g6, h6,
+ a7, b7, c7, d7, e7, f7, g7, h7,
+ a8, b8, c8, d8, e8, f8, g8, h8, no_sq
+};
+// clang-format on
+typedef enum enumSquare Square;
+
+extern const char *square_to_coordinates[];
+Square coordinates_to_square(char *cord);
+
+// board moving
+typedef U64 (*direction_f)(U64);
+U64 soutOne(U64 b);
+U64 nortOne(U64 b);
+U64 eastOne(U64 b);
+U64 westOne(U64 b);
+U64 soEaOne(U64 b);
+U64 soWeOne(U64 b);
+U64 noEaOne(U64 b);
+U64 noWeOne(U64 b);
+
+// board rotation
+U64 rotateLeft(U64 x, int s);
+U64 rotateRight(U64 x, int s);
+
+// enum types for color and piece type
+enum enumColor {
+ WHITE = 0,
+ BLACK
+};
+enum enumPiece {
+ PAWN = 0,
+ KNIGHT,
+ BISHOP,
+ ROOK,
+ QUEEN,
+ KING
+};
+
+typedef enum enumColor eColor;
+typedef enum enumPiece ePiece;
+
+int get_time_ms(void);
+
+typedef U64 (*attack_f)(Square square, U64 occupancy);
+
+#define empty_board "8/8/8/8/8/8/8/8 w - - "
+#define start_position \
+ "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 "
+
+#endif
diff --git a/src/moves/CMakeLists.txt b/src/moves/CMakeLists.txt
@@ -0,0 +1,7 @@
+add_library(moves OBJECT
+ moves.c
+)
+
+target_include_directories(moves
+ PUBLIC "${PROJECT_SOURCE_DIR}/src/include"
+)
diff --git a/src/moves.c b/src/moves/moves.c
diff --git a/src/perft.c b/src/perft.c
@@ -1,127 +0,0 @@
-#include <pthread.h>
-#include <semaphore.h>
-#include <stdio.h>
-
-#include "moves.h"
-#include "perft.h"
-
-struct MoveList moveList[10];
-long nodes;
-
-void perft_driver(Board board, struct MoveList *moveList, int depth,
- unsigned long long *nodes) {
- if (depth == 0) {
- (*nodes)++;
- return;
- }
-
- MoveList list = move_list_generate(&moveList[depth], board);
- Board copy = board_new();
-
- for (int i = 0; i < move_list_size(list); i++) {
- board_copy(board, copy);
- if (!move_make(move_list_move(list, i), copy, 0)) continue;
- perft_driver(copy, moveList, depth - 1, nodes);
- }
-
- move_list_reset(list);
- board_free(©);
-}
-
-void perft_test(Board board, int depth) {
- MoveList list = move_list_generate(&moveList[depth], board);
- Board copy = board_new();
- long start = get_time_ms();
-
- printf("\n Performance test\n\n");
-
- nodes = 0;
- for (int i = 0; i < move_list_size(list); i++) {
- board_copy(board, copy);
- Move move = move_list_move(list, i);
- if (!move_make(move_list_move(list, i), copy, 0)) continue;
- unsigned long long node = 0;
- perft_driver(copy, moveList, depth - 1, &node);
- printf("%s%s: %llu\n", square_to_coordinates[move_source(move)],
- square_to_coordinates[move_target(move)], node);
- nodes += node;
- }
- move_list_reset(list);
- board_free(©);
-
- printf("\nNodes searched: %ld\n\n", nodes);
- printf("\n Depth: %d\n", depth);
- printf(" Nodes: %ld\n", nodes);
- printf(" Time: %ld\n\n", get_time_ms() - start);
-}
-
-typedef struct perf_shared perf_shared;
-struct perf_shared {
- Board board;
- MoveList list;
- int depth;
- sem_t *mutex;
- int *index;
- sem_t *finish;
- unsigned long long *total;
-};
-
-void *perft_thread(void *arg) {
- perf_shared *shared = (perf_shared *)arg;
- Board board = board_new();
- unsigned long long node_count = 0;
-
- struct MoveList moveList[10];
-
- while (1) {
- sem_wait(shared->mutex);
- *shared->total += node_count;
- if (*shared->index >= move_list_size(shared->list)) {
- sem_post(shared->mutex);
- break;
- }
- Move move = move_list_move(shared->list, (*shared->index)++);
- sem_post(shared->mutex);
-
- board_copy(shared->board, board);
- if (!move_make(move, board, 0)) continue;
-
- node_count = 0;
- perft_driver(board, moveList, shared->depth, &node_count);
- printf("%s%s: %llu\n", square_to_coordinates[move_source(move)],
- square_to_coordinates[move_target(move)], node_count);
- }
- board_free(&board);
- sem_post(shared->finish);
- return NULL;
-}
-
-void perft_test_threaded(Board board, int depth) {
- MoveList list = move_list_generate(NULL, board);
- int size = 8;
-
- unsigned long long total = 0;
- perf_shared shared[size];
- pthread_t threads[size];
- sem_t mutex, finish;
-
- int index = 0;
- sem_init(&mutex, 0, 1);
- sem_init(&finish, 0, 0);
- for (int i = 0; i < size; i++) {
- shared[i].board = board;
- shared[i].list = list;
- shared[i].depth = depth - 1;
- shared[i].mutex = &mutex;
- shared[i].index = &index;
- shared[i].finish = &finish;
- shared[i].total = &total;
- pthread_create(threads + i, NULL, perft_thread, (void *)(shared + i));
- }
-
- for (int i = 0; i < size; i++)
- sem_wait(&finish);
- move_list_free(&list);
-
- printf("Nodes processed: %llu\n", total);
-}
diff --git a/src/perft/CMakeLists.txt b/src/perft/CMakeLists.txt
@@ -0,0 +1,17 @@
+add_executable(perft perft.c)
+
+target_link_libraries(perft
+ PRIVATE attack
+ PRIVATE board
+ PRIVATE moves
+ PRIVATE score
+ PRIVATE utils
+)
+
+target_link_libraries(perft PRIVATE "cul")
+
+set_target_properties(perft PROPERTIES
+ VERSION ${PROJECT_VERSION}
+ SOVERSION ${PROJECT_VERSION_MAJOR}
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
+)
diff --git a/src/perft/perft.c b/src/perft/perft.c
@@ -0,0 +1,145 @@
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdio.h>
+
+#include "attack.h"
+#include "board.h"
+#include "moves.h"
+#include "perft.h"
+#include "utils.h"
+
+struct MoveList moveList[10];
+long nodes;
+
+void perft_driver(Board board, struct MoveList *moveList, int depth,
+ unsigned long long *nodes) {
+ if (depth == 0) {
+ (*nodes)++;
+ return;
+ }
+
+ MoveList list = move_list_generate(&moveList[depth], board);
+ Board copy = board_new();
+
+ for (int i = 0; i < move_list_size(list); i++) {
+ board_copy(board, copy);
+ if (!move_make(move_list_move(list, i), copy, 0)) continue;
+ perft_driver(copy, moveList, depth - 1, nodes);
+ }
+
+ move_list_reset(list);
+ board_free(©);
+}
+
+void perft_test(Board board, int depth) {
+ MoveList list = move_list_generate(&moveList[depth], board);
+ Board copy = board_new();
+ long start = get_time_ms();
+
+ printf("\n Performance test\n\n");
+
+ nodes = 0;
+ for (int i = 0; i < move_list_size(list); i++) {
+ board_copy(board, copy);
+ Move move = move_list_move(list, i);
+ if (!move_make(move_list_move(list, i), copy, 0)) continue;
+ unsigned long long node = 0;
+ perft_driver(copy, moveList, depth - 1, &node);
+ printf("%s%s: %llu\n", square_to_coordinates[move_source(move)],
+ square_to_coordinates[move_target(move)], node);
+ nodes += node;
+ }
+ move_list_reset(list);
+ board_free(©);
+
+ printf("\nNodes searched: %ld\n\n", nodes);
+ printf("\n Depth: %d\n", depth);
+ printf(" Nodes: %ld\n", nodes);
+ printf(" Time: %ld\n\n", get_time_ms() - start);
+}
+
+typedef struct perf_shared perf_shared;
+struct perf_shared {
+ Board board;
+ MoveList list;
+ int depth;
+ sem_t *mutex;
+ int *index;
+ sem_t *finish;
+ unsigned long long *total;
+};
+
+void *perft_thread(void *arg) {
+ perf_shared *shared = (perf_shared *)arg;
+ Board board = board_new();
+ unsigned long long node_count = 0;
+
+ struct MoveList moveList[10];
+
+ while (1) {
+ sem_wait(shared->mutex);
+ *shared->total += node_count;
+ if (*shared->index >= move_list_size(shared->list)) {
+ sem_post(shared->mutex);
+ break;
+ }
+ Move move = move_list_move(shared->list, (*shared->index)++);
+ sem_post(shared->mutex);
+
+ board_copy(shared->board, board);
+ if (!move_make(move, board, 0)) continue;
+
+ node_count = 0;
+ perft_driver(board, moveList, shared->depth, &node_count);
+ printf("%s%s: %llu\n", square_to_coordinates[move_source(move)],
+ square_to_coordinates[move_target(move)], node_count);
+ }
+ board_free(&board);
+ sem_post(shared->finish);
+ return NULL;
+}
+
+void perft_test_threaded(Board board, int depth) {
+ MoveList list = move_list_generate(NULL, board);
+ int size = 8;
+
+ unsigned long long total = 0;
+ perf_shared shared[size];
+ pthread_t threads[size];
+ sem_t mutex, finish;
+
+ int index = 0;
+ sem_init(&mutex, 0, 1);
+ sem_init(&finish, 0, 0);
+ for (int i = 0; i < size; i++) {
+ shared[i].board = board;
+ shared[i].list = list;
+ shared[i].depth = depth - 1;
+ shared[i].mutex = &mutex;
+ shared[i].index = &index;
+ shared[i].finish = &finish;
+ shared[i].total = &total;
+ pthread_create(threads + i, NULL, perft_thread, (void *)(shared + i));
+ }
+
+ for (int i = 0; i < size; i++)
+ sem_wait(&finish);
+ move_list_free(&list);
+
+ printf("Nodes processed: %llu\n", total);
+}
+
+// FEN debug positions
+#define tricky_position \
+ "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1 "
+#define killer_position \
+ "rnbqkb1r/pp1p1pPp/8/2p1pP2/1P1P4/3P3P/P1P1P3/RNBQKBNR w KQkq e6 0 1"
+#define cmk_position \
+ "r2q1rk1/ppp2ppp/2n1bn2/2b1p3/3pP3/3P1NPP/PPP1NPB1/R1BQ1RK1 b - - 0 9 "
+
+int main(void) {
+ init_attacks();
+ Board board = board_new();
+ board_fromFEN(board, tricky_position);
+ perft_test_threaded(board, 5);
+}
diff --git a/src/score/CMakeLists.txt b/src/score/CMakeLists.txt
@@ -0,0 +1,7 @@
+add_library(score OBJECT
+ score.c
+)
+
+target_include_directories(score
+ PUBLIC "${PROJECT_SOURCE_DIR}/src/include"
+)
diff --git a/src/score.c b/src/score/score.c
diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt
@@ -0,0 +1,7 @@
+add_library(utils OBJECT
+ utils.c
+)
+
+target_include_directories(utils
+ PUBLIC "${PROJECT_SOURCE_DIR}/src/include"
+)
diff --git a/src/utils.c b/src/utils/utils.c