Why C++ instead of C#?

Discussion of chess software programming and technical issues.

Moderator: Ras

R. Tomasi
Posts: 307
Joined: Wed Sep 01, 2021 4:08 pm
Location: Germany
Full name: Roland Tomasi

Re: Why C++ instead of C#?

Post by R. Tomasi »

klx wrote: Wed Sep 29, 2021 4:29 am
R. Tomasi wrote: Wed Sep 29, 2021 12:33 am I modified the C source code to reflect the changes of the latest C# version (early outs in Illegal).
Does that actually help though? Only 4% of moves are illegal, so you almost always have to do all calculations anyway, and now you have to do extra work to check for early exit.

I tried it early on, and found that it led to a slight drop in performance (except for one of the positions). I see the same with the version you posted.
Tbh, I didn't test. But you're right, it actually hurts performance:

No early outs:

Code: Select all

Total: 1361558651 Nodes, 26199 ms, 51969K NPS
Total: 1361558651 Nodes, 26201 ms, 51965K NPS
Total: 1361558651 Nodes, 26238 ms, 51892K NPS
Early outs:

Code: Select all

Total: 1361558651 Nodes, 27586 ms, 49356K NPS
Total: 1361558651 Nodes, 27585 ms, 49358K NPS
Total: 1361558651 Nodes, 27560 ms, 49403K NPS
Edit: C source without the early outs patch:

Code: Select all

/*
 This perft implementation is based on QBBEngine by Fabio Gobbato

 Some parts of this source call gcc intrinsic functions. If you are not using gcc you need to
 change them with the functions of your compiler.
*/

#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>
#include <assert.h>

#define WHITE 0
#define BLACK 8

/* define the piece type: empty, pawn, knight, bishop, rook, queen, king */
typedef enum { EMPTY, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING } TPieceType;

/* define the move type, for example
   KING|CASTLE is a castle move
   PAWN|CAPTURE|EP is an enpassant move
   PAWN|PROMO|CAPTURE is a promotion with a capture */
typedef enum { CASTLE = 0x40, PROMO = 0x20, EP = 0x10, CAPTURE = 0x08 } TMoveType;

/* bitboard types */
typedef uint64_t TBB;

/* move structure */
typedef union
{
    struct {
        uint8_t MoveType;
        uint8_t From;
        uint8_t To;
        uint8_t Prom;
    };
    unsigned int Move;
}TMove;

/* structure for a capture, it needs the Eval field for MVV/LVA ordering */
typedef struct
{
    TMove Move;
    //int Eval;
} TMoveEval;

/*
Board structure definition

PM,P0,P1,P2 are the 4 bitboards that contain the whole board
PM is the bitboard with the side to move pieces
P0,P1 and P2: with these bitboards you can obtain every type of pieces and every pieces combinations.
*/
typedef struct
{
    TBB PM;
    TBB P0;
    TBB P1;
    TBB P2;
    uint8_t CastleFlags; /* ..sl..SL  short long opponent SHORT LONG side to move */
    uint8_t EnPassant; /* enpassant column, =8 if not set */
    uint8_t STM; /* side to move */
} TBoard;

/*
Into Game are saved all the positions from the last 50 move counter reset
Position is the pointer to the last position of the game
*/
TBoard Game[512];
TBoard* Position;

/* array of bitboards that contains all the knight destination for every square */
const TBB KnightDest[64] = { 0x0000000000020400ULL,0x0000000000050800ULL,0x00000000000a1100ULL,0x0000000000142200ULL,
                           0x0000000000284400ULL,0x0000000000508800ULL,0x0000000000a01000ULL,0x0000000000402000ULL,
                           0x0000000002040004ULL,0x0000000005080008ULL,0x000000000a110011ULL,0x0000000014220022ULL,
                           0x0000000028440044ULL,0x0000000050880088ULL,0x00000000a0100010ULL,0x0000000040200020ULL,
                           0x0000000204000402ULL,0x0000000508000805ULL,0x0000000a1100110aULL,0x0000001422002214ULL,
                           0x0000002844004428ULL,0x0000005088008850ULL,0x000000a0100010a0ULL,0x0000004020002040ULL,
                           0x0000020400040200ULL,0x0000050800080500ULL,0x00000a1100110a00ULL,0x0000142200221400ULL,
                           0x0000284400442800ULL,0x0000508800885000ULL,0x0000a0100010a000ULL,0x0000402000204000ULL,
                           0x0002040004020000ULL,0x0005080008050000ULL,0x000a1100110a0000ULL,0x0014220022140000ULL,
                           0x0028440044280000ULL,0x0050880088500000ULL,0x00a0100010a00000ULL,0x0040200020400000ULL,
                           0x0204000402000000ULL,0x0508000805000000ULL,0x0a1100110a000000ULL,0x1422002214000000ULL,
                           0x2844004428000000ULL,0x5088008850000000ULL,0xa0100010a0000000ULL,0x4020002040000000ULL,
                           0x0400040200000000ULL,0x0800080500000000ULL,0x1100110a00000000ULL,0x2200221400000000ULL,
                           0x4400442800000000ULL,0x8800885000000000ULL,0x100010a000000000ULL,0x2000204000000000ULL,
                           0x0004020000000000ULL,0x0008050000000000ULL,0x00110a0000000000ULL,0x0022140000000000ULL,
                           0x0044280000000000ULL,0x0088500000000000ULL,0x0010a00000000000ULL,0x0020400000000000ULL };
/* The same for the king */
const TBB KingDest[64] = { 0x0000000000000302ULL,0x0000000000000705ULL,0x0000000000000e0aULL,0x0000000000001c14ULL,
                          0x0000000000003828ULL,0x0000000000007050ULL,0x000000000000e0a0ULL,0x000000000000c040ULL,
                          0x0000000000030203ULL,0x0000000000070507ULL,0x00000000000e0a0eULL,0x00000000001c141cULL,
                          0x0000000000382838ULL,0x0000000000705070ULL,0x0000000000e0a0e0ULL,0x0000000000c040c0ULL,
                          0x0000000003020300ULL,0x0000000007050700ULL,0x000000000e0a0e00ULL,0x000000001c141c00ULL,
                          0x0000000038283800ULL,0x0000000070507000ULL,0x00000000e0a0e000ULL,0x00000000c040c000ULL,
                          0x0000000302030000ULL,0x0000000705070000ULL,0x0000000e0a0e0000ULL,0x0000001c141c0000ULL,
                          0x0000003828380000ULL,0x0000007050700000ULL,0x000000e0a0e00000ULL,0x000000c040c00000ULL,
                          0x0000030203000000ULL,0x0000070507000000ULL,0x00000e0a0e000000ULL,0x00001c141c000000ULL,
                          0x0000382838000000ULL,0x0000705070000000ULL,0x0000e0a0e0000000ULL,0x0000c040c0000000ULL,
                          0x0003020300000000ULL,0x0007050700000000ULL,0x000e0a0e00000000ULL,0x001c141c00000000ULL,
                          0x0038283800000000ULL,0x0070507000000000ULL,0x00e0a0e000000000ULL,0x00c040c000000000ULL,
                          0x0302030000000000ULL,0x0705070000000000ULL,0x0e0a0e0000000000ULL,0x1c141c0000000000ULL,
                          0x3828380000000000ULL,0x7050700000000000ULL,0xe0a0e00000000000ULL,0xc040c00000000000ULL,
                          0x0203000000000000ULL,0x0507000000000000ULL,0x0a0e000000000000ULL,0x141c000000000000ULL,
                          0x2838000000000000ULL,0x5070000000000000ULL,0xa0e0000000000000ULL,0x40c0000000000000ULL };

/* masks for finding the pawns that can capture with an enpassant (in move generation) */
const TBB EnPassant[8] = {
0x0000000200000000ULL,0x0000000500000000ULL,0x0000000A00000000ULL,0x0000001400000000ULL,
0x0000002800000000ULL,0x0000005000000000ULL,0x000000A000000000ULL,0x0000004000000000ULL
};

/* masks for finding the pawns that can capture with an enpassant (in make move) */
const TBB EnPassantM[8] = {
0x0000000002000000ULL,0x0000000005000000ULL,0x000000000A000000ULL,0x0000000014000000ULL,
0x0000000028000000ULL,0x0000000050000000ULL,0x00000000A0000000ULL,0x0000000040000000ULL
};

/*
reverse a bitboard:
A bitboard is an array of byte: Byte0,Byte1,Byte2,Byte3,Byte4,Byte5,Byte6,Byte7
after this function the bitboard will be: Byte7,Byte6,Byte5,Byte4,Byte3,Byte2,Byte1,Byte0

The board is saved always with the side to move in the low significant bits of the bitboard, so this function
is used to change the side to move
*/
#if defined(_MSC_VER)&&!defined(__clang__)

#include <intrin.h>

#define RevBB(bb) (_byteswap_uint64(bb))

unsigned long __inline MSB(unsigned __int64 value)
{
    unsigned long leading_zero = 0;

    if (_BitScanReverse64(&leading_zero, value))
    {
        return 0x3F ^ (63 - leading_zero);
    }
    else
    {
        return 0x3F ^ 64;
    }
}

unsigned long __inline LSB(unsigned __int64 value)
{
    unsigned long trailing_zero = 0;

    if (_BitScanForward64(&trailing_zero, value))
    {
        return trailing_zero;
    }
    else
    {
        return 64;
    }
}

#define PopCount(bb) (__popcnt64(bb))

#define ASSUME(cond) __assume(cond)


#else
#define RevBB(bb) (__builtin_bswap64(bb))
/* return the index of the most significant bit of the bitboard, bb must always be !=0 */
#define MSB(bb) (0x3F ^ __builtin_clzll(bb))
/* return the index of the least significant bit of the bitboard, bb must always be !=0 */
#define LSB(bb) (__builtin_ctzll(bb))
/* return the number of bits sets of a bitboard */
#define PopCount(bb) (__builtin_popcountll(bb))

#define ASSUME(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)

#endif
/* extract the least significant bit of the bitboard */
#define ExtractLSB(bb) ((bb)&(-(signed long long)(bb)))
/* reset the least significant bit of bb */
#define ClearLSB(bb) ((bb)&((bb)-1ll))

/* Macro to check and reset the castle rights:
   CastleSM: short castling side to move
   CastleLM: long castling side to move
   CastleSO: short castling opponent
   CastleLO: long castling opponent
 */
#define CastleSM (Position->CastleFlags&0x02)
#define CastleLM (Position->CastleFlags&0x01)
#define CastleSO (Position->CastleFlags&0x20)
#define CastleLO (Position->CastleFlags&0x10)
#define ResetCastleSM (Position->CastleFlags&=0xFD)
#define ResetCastleLM (Position->CastleFlags&=0xFE)
#define ResetCastleSO (Position->CastleFlags&=0xDF)
#define ResetCastleLO (Position->CastleFlags&=0xEF)

 /* these Macros are used to calculate the bitboard of a particular kind of piece

    P2 P1 P0
     0  0  0    empty
     0  0  1    pawn
     0  1  0    knight
     0  1  1    bishop
     1  0  0    rook
     1  0  1    queen
     1  1  0    king
 */
#define Occupation (Position->P0 | Position->P1 | Position->P2) /* board occupation */
#define Pawns (Position->P0 & ~Position->P1 & ~Position->P2) /* all the pawns on the board */
#define Knights (~Position->P0 & Position->P1 & ~Position->P2)
#define Bishops (Position->P0 & Position->P1)
#define Rooks (~Position->P0 & ~Position->P1 & Position->P2)
#define Queens (Position->P0 & Position->P2)
#define Kings (Position->P1 & Position->P2) /* a bitboard with the 2 kings */

 /* get the piece type giving the square */
#define Piece(sq) (((Position->P2>>(sq))&1)<<2 | ((Position->P1>>(sq))&1)<<1 | ((Position->P0>>(sq))&1))

/* calculate the square related to the opponent */
#define OppSq(sp) ((sp)^0x38)
/* Absolute Square, we need this macro to return the move in long algebric notation  */
#define AbsSq(sq,col) ((col)==WHITE ? (sq):OppSq(sq))

/*
The board is always saved with the side to move in the lower part of the bitboards to use the same generation and
make for the Black and the White side.
This needs the inversion of the 4 bitboards, roll the Castle rights and update the side to move.
*/
#define ChangeSide \
do{ \
   Position->PM^=Occupation; /* update the side to move pieces */\
   Position->PM=RevBB(Position->PM);\
   Position->P0=RevBB(Position->P0);\
   Position->P1=RevBB(Position->P1);\
   Position->P2=RevBB(Position->P2);/* reverse the board */\
   Position->CastleFlags=(Position->CastleFlags>>4)|(Position->CastleFlags<<4);/* roll the castle rights */\
   Position->STM ^= BLACK; /* change the side to move */\
}while(0)

/* return the bitboard with the rook destinations */
static inline TBB GenRook(uint64_t sq, TBB occupation)
{
    TBB piece = 1ULL << sq;
    occupation ^= piece; /* remove the selected piece from the occupation */
    TBB piecesup = (0x0101010101010101ULL << sq) & (occupation | 0xFF00000000000000ULL); /* find the pieces up */
    TBB piecesdo = (0x8080808080808080ULL >> (63 - sq)) & (occupation | 0x00000000000000FFULL); /* find the pieces down */
    TBB piecesri = (0x00000000000000FFULL << sq) & (occupation | 0x8080808080808080ULL); /* find pieces on the right */
    TBB piecesle = (0xFF00000000000000ULL >> (63 - sq)) & (occupation | 0x0101010101010101ULL); /* find pieces on the left */
    return (((0x8080808080808080ULL >> (63 - LSB(piecesup))) & (0x0101010101010101ULL << MSB(piecesdo))) |
        ((0xFF00000000000000ULL >> (63 - LSB(piecesri))) & (0x00000000000000FFULL << MSB(piecesle)))) ^ piece;
    /* From every direction find the first piece and from that piece put a mask in the opposite direction.
       Put togheter all the 4 masks and remove the moving piece */
}

/* return the bitboard with the bishops destinations */
static inline TBB GenBishop(uint64_t sq, TBB occupation)
{  /* it's the same as the rook */
    TBB piece = 1ULL << sq;
    occupation ^= piece;
    TBB piecesup = (0x8040201008040201ULL << sq) & (occupation | 0xFF80808080808080ULL);
    TBB piecesdo = (0x8040201008040201ULL >> (63 - sq)) & (occupation | 0x01010101010101FFULL);
    TBB piecesle = (0x8102040810204081ULL << sq) & (occupation | 0xFF01010101010101ULL);
    TBB piecesri = (0x8102040810204081ULL >> (63 - sq)) & (occupation | 0x80808080808080FFULL);
    return (((0x8040201008040201ULL >> (63 - LSB(piecesup))) & (0x8040201008040201ULL << MSB(piecesdo))) |
        ((0x8102040810204081ULL >> (63 - LSB(piecesle))) & (0x8102040810204081ULL << MSB(piecesri)))) ^ piece;
}

/* return the bitboard with pieces of the same type */
static inline TBB BBPieces(TPieceType piece)
{
    switch (piece) // find the bb with the pieces of the same type
    {
    case PAWN: return Pawns;
    case KNIGHT: return Knights;
    case BISHOP: return Bishops;
    case ROOK: return Rooks;
    case QUEEN: return Queens;
    case KING: return Kings;
    default:
        ASSUME(0);
        return 0;
    }
}

/* return the bitboard with the destinations of a piece in a square (exept for pawns) */
static inline TBB BBDestinations(TPieceType piece, uint64_t sq, TBB occupation)
{
    switch (piece) // generate the destination squares of the piece
    {
    case KNIGHT: return KnightDest[sq];
    case BISHOP: return GenBishop(sq, occupation);
    case ROOK: return GenRook(sq, occupation);
    case QUEEN: return GenRook(sq, occupation) | GenBishop(sq, occupation);
    case KING: return KingDest[sq];
    default:
        ASSUME(0);
        return 0;
    }
}


/* try the move and see if the king is in check. If so return the attacking pieces, if not return 0 */
static inline TBB Illegal(TMove move)
{
    TBB From = 1ULL << move.From;
    TBB To = 1ULL << move.To;
    TBB occupation, opposing;
    occupation = Occupation;
    opposing = Position->PM ^ occupation;
    uint64_t kingsq;
    TBB newoccupation, newopposing;
    TBB king;
    newoccupation = (occupation ^ From) | To;
    newopposing = opposing & ~To;
    if ((move.MoveType & 0x07) == KING)
    {
        king = To;
        kingsq = move.To;
    }
    else
    {
        king = Kings & Position->PM;
        kingsq = LSB(king);
        if (move.MoveType & EP) { newopposing ^= To >> 8; newoccupation ^= To >> 8; }
    }

    TBB mask = KnightDest[kingsq] & newopposing;
    if (mask != 0 && (mask & Knights) > 0)
        return 1;

    mask = (((king << 9) & 0xFEFEFEFEFEFEFEFEULL) | ((king << 7) & 0x7F7F7F7F7F7F7F7FULL)) & newopposing;
    if (mask != 0 && (mask & Pawns) > 0)
        return 1;

    mask = (Bishops | Queens) & newopposing;
    if (mask != 0 && (mask & GenBishop(kingsq, newoccupation)) > 0)
        return 1;

    mask = (Rooks | Queens) & newopposing;
    if (mask != 0 && (mask & GenRook(kingsq, newoccupation)) > 0)
        return 1;

    mask = newopposing & KingDest[kingsq];
    return (mask != 0 && (mask & Kings) > 0);
}

/* Generate all pseudo-legal quiet moves */
static inline TMove* GenerateQuiets(TMove* const quiets)
{
    TBB occupation, opposing;
    occupation = Occupation;
    opposing = occupation ^ Position->PM;

    TMove* pquiets = quiets;
    for (TPieceType piece = KING; piece >= KNIGHT; piece--) // generate moves from king to knight
    {
        // generate moves for every piece of the same type of the side to move
        for (TBB pieces = BBPieces(piece) & Position->PM; pieces; pieces = ClearLSB(pieces))
        {
            uint64_t sq = LSB(pieces);
            // for every destinations on a free square generate a move
            for (TBB destinations = ~occupation & BBDestinations(piece, sq, occupation); destinations; destinations = ClearLSB(destinations))
            {
                pquiets->MoveType = piece;
                pquiets->From = sq;
                pquiets->To = LSB(destinations);
                pquiets->Prom = EMPTY;
                pquiets++;
            }
        }
    }

    /* one pawns push */
    TBB push1 = (((Pawns & Position->PM) << 8) & ~occupation) & 0x00FFFFFFFFFFFFFFULL;
    for (TBB pieces = push1; pieces; pieces = ClearLSB(pieces))
    {
        pquiets->MoveType = PAWN;
        pquiets->From = LSB(pieces) - 8;
        pquiets->To = LSB(pieces);
        pquiets->Prom = EMPTY;
        pquiets++;
    }

    /* double pawns pushes */
    for (TBB push2 = (push1 << 8) & ~occupation & 0x00000000FF000000ULL; push2; push2 = ClearLSB(push2))
    {
        pquiets->MoveType = PAWN;
        pquiets->From = LSB(push2) - 16;
        pquiets->To = LSB(push2);
        pquiets->Prom = EMPTY;
        pquiets++;
    }

    /* check if long castling is possible */
    if (CastleLM && !(occupation & 0x0EULL))
    {
        TBB roo, bis;
        roo = ExtractLSB(0x1010101010101000ULL & occupation); /* column e */
        roo |= ExtractLSB(0x0808080808080800ULL & occupation); /*column d */
        roo |= ExtractLSB(0x0404040404040400ULL & occupation); /*column c */
        roo |= ExtractLSB(0x00000000000000E0ULL & occupation);  /* row 1 */
        bis = ExtractLSB(0x0000000102040800ULL & occupation); /*antidiag from e1/e8 */
        bis |= ExtractLSB(0x0000000001020400ULL & occupation); /*antidiag from d1/d8 */
        bis |= ExtractLSB(0x0000000000010200ULL & occupation); /*antidiag from c1/c8 */
        bis |= ExtractLSB(0x0000000080402000ULL & occupation); /*diag from e1/e8 */
        bis |= ExtractLSB(0x0000008040201000ULL & occupation); /*diag from d1/d8 */
        bis |= ExtractLSB(0x0000804020100800ULL & occupation); /*diag from c1/c8 */
        if (!(((roo & (Rooks | Queens)) | (bis & (Bishops | Queens)) | (0x00000000003E7700ULL & Knights) |
            (0x0000000000003E00ULL & Pawns) | (Kings & 0x0000000000000600ULL)) & opposing))
        {  /* check if c1/c8 d1/d8 e1/e8 are not attacked */
            pquiets->MoveType = KING | CASTLE;
            pquiets->From = 4;
            pquiets->To = 2;
            pquiets->Prom = EMPTY;
            pquiets++;
        }
    }
    /* check if short castling is possible */
    if (CastleSM && !(occupation & 0x60ULL))
    {
        TBB roo, bis;
        roo = ExtractLSB(0x1010101010101000ULL & occupation); /* column e */
        roo |= ExtractLSB(0x2020202020202000ULL & occupation); /* column f */
        roo |= ExtractLSB(0x4040404040404000ULL & occupation); /* column g */
        roo |= 1ULL << MSB(0x000000000000000FULL & (occupation | 0x1ULL));/* row 1 */
        bis = ExtractLSB(0x0000000102040800ULL & occupation); /* antidiag from e1/e8 */
        bis |= ExtractLSB(0x0000010204081000ULL & occupation); /*antidiag from f1/f8 */
        bis |= ExtractLSB(0x0001020408102000ULL & occupation); /*antidiag from g1/g8 */
        bis |= ExtractLSB(0x0000000080402000ULL & occupation); /*diag from e1/e8 */
        bis |= ExtractLSB(0x0000000000804000ULL & occupation); /*diag from f1/f8 */
        bis |= 0x0000000000008000ULL; /*diag from g1/g8 */
        if (!(((roo & (Rooks | Queens)) | (bis & (Bishops | Queens)) | (0x0000000000F8DC00ULL & Knights) |
            (0x000000000000F800ULL & Pawns) | (Kings & 0x0000000000004000ULL)) & opposing))
        {  /* check if e1/e8 f1/f8 g1/g8 are not attacked */
            pquiets->MoveType = KING | CASTLE;
            pquiets->From = 4;
            pquiets->To = 6;
            pquiets->Prom = EMPTY;
            pquiets++;
        }
    }
    return pquiets;
}

/* Generate all pseudo-legal capture and promotions */
static inline TMoveEval* GenerateCapture(TMoveEval* const capture)
{
    TBB opposing, occupation;
    occupation = Occupation;
    opposing = Position->PM ^ occupation;

    TMoveEval* pcapture = capture;
    for (TPieceType piece = KING; piece >= KNIGHT; piece--) // generate moves from king to knight
    {
        // generate moves for every piece of the same type of the side to move
        for (TBB pieces = BBPieces(piece) & Position->PM; pieces; pieces = ClearLSB(pieces))
        {
            uint64_t sq = LSB(pieces);
            // for every destinations on an opponent pieces generate a move
            for (TBB destinations = opposing & BBDestinations(piece, sq, occupation); destinations; destinations = ClearLSB(destinations))
            {
                pcapture->Move.MoveType = piece | CAPTURE;
                pcapture->Move.From = sq;
                pcapture->Move.To = LSB(destinations);
                pcapture->Move.Prom = EMPTY;
                //pcapture->Eval = (Piece(LSB(destinations))<<4)|(KING-piece);
                pcapture++;
            }
        }
    }

    /* Generate pawns right captures */
    TBB pieces = Pawns & Position->PM;
    for (TBB captureri = (pieces << 9) & 0x00FEFEFEFEFEFEFEULL & opposing; captureri; captureri = ClearLSB(captureri))
    {
        pcapture->Move.MoveType = PAWN | CAPTURE;
        pcapture->Move.From = LSB(captureri) - 9;
        pcapture->Move.To = LSB(captureri);
        pcapture->Move.Prom = EMPTY;
        //pcapture->Eval = (Piece(LSB(captureri))<<4)|(KING-PAWN);
        pcapture++;
    }
    /* Generate pawns left captures */
    for (TBB capturele = (pieces << 7) & 0x007F7F7F7F7F7F7FULL & opposing; capturele; capturele = ClearLSB(capturele))
    {
        pcapture->Move.MoveType = PAWN | CAPTURE;
        pcapture->Move.From = LSB(capturele) - 7;
        pcapture->Move.To = LSB(capturele);
        pcapture->Move.Prom = EMPTY;
        //pcapture->Eval = (Piece(LSB(capturele))<<4)|(KING-PAWN);
        pcapture++;
    }

    /* Generate pawns promotions */
    if (pieces & 0x00FF000000000000ULL)
    {
        /* promotions with left capture */
        for (TBB promo = (pieces << 9) & 0xFE00000000000000ULL & opposing; promo; promo = ClearLSB(promo))
        {
            TMove move;
            move.MoveType = PAWN | PROMO | CAPTURE;
            move.From = LSB(promo)-9;
            move.To = LSB(promo);
            move.Prom = QUEEN;
            pcapture->Move = move;
            //pcapture->Eval = (QUEEN<<4)|(KING-PAWN);
            pcapture++;
            for (TPieceType piece = ROOK; piece >= KNIGHT; piece--) /* generate underpromotions */
            {
                move.Prom = piece;
                pcapture->Move = move;
                //pcapture->Eval = piece-ROOK-1; /* keep behind the other captures-promotions */
                pcapture++;
            }
        }
        /* promotions with right capture */
        for (TBB promo = (pieces << 7) & 0x7F00000000000000ULL & opposing; promo; promo = ClearLSB(promo))
        {
            TMove move;
            move.MoveType = PAWN | PROMO | CAPTURE;
            move.From = LSB(promo) - 7;
            move.To = LSB(promo);
            move.Prom = QUEEN;
            pcapture->Move = move;
            //pcapture->Eval = (QUEEN<<4)|(KING-PAWN);
            pcapture++;
            for (TPieceType piece = ROOK; piece >= KNIGHT; piece--) /* generate underpromotions */
            {
                move.Prom = piece;
                pcapture->Move = move;
                //pcapture->Eval = piece-ROOK-1; /* keep behind the other captures-promotions */
                pcapture++;
            }
        }
        /* no capture promotions */
        for (TBB promo = ((pieces << 8) & ~occupation) & 0xFF00000000000000ULL; promo; promo = ClearLSB(promo))
        {
            TMove move;
            move.MoveType = PAWN | PROMO;
            move.From = LSB(promo) - 8;
            move.To = LSB(promo);
            move.Prom = QUEEN;
            pcapture->Move = move;
            //pcapture->Eval = (QUEEN<<4)|(KING-PAWN);
            pcapture++;
            for (TPieceType piece = ROOK; piece >= KNIGHT; piece--) /* generate underpromotions */
            {
                move.Prom = piece;
                pcapture->Move = move;
                //pcapture->Eval = piece-ROOK-1; /* keep behind the other captures-promotions */
                pcapture++;
            }
        }
    }

    if (Position->EnPassant != 8)
    {  /* Generate EnPassant captures */
        for (TBB enpassant = pieces & EnPassant[Position->EnPassant]; enpassant; enpassant = ClearLSB(enpassant))
        {
            pcapture->Move.MoveType = PAWN | EP | CAPTURE;
            pcapture->Move.From = LSB(enpassant);
            pcapture->Move.To = 40 + Position->EnPassant;
            pcapture->Move.Prom = EMPTY;
            //pcapture->Eval = (PAWN<<4)|(KING-PAWN);
            pcapture++;
        }
    }
    return pcapture;
}

/* Make the move */
static inline void Make(TMove move)
{
    Position++;
    *Position = *(Position - 1); /* copy the previous position into the last one */
    TBB part = 1ULL << move.From;
    TBB dest = 1ULL << move.To;
    switch (move.MoveType & 0x07)
    {
    case PAWN:
        if (move.MoveType & EP)
        {  /* EnPassant */
            Position->PM ^= part | dest;
            Position->P0 ^= part | dest;
            Position->P0 ^= dest >> 8; /* delete the captured pawn */
            Position->EnPassant = 8;
        }
        else
        {
            if (move.MoveType & CAPTURE)
            {  /* Delete the captured piece */
                Position->P0 &= ~dest;
                Position->P1 &= ~dest;
                Position->P2 &= ~dest;
            }
            if (move.MoveType & PROMO)
            {
                Position->PM ^= part | dest;
                Position->P0 ^= part;
                Position->P0 |= (TBB)(move.Prom & 1) << (move.To);
                Position->P1 |= (TBB)(((move.Prom) >> 1) & 1) << (move.To);
                Position->P2 |= (TBB)((move.Prom) >> 2) << (move.To);
                Position->EnPassant = 8; /* clear enpassant */
            }
            else /* capture or push */
            {
                Position->PM ^= part | dest;
                Position->P0 ^= part | dest;
                Position->EnPassant = 8; /* clear enpassant */
                if (move.To == move.From + 16 && EnPassantM[move.To & 0x07] & Pawns & (Position->PM ^ (Occupation)))
                    Position->EnPassant = move.To & 0x07; /* save enpassant column */
            }
            if (move.MoveType & CAPTURE)
            {
                if (move.To == 63) ResetCastleSO; /* captured the opponent king side rook */
                else if (move.To == 56) ResetCastleLO; /* captured the opponent quuen side rook */
            }
        }
        ChangeSide;
        break;
    case KNIGHT:
    case BISHOP:
    case ROOK:
    case QUEEN:
        if (move.MoveType & CAPTURE)
        {
            Position->P0 &= ~dest;
            Position->P1 &= ~dest;
            Position->P2 &= ~dest;
        }
        Position->PM ^= part | dest;
        Position->P0 ^= (move.MoveType & 1) ? part | dest : 0;
        Position->P1 ^= (move.MoveType & 2) ? part | dest : 0;
        Position->P2 ^= (move.MoveType & 4) ? part | dest : 0;
        Position->EnPassant = 8;
        if ((move.MoveType & 0x7) == ROOK) /* update the castle rights */
        {
            if (move.From == 7) ResetCastleSM;
            else if (move.From == 0) ResetCastleLM;
        }
        if (move.MoveType & CAPTURE) /* update the castle rights */
        {
            if (move.To == 63) ResetCastleSO;
            else if (move.To == 56) ResetCastleLO;
        }
        ChangeSide;
        break;
    case KING:
        if (move.MoveType & CAPTURE)
        {
            Position->P0 &= ~dest;
            Position->P1 &= ~dest;
            Position->P2 &= ~dest;
        }
        Position->PM ^= part | dest;
        Position->P1 ^= part | dest;
        Position->P2 ^= part | dest;
        ResetCastleSM; /* update the castle rights */
        ResetCastleLM;
        Position->EnPassant = 8;
        if (move.MoveType & CAPTURE)
        {
            if (move.To == 63) ResetCastleSO;
            else if (move.To == 56) ResetCastleLO;
        }
        else if (move.MoveType & CASTLE)
        {
            if (move.To == 6) { Position->PM ^= 0x00000000000000A0ULL; Position->P2 ^= 0x00000000000000A0ULL; } /* short castling */
            else { Position->P2 ^= 0x0000000000000009ULL; Position->PM ^= 0x0000000000000009ULL; } /* long castling */
        }
        ChangeSide;
    default: break;
    }
}

#if defined(_WIN32)

#include <windows.h>

#define BILLION                             (1E9)

static BOOL g_first_time = 1;
static LARGE_INTEGER g_counts_per_sec;

int gettime(struct timespec* ct)
{
    LARGE_INTEGER count;

    if (g_first_time)
    {
        g_first_time = 0;

        if (0 == QueryPerformanceFrequency(&g_counts_per_sec))
        {
            g_counts_per_sec.QuadPart = 0;
        }
    }

    if ((NULL == ct) || (g_counts_per_sec.QuadPart <= 0) ||
        (0 == QueryPerformanceCounter(&count)))
    {
        return -1;
    }

    ct->tv_sec = count.QuadPart / g_counts_per_sec.QuadPart;
    ct->tv_nsec = ((count.QuadPart % g_counts_per_sec.QuadPart) * BILLION) / g_counts_per_sec.QuadPart;

    return 0;
}

#else

int gettime(struct timespec* ct)
{
    clock_gettime(CLOCK_MONOTONIC, ct);
}

#endif

/*
Load a position starting from a fen and a list of moves.
This function doesn't check the correctness of the fen and the moves sent.
*/
static void LoadPosition(const char* fen, char* moves)
{
    /* Clear the board */
    Position = Game;
    Position->P0 = Position->P1 = Position->P2 = Position->PM = 0;
    Position->EnPassant = 8;
    Position->STM = WHITE;
    Position->CastleFlags = 0;

    /* translate the fen to the relative position */
    uint8_t pieceside = WHITE;
    uint8_t piece = PAWN;
    uint8_t sidetomove = WHITE;
    uint64_t square = 0;
    const char* cursor;
    for (cursor = fen; *cursor != ' '; cursor++)
    {
        if (*cursor >= '1' && *cursor <= '8') square += *cursor - '0';
        else if (*cursor == '/') continue;
        else
        {
            uint64_t pos = OppSq(square);
            if (*cursor == 'p') { piece = PAWN; pieceside = BLACK; }
            else if (*cursor == 'n') { piece = KNIGHT; pieceside = BLACK; }
            else if (*cursor == 'b') { piece = BISHOP; pieceside = BLACK; }
            else if (*cursor == 'r') { piece = ROOK; pieceside = BLACK; }
            else if (*cursor == 'q') { piece = QUEEN; pieceside = BLACK; }
            else if (*cursor == 'k') { piece = KING; pieceside = BLACK; }
            else if (*cursor == 'P') { piece = PAWN; pieceside = WHITE; }
            else if (*cursor == 'N') { piece = KNIGHT; pieceside = WHITE; }
            else if (*cursor == 'B') { piece = BISHOP; pieceside = WHITE; }
            else if (*cursor == 'R') { piece = ROOK; pieceside = WHITE; }
            else if (*cursor == 'Q') { piece = QUEEN; pieceside = WHITE; }
            else if (*cursor == 'K') { piece = KING; pieceside = WHITE; }
            Position->P0 |= ((uint64_t)piece & 1) << pos;
            Position->P1 |= ((uint64_t)(piece >> 1) & 1) << pos;
            Position->P2 |= ((uint64_t)piece >> 2) << pos;
            if (pieceside == WHITE) { Position->PM |= 1ULL << pos; piece |= BLACK; }
            square++;
        }
    }
    cursor++; /* read the side to move  */
    if (*cursor == 'w') sidetomove = WHITE;
    else if (*cursor == 'b') sidetomove = BLACK;
    cursor += 2;
    if (*cursor != '-') /* read the castle rights */
    {
        for (; *cursor != ' '; cursor++)
        {
            if (*cursor == 'K') Position->CastleFlags |= 0x02;
            else if (*cursor == 'Q') Position->CastleFlags |= 0x01;
            else if (*cursor == 'k') Position->CastleFlags |= 0x20;
            else if (*cursor == 'q') Position->CastleFlags |= 0x10;
        }
        cursor++;
    }
    else cursor += 2;
    if (*cursor != '-') /* read the enpassant column */
    {
        Position->EnPassant = *cursor - 'a';
        //cursor++;
    }
    if (sidetomove == BLACK) ChangeSide;
}

/* Check the correctness of the move generator with the Perft function */
static int64_t Perft(int depth)
{
    TMove quiets[256];
    TMoveEval capture[64];
    TMove move;
    move.Move = 0;

    int64_t tot = 0;

    for (TMoveEval* pcapture = GenerateCapture(capture); pcapture > capture; pcapture--)
    {
        move = (pcapture - 1)->Move;
        if (Illegal(move)) continue;
        if (depth > 1)
        {
            Make(move);
            tot += Perft(depth - 1);
            Position--;
        }
        else tot++;
    }
    for (TMove* pquiets = GenerateQuiets(quiets); pquiets > quiets; pquiets--)
    {
        move = *(pquiets - 1);
        if (Illegal(move)) continue;
        if (depth > 1)
        {
            Make(move);
            tot += Perft(depth - 1);
            Position--;
        }
        else tot++;
    }
    return tot;
}

/* Run the Perft with this 6 test positions */
static void TestPerft(void)
{
    struct {
        char fen[200];
        int depth;
        int64_t count;
    }Test[] = { {"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",6,119060324},
              {"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1",5,193690690},
              {"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", 7,178633661},
              {"r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1",6,706045033},
              {"rnbqkb1r/pp1p1ppp/2p5/4P3/2B5/8/PPP1NnPP/RNBQK2R w KQkq - 0 6",3,53392},
              {"r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10",5,164075551} };

    int64_t totalCount = 0;
    int64_t totalDuration = 0;
    for (unsigned int i = 0; i < (sizeof Test) / (sizeof(Test[0])); i++)
    {
        LoadPosition(Test[i].fen, "");
        struct timespec begin, end;
        gettime(&begin);
        printf("%s%"PRId64"%s%"PRId64"%s", "Expected: ", Test[i].count, " Computed: ", Perft(Test[i].depth), "\r\n");
        gettime(&end);
        long t_diff = (end.tv_sec - begin.tv_sec) * 1000 + (end.tv_nsec - begin.tv_nsec) / (1000 * 1000);
        long knps = Test[i].count / t_diff;
        printf("%lu ms, %luK NPS\r\n", t_diff, knps);
        totalCount += Test[i].count;
        totalDuration += t_diff;
    }
    printf("\r\n");
    printf("Total: %lu Nodes, %lu ms, %luK NPS\r\n", (unsigned long)totalCount, (unsigned long)totalDuration, (unsigned long)(totalCount / totalDuration));
    exit(0);
}

int main(int argc, char* argv[])
{
    printf("QBB Perft in C - v1.1\r\n");
    TestPerft();
    return 0;
}
Last edited by R. Tomasi on Wed Sep 29, 2021 5:14 am, edited 1 time in total.
R. Tomasi
Posts: 307
Joined: Wed Sep 01, 2021 4:08 pm
Location: Germany
Full name: Roland Tomasi

Re: Why C++ instead of C#?

Post by R. Tomasi »

krunch wrote: Wed Sep 29, 2021 4:48 am I decided to see how fast I could make the C# version.

[snip]
Very impressive! Without porting the changes to the C version the comparision is hardly fair, but it shows that C# isn't that much of a loser, after all!
spirch
Posts: 95
Joined: Fri Nov 09, 2012 12:36 am

Re: Why C++ instead of C#?

Post by spirch »

would you mind sharing the source?

here the goal was to not change the implementation and see what happen :P

but that is nice tweak!
krunch
Posts: 10
Joined: Sat Sep 18, 2021 9:36 pm
Full name: Tony Schwebs

Re: Why C++ instead of C#?

Post by krunch »

spirch wrote: Wed Sep 29, 2021 5:16 am would you mind sharing the source?

Code: Select all

/*
 This perft implementation is based on QBBEngine by Fabio Gobbato and ported to C# by Thomas Jahn
 
 The purpose is to compare the speed differences of C# and C in chess-programming workload.
*/

using System;
using System.Buffers.Binary;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.X86;
using System.Text;

namespace QBBLatest
{
    public class QbbPerft
    {
        const int MAX_PLY = 32;
        const int WHITE = 0;
        const int BLACK = 8;

        /* define the move type, for example
           KING|CASTLE is a castle move
           PAWN|CAPTURE|EP is an enpassant move
           PAWN|PROMO|CAPTURE is a promotion with a capture */

        /* define the piece type: empty, pawn, knight, bishop, rook, queen, king */
        [Flags]
        public enum TPieceType : byte { EMPTY, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING, PIECE_MASK = 0x07, CASTLE = 0x40, PROMO = 0x20, EP = 0x10, CAPTURE = 0x08, CHECKMOVEILLEGAL = 0x80 }

        /* move structure */
        [StructLayout(LayoutKind.Explicit)]
        public struct TMove
        {
            [FieldOffset(0)]
            public int Move;
            [FieldOffset(0)]
            public TPieceType MoveType;
            [FieldOffset(1)]
            public byte From;
            [FieldOffset(2)]
            public byte To;
            [FieldOffset(3)]
            public TPieceType Promotion;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static int NewMove(TPieceType MoveType, byte From, byte To) => (byte)MoveType | (From << 8) | (To << 16);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static int NewMovePromo(TPieceType MoveType, byte From, byte To, TPieceType Promotion) => (byte)MoveType | (From << 8) | (To << 16) | ((byte)Promotion << 24);

        static TMove[][] MovesLists;

        static QbbPerft()
        {
            MovesLists = new TMove[MAX_PLY][];
            for (int i = 0; i < MAX_PLY; i++)
            {
                MovesLists[i] = new TMove[225];
            }

            PreComputePEXTAttacks();
        }

        /*
        Board structure definition
        PM,P0,P1,P2 are the 4 bitboards that contain the whole board
        PM is the bitboard with the side to move pieces
        P0,P1 and P2: with these bitboards you can obtain every type of pieces and every pieces combinations.
        */
        public struct TBoard
        {
            public ulong PM;
            public ulong P0;
            public ulong P1;
            public ulong P2;
            public byte CastleFlags; /* ..sl..SL  short long opponent SHORT LONG side to move */
            public byte EnPassant; /* enpassant column, =8 if not set */
            public byte STM; /* side to move */
        }

        static TBoard[] Game = new TBoard[MAX_PLY];
        static int iPosition;
        private static TBoard Position;

        public static ulong[] LateralMasks;
        public static ulong[] LateralMasksF;
        public static ulong[] LateralMasksR;
        public static ulong[] DiagonalMasks;
        public static ulong[] DiagonalMasksD;
        public static ulong[] DiagonalMasksA;

        public static ushort[] AttackMapDiag;
        public static ushort[] AttackMapLateral;

        public static readonly ulong[] RankMaskBase = { 0x00000000000000FF, 0x000000000000FF00, 0x0000000000FF0000, 0x00000000FF000000, 0x000000FF00000000, 0x0000FF0000000000, 0x00FF000000000000, 0xFF00000000000000 };
        public static readonly ulong[] FileMaskBase = { 0x0101010101010101, 0x0202020202020202, 0x0404040404040404,  0x0808080808080808, 0x1010101010101010, 0x2020202020202020, 0x4040404040404040, 0x8080808080808080 };
        public static readonly ulong[] DiagonalMaskBase = { 0x0000000000000001, 0x0000000000000102, 0x0000000000010204, 0x0000000001020408, 0x0000000102040810, 0x0000010204081020, 0x0001020408102040, 0x0102040810204080, 0x0204081020408000, 0x0408102040800000, 0x0810204080000000, 0x1020408000000000, 0x2040800000000000, 0x4080000000000000, 0x8000000000000000 };
        public static readonly ulong[] AntiDiagonalMaskBase = { 0x0000000000000080, 0x0000000000008040, 0x0000000000804020, 0x0000000080402010, 0x0000008040201008, 0x0000804020100804, 0x0080402010080402, 0x8040201008040201, 0x4020100804020100, 0x2010080402010000, 0x1008040201000000, 0x0804020100000000, 0x0402010000000000, 0x0201000000000000, 0x0100000000000000 };

        public static void PreComputePEXTAttacks()
        {
            LateralMasks = new ulong[65];
            LateralMasksF = new ulong[65];
            LateralMasksR = new ulong[65];
            DiagonalMasks = new ulong[65];
            DiagonalMasksD = new ulong[65];
            DiagonalMasksA = new ulong[65];
            AttackMapDiag = new ushort[64 * 64 * 256];
            AttackMapLateral = new ushort[64 * 128 * 256];

            for (int sq = 0; sq < 64; sq++)
            {
                int diagOffset = (sq / 8) + (sq % 8);
                int antiDiagOffset = (sq / 8) + 7 - (sq % 8);
                LateralMasksF[sq] = FileMaskBase[sq % 8];
                LateralMasksR[sq] = RankMaskBase[sq / 8];
                LateralMasks[sq] = FileMaskBase[sq % 8] | RankMaskBase[sq / 8];
                DiagonalMasksD[sq] = DiagonalMaskBase[diagOffset];
                DiagonalMasksA[sq] = AntiDiagonalMaskBase[antiDiagOffset];
                DiagonalMasks[sq] = DiagonalMaskBase[diagOffset] | AntiDiagonalMaskBase[antiDiagOffset];

                for (int occupiedOne = 0; occupiedOne < 256; occupiedOne++)
                {
                    for (int occupiedTwo = 0; occupiedTwo < 256; occupiedTwo++)
                    {
                        var dMask = DiagonalMasks[sq];
                        var lMask = LateralMasks[sq];

                        ulong aPosition = Bmi2.X64.ParallelBitDeposit((ulong)occupiedOne, AntiDiagonalMaskBase[antiDiagOffset]) | (1UL << sq);
                        ulong dPosition = Bmi2.X64.ParallelBitDeposit((ulong)occupiedTwo, DiagonalMaskBase[diagOffset]) | (1UL << sq);
                        ulong rPosition = Bmi2.X64.ParallelBitDeposit((ulong)occupiedOne, RankMaskBase[sq / 8]) | (1UL << sq);
                        ulong fPosition = Bmi2.X64.ParallelBitDeposit((ulong)occupiedTwo, FileMaskBase[sq % 8]) | (1UL << sq);

                        ulong index = Bmi2.X64.ParallelBitExtract((aPosition | dPosition), dMask);
                        ulong index2 = Bmi2.X64.ParallelBitExtract((rPosition | fPosition), lMask);

                        ulong dVal = Bmi2.X64.ParallelBitExtract(GenBishop2(sq, aPosition | dPosition), dMask);
                        ulong lVal = Bmi2.X64.ParallelBitExtract(GenRook2(sq, rPosition | fPosition), lMask);

                        AttackMapDiag[sq * (64 * 256) + (int)index] = (ushort)dVal;
                        AttackMapLateral[sq * (128 * 256) + (int)index2] = (ushort)lVal;
                    }
                }
            }
        }

        /* array of bitboards that contains all the knight destination for every square */
        public static readonly ulong[] KnightDest = {
            0x0000000000020400UL,0x0000000000050800UL,0x00000000000a1100UL,0x0000000000142200UL,
            0x0000000000284400UL,0x0000000000508800UL,0x0000000000a01000UL,0x0000000000402000UL,
            0x0000000002040004UL,0x0000000005080008UL,0x000000000a110011UL,0x0000000014220022UL,
            0x0000000028440044UL,0x0000000050880088UL,0x00000000a0100010UL,0x0000000040200020UL,
            0x0000000204000402UL,0x0000000508000805UL,0x0000000a1100110aUL,0x0000001422002214UL,
            0x0000002844004428UL,0x0000005088008850UL,0x000000a0100010a0UL,0x0000004020002040UL,
            0x0000020400040200UL,0x0000050800080500UL,0x00000a1100110a00UL,0x0000142200221400UL,
            0x0000284400442800UL,0x0000508800885000UL,0x0000a0100010a000UL,0x0000402000204000UL,
            0x0002040004020000UL,0x0005080008050000UL,0x000a1100110a0000UL,0x0014220022140000UL,
            0x0028440044280000UL,0x0050880088500000UL,0x00a0100010a00000UL,0x0040200020400000UL,
            0x0204000402000000UL,0x0508000805000000UL,0x0a1100110a000000UL,0x1422002214000000UL,
            0x2844004428000000UL,0x5088008850000000UL,0xa0100010a0000000UL,0x4020002040000000UL,
            0x0400040200000000UL,0x0800080500000000UL,0x1100110a00000000UL,0x2200221400000000UL,
            0x4400442800000000UL,0x8800885000000000UL,0x100010a000000000UL,0x2000204000000000UL,
            0x0004020000000000UL,0x0008050000000000UL,0x00110a0000000000UL,0x0022140000000000UL,
            0x0044280000000000UL,0x0088500000000000UL,0x0010a00000000000UL,0x0020400000000000UL,
        };

        /* The same for the king */
        public static readonly ulong[] KingDest = {
            0x0000000000000302UL,0x0000000000000705UL,0x0000000000000e0aUL,0x0000000000001c14UL,
            0x0000000000003828UL,0x0000000000007050UL,0x000000000000e0a0UL,0x000000000000c040UL,
            0x0000000000030203UL,0x0000000000070507UL,0x00000000000e0a0eUL,0x00000000001c141cUL,
            0x0000000000382838UL,0x0000000000705070UL,0x0000000000e0a0e0UL,0x0000000000c040c0UL,
            0x0000000003020300UL,0x0000000007050700UL,0x000000000e0a0e00UL,0x000000001c141c00UL,
            0x0000000038283800UL,0x0000000070507000UL,0x00000000e0a0e000UL,0x00000000c040c000UL,
            0x0000000302030000UL,0x0000000705070000UL,0x0000000e0a0e0000UL,0x0000001c141c0000UL,
            0x0000003828380000UL,0x0000007050700000UL,0x000000e0a0e00000UL,0x000000c040c00000UL,
            0x0000030203000000UL,0x0000070507000000UL,0x00000e0a0e000000UL,0x00001c141c000000UL,
            0x0000382838000000UL,0x0000705070000000UL,0x0000e0a0e0000000UL,0x0000c040c0000000UL,
            0x0003020300000000UL,0x0007050700000000UL,0x000e0a0e00000000UL,0x001c141c00000000UL,
            0x0038283800000000UL,0x0070507000000000UL,0x00e0a0e000000000UL,0x00c040c000000000UL,
            0x0302030000000000UL,0x0705070000000000UL,0x0e0a0e0000000000UL,0x1c141c0000000000UL,
            0x3828380000000000UL,0x7050700000000000UL,0xe0a0e00000000000UL,0xc040c00000000000UL,
            0x0203000000000000UL,0x0507000000000000UL,0x0a0e000000000000UL,0x141c000000000000UL,
            0x2838000000000000UL,0x5070000000000000UL,0xa0e0000000000000UL,0x40c0000000000000UL
        };

        /* masks for finding the pawns that can capture with an enpassant (in move generation) */
        static readonly ulong[] EnPassant = {
            0x0000000200000000UL,0x0000000500000000UL,0x0000000A00000000UL,0x0000001400000000UL,
            0x0000002800000000UL,0x0000005000000000UL,0x000000A000000000UL,0x0000004000000000UL
        };

        /* masks for finding the pawns that can capture with an enpassant (in make move) */
        static readonly ulong[] EnPassantM = {
            0x0000000002000000UL,0x0000000005000000UL,0x000000000A000000UL,0x0000000014000000UL,
            0x0000000028000000UL,0x0000000050000000UL,0x00000000A0000000UL,0x0000000040000000UL
        };

        /*
        reverse a bitboard:
        A bitboard is an array of byte: Byte0,Byte1,Byte2,Byte3,Byte4,Byte5,Byte6,Byte7
        after this function the bitboard will be: Byte7,Byte6,Byte5,Byte4,Byte3,Byte2,Byte1,Byte0
        The board is saved always with the side to move in the low significant bits of the bitboard, so this function
        is used to change the side to move
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
        public static ulong RevBB(ulong bb) => BinaryPrimitives.ReverseEndianness(bb);

        /* return the index of the most significant bit of the bitboard, bb must always be !=0 */
        [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
        public static ulong MSB(ulong bb) => 63 ^ Lzcnt.X64.LeadingZeroCount(bb);

        /* return the index of the least significant bit of the bitboard, bb must always be !=0 */
        [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
        public static ulong LSB(ulong bb) => Bmi1.X64.TrailingZeroCount(bb);

        /* extract the least significant bit of the bitboard */
        public static ulong ExtractLSB(ulong bb) => Bmi1.X64.ExtractLowestSetBit(bb);

        /* reset the least significant bit of bb */
        public static ulong ClearLSB(ulong bb) => Bmi1.X64.ResetLowestSetBit(bb);

        /* Macro to check and reset the castle rights:
           CastleSM: short castling side to move
           CastleLM: long castling side to move
           CastleSO: short castling opponent
           CastleLO: long castling opponent
         */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool CanCastleSM() => (Position.CastleFlags & 0x02) != 0;
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool CanCastleLM() => (Position.CastleFlags & 0x01) != 0;
        static void ResetCastleSM() => Position.CastleFlags &= 0xFD;
        static void ResetCastleLM() => Position.CastleFlags &= 0xFE;
        static void ResetCastleSO() => Position.CastleFlags &= 0xDF;
        static void ResetCastleLO() => Position.CastleFlags &= 0xEF;

        /* these Macros are used to calculate the bitboard of a particular kind of piece
           P2 P1 P0
            0  0  0    empty
            0  0  1    pawn
            0  1  0    knight
            0  1  1    bishop
            1  0  0    rook
            1  0  1    queen
            1  1  0    king
        */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static ulong Occupation(ref TBoard position) => position.P0 | position.P1 | position.P2; /* board occupation */
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static ulong Pawns(ref TBoard position) => Bmi1.X64.AndNot(position.P2, Bmi1.X64.AndNot(position.P1, position.P0));

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static ulong Knights(ref TBoard position) => Bmi1.X64.AndNot(position.P0, Bmi1.X64.AndNot(position.P2, position.P1));
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static ulong Bishops(ref TBoard position) => position.P0 & position.P1;
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static ulong Rooks(ref TBoard position) => Bmi1.X64.AndNot(position.P0, Bmi1.X64.AndNot(position.P1, position.P2));
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static ulong Queens(ref TBoard position) => position.P0 & position.P2;
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static ulong QueenOrRooks(ref TBoard position) => Bmi1.X64.AndNot(position.P1, position.P2);
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static ulong QueenOrBishops(ref TBoard position) => position.P0 & (position.P2 | position.P1);
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static ulong Kings(ref TBoard position) => position.P1 & position.P2; /* a bitboard with the 2 kings */
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static ulong SideToMove(ref TBoard position) => position.PM;
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static byte EnPass(ref TBoard position) => position.EnPassant;
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static ulong Opposing(ref TBoard position) => position.PM ^ (position.P0 | position.P1 | position.P2);
        
        /* get the piece type giving the square */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static ulong Piece(int sq) => ((Position.P2 >> sq) & 1) << 2 |
                                              ((Position.P1 >> sq) & 1) << 1 |
                                              ((Position.P0 >> sq) & 1);

        /* calculate the square related to the opponent */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static int OppSq(int sp) => sp ^ 56;
        /* Absolute Square, we need this macro to return the move in long algebric notation  */
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static int AbsSq(int sq, int col) => col == WHITE ? sq : OppSq(sq);

        /* get the corresponding string to the given move  */
        static string MoveToStr(TMove move, byte tomove)
        {
            Span<char> promo = stackalloc[] { ' ', ' ', 'n', 'b', 'r', 'q' };
            StringBuilder result = new StringBuilder(6);
            result.Append((char)('a' + AbsSq(move.From, tomove) % 8));
            result.Append((char)('1' + AbsSq(move.From, tomove) / 8));
            result.Append((char)('a' + AbsSq(move.To, tomove) % 8));
            result.Append((char)('1' + AbsSq(move.To, tomove) / 8));
            result.Append(promo[(byte)move.Promotion]);
            return result.ToString();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
        static void ChangeSide()
        {
            ref TBoard position = ref Position;
            position.PM = BinaryPrimitives.ReverseEndianness(position.PM ^ Occupation(ref position));
            position.P0 = BinaryPrimitives.ReverseEndianness(position.P0);
            position.P1 = BinaryPrimitives.ReverseEndianness(position.P1);
            position.P2 = BinaryPrimitives.ReverseEndianness(position.P2);/* reverse the board */
            position.CastleFlags = (byte)((position.CastleFlags >> 4) | (position.CastleFlags << 4));/* roll the castle rights */
            position.STM ^= BLACK; /* change the side to move */
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
        private static ulong GenRook(int sq, ulong occupation)
        {
            var lMask = LateralMasks[sq];
            ulong index = Bmi2.X64.ParallelBitExtract(occupation, lMask);
            return Bmi2.X64.ParallelBitDeposit(AttackMapLateral[(sq * (128 * 256)) + (int)index], lMask);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
        private static ulong GenBishop(int sq, ulong occupation)
        {
            var dMask = DiagonalMasks[sq];
            ulong index = Bmi2.X64.ParallelBitExtract(occupation, dMask);
            return Bmi2.X64.ParallelBitDeposit(AttackMapDiag[(sq * (64 * 256)) + (int)index], dMask);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
        private static ulong GenRook2(int sq, ulong occupation)
        {
            ulong piece = 1UL << sq;
            occupation ^= piece; /* remove the selected piece from the occupation */
            ulong piecesup = (0x0101010101010101UL << sq) & (occupation | 0xFF00000000000000UL); /* find the pieces up */
            ulong piecesdo = (0x8080808080808080UL >> (63 - sq)) & (occupation | 0x00000000000000FFUL); /* find the pieces down */
            ulong piecesri = (0x00000000000000FFUL << sq) & (occupation | 0x8080808080808080UL); /* find pieces on the right */
            ulong piecesle = (0xFF00000000000000UL >> (63 - sq)) & (occupation | 0x0101010101010101UL); /* find pieces on the left */
            return (((0x8080808080808080UL >> (63 - (int)LSB(piecesup))) & (0x0101010101010101UL << (int)MSB(piecesdo))) |
                         ((0xFF00000000000000UL >> (63 - (int)LSB(piecesri))) & (0x00000000000000FFUL << (int)MSB(piecesle)))) ^ piece;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
        private static ulong GenBishop2(int sq, ulong occupation)
        {
            /* it's the same as the rook */
            ulong piece = 1UL << sq;
            occupation ^= piece;
            ulong piecesup = (0x8040201008040201UL << sq) & (occupation | 0xFF80808080808080UL);
            ulong piecesdo = (0x8040201008040201UL >> (63 - sq)) & (occupation | 0x01010101010101FFUL);
            ulong piecesle = (0x8102040810204081UL << sq) & (occupation | 0xFF01010101010101UL);
            ulong piecesri = (0x8102040810204081UL >> (63 - sq)) & (occupation | 0x80808080808080FFUL);
            return (((0x8040201008040201UL >> (63 - (int)LSB(piecesup))) & (0x8040201008040201UL << (int)MSB(piecesdo))) |
                         ((0x8102040810204081UL >> (63 - (int)LSB(piecesle))) & (0x8102040810204081UL << (int)MSB(piecesri)))) ^ piece;
        }

        [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
        private static void GenerateKingMoves(ref TBoard position, ulong occupation, ulong opposing, ref int index, TMove[] moves)
        {
            for (ulong pieces = Kings(ref position) & SideToMove(ref position); pieces != 0; pieces = ClearLSB(pieces))
            {
                byte square = (byte)LSB(pieces);

                ulong kingDest = KingDest[square];

                // generate captures
                for (ulong destinations = opposing & kingDest; destinations != 0; destinations = ClearLSB(destinations))
                {
                    moves[index++].Move = NewMove( MoveType: TPieceType.KING | TPieceType.CAPTURE | TPieceType.CHECKMOVEILLEGAL, From: square, To: (byte)LSB(destinations) );
                }

                // generate moves
                for (ulong destinations = ~occupation & kingDest; destinations != 0; destinations = ClearLSB(destinations))
                {
                    moves[index++].Move = NewMove(MoveType: TPieceType.KING | TPieceType.CHECKMOVEILLEGAL, From: square, To: (byte)LSB(destinations));
                }
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
        private static void GenerateKnightMoves(ref TBoard position, ulong occupation, ulong opposing, ref int index, TMove[] moves, TPieceType inCheck, ulong pinnedSquares)
        {
            for (ulong pieces = Knights(ref position) & SideToMove(ref position); pieces != 0; pieces = ClearLSB(pieces))
            {
                byte square = (byte)LSB(pieces);
                TPieceType pinned = ((pinnedSquares & (1UL << square)) != 0) ? TPieceType.CHECKMOVEILLEGAL : TPieceType.EMPTY;

                ulong knightDest = KnightDest[square];

                // generate captures
                for (ulong destinations = opposing & knightDest; destinations != 0; destinations = ClearLSB(destinations))
                {
                    moves[index++].Move = NewMove(MoveType: TPieceType.KNIGHT | TPieceType.CAPTURE | inCheck | pinned, From: square, To: (byte)LSB(destinations) );
                }

                // generate moves
                for (ulong destinations = ~occupation & knightDest; destinations != 0; destinations = ClearLSB(destinations))
                {
                    moves[index++].Move = NewMove(MoveType: TPieceType.KNIGHT | inCheck | pinned, From: square, To: (byte)LSB(destinations));
                }
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
        private static void GenerateRookMoves(ref TBoard position, ulong occupation, ulong opposing, ref int index, TMove[] moves, TPieceType inCheck, ulong pinnedSquares)
        {
            for (ulong pieces = Rooks(ref position) & SideToMove(ref position); pieces != 0; pieces = ClearLSB(pieces))
            {
                byte square = (byte)LSB(pieces);
                TPieceType pinned = ((pinnedSquares & (1UL << square)) != 0) ? TPieceType.CHECKMOVEILLEGAL : TPieceType.EMPTY;

                ulong rookDest = GenRook(square, occupation);

                // generate captures
                for (ulong destinations = opposing & rookDest; destinations != 0; destinations = ClearLSB(destinations))
                {
                    moves[index++].Move = NewMove( MoveType: TPieceType.ROOK | TPieceType.CAPTURE | inCheck | pinned, From: square, To: (byte)LSB(destinations) );
                }

                // generate moves
                for (ulong destinations = ~occupation & rookDest; destinations != 0; destinations = ClearLSB(destinations))
                {
                    moves[index++].Move = NewMove(MoveType: TPieceType.ROOK | inCheck | pinned, From: square, To: (byte)LSB(destinations));
                }
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
        private static void GenerateBishopMoves(ref TBoard position, ulong occupation, ulong opposing, ref int index, TMove[] moves, TPieceType inCheck, ulong pinnedSquares)
        {
            for (ulong pieces = Bishops(ref position) & SideToMove(ref position); pieces != 0; pieces = ClearLSB(pieces))
            {
                byte square = (byte)LSB(pieces);
                TPieceType pinned = ((pinnedSquares & (1UL << square)) != 0) ? TPieceType.CHECKMOVEILLEGAL : TPieceType.EMPTY;

                ulong bishopDest = GenBishop(square, occupation);

                // generate captures
                for (ulong destinations = opposing & bishopDest; destinations != 0; destinations = ClearLSB(destinations))
                {
                    moves[index++].Move = NewMove( MoveType: TPieceType.BISHOP | TPieceType.CAPTURE | inCheck | pinned, From: square, To: (byte)LSB(destinations) );
                }

                // generate moves
                for (ulong destinations = ~occupation & bishopDest; destinations != 0; destinations = ClearLSB(destinations))
                {
                    moves[index++].Move = NewMove(MoveType: TPieceType.BISHOP | inCheck | pinned, From: square, To: (byte)LSB(destinations));
                }
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
        private static void GenerateQueenMoves(ref TBoard position, ulong occupation, ulong opposing, ref int index, TMove[] moves, TPieceType inCheck, ulong pinnedSquares)
        {
            for (ulong pieces = Queens(ref position) & SideToMove(ref position); pieces != 0; pieces = ClearLSB(pieces))
            {
                byte square = (byte)LSB(pieces);
                TPieceType pinned = ((pinnedSquares & (1UL << square)) != 0) ? TPieceType.CHECKMOVEILLEGAL : TPieceType.EMPTY;

                ulong queenDest = (GenRook(square, occupation) | GenBishop(square, occupation));

                // generate captures
                for (ulong destinations = opposing & queenDest; destinations != 0; destinations = ClearLSB(destinations))
                {
                    moves[index++].Move = NewMove( MoveType: TPieceType.QUEEN | TPieceType.CAPTURE | inCheck | pinned, From: square, To: (byte)LSB(destinations) );
                }

                // generate moves
                for (ulong destinations = ~occupation & queenDest; destinations != 0; destinations = ClearLSB(destinations))
                {
                    moves[index++].Move = NewMove(MoveType: TPieceType.QUEEN | inCheck | pinned, From: square, To: (byte)LSB(destinations));
                }
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static bool Illegal(TMove move, ulong checks, int kingsq)
        {
            ref TBoard position = ref Position;
            ulong From = 1UL << move.From;
            ulong king = 1UL << move.To;
            ulong newoccupation = (Occupation(ref position) ^ From) | king;
            ulong newopposing = Bmi1.X64.AndNot(king, Opposing(ref position));

            if ((move.MoveType & TPieceType.PIECE_MASK) == TPieceType.KING)
            {
                kingsq = move.To;
                checks = (KnightDest[kingsq] & Knights(ref position)) | ((((king << 9) & 0xFEFEFEFEFEFEFEFEUL) | ((king << 7) & 0x7F7F7F7F7F7F7F7FUL))) & Pawns(ref position) | (KingDest[kingsq] & Kings(ref position));
            }
            else if ((move.MoveType & TPieceType.EP) > 0)
            {
                // Handle cases where taking EP exposes the king to check
                ulong epTo = (king >> 8);
                newoccupation ^= epTo;
                newopposing ^= epTo;
            }

            return (checks & newopposing) > 0 ||
                   (GenBishop(kingsq, newoccupation) & QueenOrBishops(ref position) & newopposing) > 0 ||
                   (GenRook(kingsq, newoccupation) & QueenOrRooks(ref position) & newopposing) > 0;
        }


        [MethodImpl(MethodImplOptions.AggressiveOptimization)]
        private static void GenerateMoves(TMove[] moves, ref int index, TPieceType isInCheck, ulong pinnedSquares)
        {
            ref TBoard position = ref Position;
            ulong occupation = Occupation(ref position);
            ulong opposing = Opposing(ref position);

            // generate moves from king to knight
            GenerateKingMoves(ref position, occupation, opposing, ref index, moves);
            GenerateQueenMoves(ref position, occupation, opposing, ref index, moves, isInCheck, pinnedSquares);
            GenerateRookMoves(ref position, occupation, opposing, ref index, moves, isInCheck, pinnedSquares);
            GenerateBishopMoves(ref position, occupation, opposing, ref index, moves, isInCheck, pinnedSquares);
            GenerateKnightMoves(ref position, occupation, opposing, ref index, moves, isInCheck, pinnedSquares);

            ulong pawns = Pawns(ref position) & SideToMove(ref position);
            GeneratePawnCaptures(moves, ref index, isInCheck, pinnedSquares, opposing, pawns);
            GeneratePawnPromotions(moves, ref index, isInCheck, pinnedSquares, occupation, opposing, pawns);
            GenerateEnPassant(moves, ref index, position, pawns);
            GeneratePawnPush(moves, ref index, isInCheck, pinnedSquares, position, occupation);
            GenerateCastling(moves, ref index, position, occupation, opposing);
        }

        [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
        private static void GenerateEnPassant(TMove[] moves, ref int index, TBoard position, ulong pawns)
        {
            if (EnPass(ref position) != 8)
            {
                for (ulong enpassant = pawns & EnPassant[EnPass(ref position)]; enpassant != 0; enpassant = ClearLSB((enpassant)))
                    moves[index++].Move = NewMove(
                        MoveType: TPieceType.PAWN | TPieceType.EP | TPieceType.PROMO | TPieceType.CHECKMOVEILLEGAL,
                        From: (byte)LSB(enpassant),
                        To: (byte)(40 + EnPass(ref position))
                    );
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
        private static void GenerateCastling(TMove[] moves, ref int index, TBoard position, ulong occupation, ulong opposing)
        {
            /* check if long castling is possible */
            if (CanCastleLM() && (occupation & 0x0EUL) == 0)
            {
                ulong roo = ExtractLSB(0x1010101010101000UL & occupation); /* column e */
                roo |= ExtractLSB(0x0808080808080800UL & occupation); /*column d */
                roo |= ExtractLSB(0x0404040404040400UL & occupation); /*column c */
                roo |= ExtractLSB(0x00000000000000E0UL & occupation);  /* row 1 */
                ulong bis = ExtractLSB(0x0000000102040800UL & occupation); /*antidiag from e1/e8 */
                bis |= ExtractLSB(0x0000000001020400UL & occupation); /*antidiag from d1/d8 */
                bis |= ExtractLSB(0x0000000000010200UL & occupation); /*antidiag from c1/c8 */
                bis |= ExtractLSB(0x0000000080402000UL & occupation); /*diag from e1/e8 */
                bis |= ExtractLSB(0x0000008040201000UL & occupation); /*diag from d1/d8 */
                bis |= ExtractLSB(0x0000804020100800UL & occupation); /*diag from c1/c8 */
                if ((((roo & QueenOrRooks(ref position)) | (bis & QueenOrBishops(ref position)) | (0x00000000003E7700UL & Knights(ref position)) |
                (0x0000000000003E00UL & Pawns(ref position)) | (Kings(ref position) & 0x0000000000000600UL)) & opposing) == 0)
                    /* check if c1/c8 d1/d8 e1/e8 are not attacked */
                    moves[index++].Move = NewMove
                    (
                        MoveType: TPieceType.KING | TPieceType.CASTLE,
                        From: 4,
                        To: 2
                    );
            }

            /* check if short castling is possible */
            if (CanCastleSM() && (occupation & 0x60UL) == 0)
            {
                ulong roo = ExtractLSB(0x1010101010101000UL & occupation); /* column e */
                roo |= ExtractLSB(0x2020202020202000UL & occupation); /* column f */
                roo |= ExtractLSB(0x4040404040404000UL & occupation); /* column g */
                roo |= 1UL << (byte)MSB(0x000000000000000FUL & (occupation | 0x1UL));/* row 1 */
                ulong bis = ExtractLSB(0x0000000102040800UL & occupation); /* antidiag from e1/e8 */
                bis |= ExtractLSB(0x0000010204081000UL & occupation); /*antidiag from f1/f8 */
                bis |= ExtractLSB(0x0001020408102000UL & occupation); /*antidiag from g1/g8 */
                bis |= ExtractLSB(0x0000000080402000UL & occupation); /*diag from e1/e8 */
                bis |= ExtractLSB(0x0000000000804000UL & occupation); /*diag from f1/f8 */
                bis |= 0x0000000000008000UL; /*diag from g1/g8 */
                if ((((roo & QueenOrRooks(ref position)) | (bis & QueenOrBishops(ref position)) | (0x0000000000F8DC00UL & Knights(ref position)) |
                (0x000000000000F800UL & Pawns(ref position)) | (Kings(ref position) & 0x0000000000004000UL)) & opposing) == 0)
                    /* check if e1/e8 f1/f8 g1/g8 are not attacked */
                    moves[index++].Move = NewMove
                    (
                        MoveType: TPieceType.KING | TPieceType.CASTLE,
                        From: 4,
                        To: 6
                    );
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
        private static void GeneratePawnPush(TMove[] moves, ref int index, TPieceType isInCheck, ulong kingRay, TBoard position, ulong occupation)
        {
            /* one pawns push */
            ulong push1 = (((Pawns(ref position) & SideToMove(ref position)) << 8) & ~occupation) & 0x00FFFFFFFFFFFFFFUL;
            for (ulong pieces = push1; pieces != 0; pieces = ClearLSB(pieces))
            {
                byte square = (byte)LSB(pieces);
                TPieceType pinned = (kingRay & (1UL << (square - 8))) != 0 ? TPieceType.CHECKMOVEILLEGAL : TPieceType.EMPTY; ;
                moves[index++].Move = NewMove
                (
                    MoveType: TPieceType.PAWN | isInCheck | pinned,
                    From: (byte)(square - 8),
                    To: square
                );
            }

            /* double pawns pushes */
            ulong push2 = (push1 << 8) & ~occupation & 0x00000000FF000000UL;
            for (; push2 != 0; push2 = ClearLSB(push2))
            {
                byte square = (byte)LSB(push2);
                TPieceType pinned = (kingRay & (1UL << (square - 16))) != 0 ? TPieceType.CHECKMOVEILLEGAL : TPieceType.EMPTY; ;
                moves[index++].Move = NewMove
                (
                    MoveType: TPieceType.PAWN | isInCheck | pinned,
                    From: (byte)(square - 16),
                    To: square
                );
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
        private static void GeneratePawnCaptures(TMove[] moves, ref int index, TPieceType isInCheck, ulong kingRay, ulong opposing, ulong pawns)
        {
            /* Generate pawns right captures */
            for (ulong rpc = (pawns << 9) & 0x00FEFEFEFEFEFEFEUL & opposing; rpc != 0; rpc = ClearLSB(rpc))
            {
                byte square = (byte)LSB(rpc);
                TPieceType pinned = (kingRay & (1UL << (square - 9))) != 0 ? TPieceType.CHECKMOVEILLEGAL : TPieceType.EMPTY; ;
                moves[index++].Move = NewMove(
                    MoveType: TPieceType.PAWN | TPieceType.CAPTURE | isInCheck | pinned,
                    From: (byte)(square - 9),
                    To: square
                );
            }

            /* Generate pawns left captures */
            for (ulong lpc = (pawns << 7) & 0x007F7F7F7F7F7F7FUL & opposing; lpc != 0; lpc = ClearLSB(lpc))
            {
                byte square = (byte)LSB(lpc);
                TPieceType pinned = (kingRay & (1UL << (square - 7))) != 0 ? TPieceType.CHECKMOVEILLEGAL : TPieceType.EMPTY; ;
                moves[index++].Move = NewMove(
                    MoveType: TPieceType.PAWN | TPieceType.CAPTURE | isInCheck | pinned,
                    From: (byte)(square - 7),
                    To: (byte)square
                );
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
        private static void GeneratePawnPromotions(TMove[] moves, ref int index, TPieceType isInCheck, ulong kingRay, ulong occupation, ulong opposing, ulong pawns)
        {
            /* Generate pawns promotions */
            if ((pawns & 0x00FF000000000000UL) != 0)
            {
                /* promotions with left capture */
                for (ulong promo = (pawns << 9) & 0xFE00000000000000UL & opposing; promo != 0; promo = ClearLSB(promo))
                {
                    byte square = (byte)LSB(promo);
                    TPieceType pinned = (kingRay & (1UL << (square - 9))) != 0 ? TPieceType.CHECKMOVEILLEGAL : TPieceType.EMPTY; ;
                    for (TPieceType piece = TPieceType.QUEEN; piece >= TPieceType.KNIGHT; piece--)
                    {
                        moves[index++].Move = NewMovePromo(
                            MoveType: TPieceType.PAWN | TPieceType.PROMO | TPieceType.CAPTURE | isInCheck | pinned,
                            From: (byte)(square - 9),
                            To: (byte)square,
                            Promotion: piece
                        );
                    }
                }

                /* promotions with right capture */
                for (ulong promo = (pawns << 7) & 0x7F00000000000000UL & opposing; promo != 0; promo = ClearLSB(promo))
                {
                    byte square = (byte)LSB(promo);
                    TPieceType pinned = (kingRay & (1UL << (square - 7))) != 0 ? TPieceType.CHECKMOVEILLEGAL : TPieceType.EMPTY; ;
                    for (TPieceType piece = TPieceType.QUEEN; piece >= TPieceType.KNIGHT; piece--)
                        moves[index++].Move = NewMovePromo(
                            MoveType: TPieceType.PAWN | TPieceType.PROMO | TPieceType.CAPTURE | isInCheck | pinned,
                            From: (byte)(square - 7),
                            To: (byte)square,
                            Promotion: piece
                        );
                }
                /* no capture promotions */
                for (ulong promo = ((pawns << 8) & ~occupation) & 0xFF00000000000000UL;
                    promo != 0;
                    promo = ClearLSB(promo))
                {
                    byte square = (byte)LSB(promo);
                    TPieceType pinned = (kingRay & (1UL << (square - 8))) != 0 ? TPieceType.CHECKMOVEILLEGAL : TPieceType.EMPTY; ;
                    for (TPieceType piece = TPieceType.QUEEN; piece >= TPieceType.KNIGHT; piece--)
                        moves[index++].Move = NewMovePromo(
                            MoveType: TPieceType.PAWN | TPieceType.PROMO | isInCheck | pinned,
                            From: (byte)(square - 8),
                            To: (byte)square,
                            Promotion: piece
                        );
                }
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
        private static void Make(TMove move)
        {
            ref TBoard position = ref Position;
            Game[iPosition++] = position;
            ulong part = 1UL << move.From;
            ulong dest = 1UL << move.To;
            position.EnPassant = 8;

            if ((move.MoveType & TPieceType.CAPTURE) != 0)
            {
                /* Delete the captured piece */
                position.P0 &= ~dest;
                position.P1 &= ~dest;
                position.P2 &= ~dest;

                if (move.To == 63) ResetCastleSO(); /* captured the opponent king side rook */
                else if (move.To == 56) ResetCastleLO(); /* captured the opponent quuen side rook */
            }

            switch (move.MoveType & TPieceType.PIECE_MASK)
            {
                case TPieceType.PAWN:
                    if ((move.MoveType & (TPieceType.EP | TPieceType.PROMO)) != 0)
                    { 
                        /* EnPassant */
                        if ((move.MoveType & TPieceType.EP) != 0)
                        {
                            position.PM ^= part | dest;
                            position.P0 ^= part | dest;
                            position.P0 ^= dest >> 8; //delete the captured pawn
                        }
                        else
                        {
                            position.PM ^= part | dest;
                            position.P0 ^= part;
                            position.P0 |= (ulong)((int)move.Promotion & 1) << move.To;
                            position.P1 |= (ulong)(((int)move.Promotion >> 1) & 1) << move.To;
                            position.P2 |= (ulong)((int)move.Promotion >> 2) << move.To;
                        }
                    }
                    else /* capture or push */
                    {
                        position.PM ^= part | dest;
                        position.P0 ^= part | dest;

                        if (move.To == move.From + 16 && (EnPassantM[move.To & 0x07] & Pawns(ref position) & Opposing(ref position)) != 0)
                            position.EnPassant = (byte)(move.To & 0x07); /* save enpassant column */
                    }

                    ChangeSide();
                    return;
                case TPieceType.KNIGHT:
                    position.PM ^= part | dest;
                    position.P1 ^= part | dest;
                    ChangeSide();
                    return;
                case TPieceType.BISHOP:
                    position.PM ^= part | dest;
                    position.P0 ^= part | dest;
                    position.P1 ^= part | dest;
                    ChangeSide();
                    return;
                case TPieceType.ROOK:
                    position.PM ^= part | dest;
                    position.P2 ^= part | dest;
                    if (move.From == 7)
                        ResetCastleSM(); //king side rook moved
                    else if (move.From == 0)
                        ResetCastleLM(); // queen side rook moved
                    ChangeSide();
                    return;
                case TPieceType.QUEEN:
                    position.PM ^= part | dest;
                    position.P0 ^= part | dest;
                    position.P2 ^= part | dest;

                    ChangeSide();
                    return;
                case TPieceType.KING:
                    position.PM ^= part | dest;
                    position.P1 ^= part | dest;
                    position.P2 ^= part | dest;
                    ResetCastleSM(); /* update the castle rights */
                    ResetCastleLM();

                    if ((move.MoveType & TPieceType.CASTLE) != 0)
                    {
                        if (move.To == 6)
                        {
                            position.PM ^= 0x00000000000000A0UL;
                            position.P2 ^= 0x00000000000000A0UL;
                        } /* short castling */
                        else
                        {
                            position.PM ^= 0x0000000000000009UL;
                            position.P2 ^= 0x0000000000000009UL;
                        } /* long castling */
                    }
                    ChangeSide();
                    return;
            }
        }

        private static void LoadPosition(string fen)
        {
            /* Clear the board */
            ref TBoard pos = ref Position;
            pos.P0 = pos.P1 = pos.P2 = pos.PM = 0;
            pos.EnPassant = 8;
            pos.STM = WHITE;
            pos.CastleFlags = 0;

            /* translate the fen to the relative position */
            byte pieceside = WHITE;
            ulong piece = (ulong)TPieceType.PAWN;
            byte sidetomove = WHITE;
            int square = 0;
            int cursor;
            for (cursor = 0; fen[cursor] != ' '; cursor++)
            {
                char cur = fen[cursor];
                if (cur >= '1' && cur <= '8')
                    square += cur - '0';
                else if (cur != '/')
                {
                    int bit = OppSq(square);
                    if (cur == 'p') { piece = (ulong)TPieceType.PAWN; pieceside = BLACK; }
                    else if (cur == 'n') { piece = (ulong)TPieceType.KNIGHT; pieceside = BLACK; }
                    else if (cur == 'b') { piece = (ulong)TPieceType.BISHOP; pieceside = BLACK; }
                    else if (cur == 'r') { piece = (ulong)TPieceType.ROOK; pieceside = BLACK; }
                    else if (cur == 'q') { piece = (ulong)TPieceType.QUEEN; pieceside = BLACK; }
                    else if (cur == 'k') { piece = (ulong)TPieceType.KING; pieceside = BLACK; }
                    else if (cur == 'P') { piece = (ulong)TPieceType.PAWN; pieceside = WHITE; }
                    else if (cur == 'N') { piece = (ulong)TPieceType.KNIGHT; pieceside = WHITE; }
                    else if (cur == 'B') { piece = (ulong)TPieceType.BISHOP; pieceside = WHITE; }
                    else if (cur == 'R') { piece = (ulong)TPieceType.ROOK; pieceside = WHITE; }
                    else if (cur == 'Q') { piece = (ulong)TPieceType.QUEEN; pieceside = WHITE; }
                    else if (cur == 'K') { piece = (ulong)TPieceType.KING; pieceside = WHITE; }
                    pos.P0 |= (piece & 1) << bit; //001
                    pos.P1 |= ((piece >> 1) & 1) << bit; //010
                    pos.P2 |= (piece >> 2) << bit; //100
                    if (pieceside == WHITE)
                    {
                        pos.PM |= (1UL << bit);
                        piece |= BLACK;
                    }
                    square++;
                }
            }

            cursor++; /* read the side to move  */
            if (fen[cursor] == 'w')
                sidetomove = WHITE;
            else if (fen[cursor] == 'b')
                sidetomove = BLACK;
            cursor += 2;
            if (fen[cursor] != '-') /* read the castle rights */
            {
                for (; fen[cursor] != ' '; cursor++)
                {
                    char cur = fen[cursor];
                    if (cur == 'K') pos.CastleFlags |= 0x02;
                    else if (cur == 'Q') pos.CastleFlags |= 0x01;
                    else if (cur == 'k') pos.CastleFlags |= 0x20;
                    else if (cur == 'q') pos.CastleFlags |= 0x10;
                }
                cursor++;
            }
            else cursor += 2;
            if (fen[cursor] != '-') /* read the enpassant column */
            {
                pos.EnPassant = (byte)(fen[cursor] - 'a');
                //cursor++;
            }
            if (sidetomove == BLACK)
                ChangeSide();
        }

        private static long Perft(int depth)
        {
            long total = 0;
            int index = 0;
            var moveList = MovesLists[depth];
            ref TBoard position = ref Position;
            ulong king = Kings(ref position) & SideToMove(ref position);
            byte kingsq = (byte)LSB(king);
            ulong occupation = Occupation(ref position);
            ulong opposing = Opposing(ref position);
            ulong kingRayDiag = GenBishop(kingsq, occupation);
            ulong kingRayLat = GenRook(kingsq, occupation);

            ulong checks = (KnightDest[kingsq] & Knights(ref position)) | ((((king << 9) & 0xFEFEFEFEFEFEFEFEUL) | ((king << 7) & 0x7F7F7F7F7F7F7F7FUL)) & Pawns(ref position)) | (KingDest[kingsq] & Kings(ref position));

            TPieceType inCheck = (kingRayDiag & QueenOrBishops(ref position) & opposing) != 0 ||
                (kingRayLat & QueenOrRooks(ref position) & opposing) != 0 ||
                (checks & opposing) != 0 ? TPieceType.CHECKMOVEILLEGAL : TPieceType.EMPTY;

            ulong opposingDiag = QueenOrBishops(ref position) & opposing;
            ulong opposingLat = QueenOrRooks(ref position) & opposing;

            ulong kingRayD = 0;
            ulong kingRayA = 0;
            ulong kingRayF = 0;
            ulong kingRayR = 0;

            if ((DiagonalMasksA[kingsq] & opposingDiag) != 0)
            {
                kingRayA = DiagonalMasksA[kingsq] & kingRayDiag;
            }

            if ((DiagonalMasksD[kingsq] & opposingDiag) != 0)
            {
                kingRayD = DiagonalMasksD[kingsq] & kingRayDiag;
            }

            if ((LateralMasksF[kingsq] & opposingLat) != 0)
            {
                kingRayF = LateralMasksF[kingsq] & kingRayLat;
            }

            if ((LateralMasksR[kingsq] & opposingLat) != 0)
            {
                kingRayR = LateralMasksR[kingsq] & kingRayLat;
            }

            ulong pinnedSquares = (kingRayD | kingRayA | kingRayF | kingRayR);

            GenerateMoves(moveList, ref index, inCheck, pinnedSquares);

            for (int i = 0; i < index; i++)
            {
                if ((moveList[i].MoveType & TPieceType.CHECKMOVEILLEGAL) != 0 && Illegal(moveList[i], checks, kingsq))
                    continue;
                if (depth > 1)
                {
                    Make(moveList[i]);

                    total += Perft(depth - 1);
                    Position = Game[--iPosition];
                }
                else
                    total++;
            }

            return total;
        }

        struct PerftResult
        {
            public double Duration;
            public long Nodes;

            public PerftResult(double t, long n)
            {
                Duration = t;
                Nodes = n;
            }

            public static PerftResult operator +(PerftResult a, PerftResult b) => new(a.Duration + b.Duration, a.Nodes + b.Nodes);
        }

        private static PerftResult TestPerft(string fen, int depth, int expectedResult)
        {
            LoadPosition(fen);

            long t0 = Stopwatch.GetTimestamp();
            long count = Perft(depth);
            long t1 = Stopwatch.GetTimestamp();
            double dt = (t1 - t0) / (double)Stopwatch.Frequency;
            double ms = (1000 * dt);
            if (expectedResult != count)
            {
                Console.WriteLine($"ERROR in Perft({fen}, {depth})");
                Console.WriteLine($"Computed result: {count}");
                Console.WriteLine($"Expected result: {expectedResult}");
            }
            else
                Console.WriteLine($"OK! {(int)ms}ms, {(int)(count / ms)}K NPS");
            return new PerftResult(dt, count);
        }

        public static void Main2()
        {
            Console.WriteLine("QBB Perft in C#");
            Console.WriteLine("https://github.com/lithander/QBB-Perft/tree/v1.4");
            Console.WriteLine();

            PerftResult totalTime = new PerftResult(0, 0);
            totalTime += TestPerft("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 6, 119060324); //Start Position
            totalTime += TestPerft("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", 5, 193690690);
            totalTime += TestPerft("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1", 7, 178633661);
            totalTime += TestPerft("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 6, 706045033);
            totalTime += TestPerft("rnbqkb1r/pp1p1ppp/2p5/4P3/2B5/8/PPP1NnPP/RNBQK2R w KQkq - 0 6", 3, 53392);
            totalTime += TestPerft("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 5, 164075551);
            Console.WriteLine();
            Console.WriteLine($"Total: {totalTime.Nodes} Nodes, {(int)(1000 * totalTime.Duration)}ms, {(int)(totalTime.Nodes / totalTime.Duration / 1000)}K NPS");
            Console.WriteLine("Press any key to quit");//stop command prompt from closing automatically on windows
            //Console.ReadKey();
        }

        //**** Debug Helpers not in the original C code

        private static void PrintPosition(TBoard pos)
        {
            PrintBB(pos.PM, "PM");
            PrintBB(pos.P0, "P0");
            PrintBB(pos.P1, "P1");
            PrintBB(pos.P2, "P2");
            Console.WriteLine("- - - -");
            PrintBB(Pawns(ref pos), "Pawns");
            PrintBB(Knights(ref pos), "Knights");
            PrintBB(Bishops(ref pos), "Bishops");
            PrintBB(Rooks(ref pos), "Roosk");
            PrintBB(Queens(ref pos), "Queens");
            PrintBB(Kings(ref pos), "Kings");

            Console.WriteLine($"CastleFlags: {pos.CastleFlags}");  /* ..sl..SL  short long opponent SHORT LONG side to move */
            Console.WriteLine($"EnPassant column: {pos.EnPassant} (8 if not set)");
            Console.WriteLine($"SideToMove: {pos.STM}"); /* side to move */
            Console.WriteLine();
        }

        static void PrintBB(ulong bb, string label)
        {
            if (label != null)
                Console.WriteLine(label);
            Console.WriteLine(Convert.ToString((long)bb, 16).PadLeft(16, '0'));
            Console.WriteLine("----------------");
            byte[] bbBytes = BitConverter.GetBytes(bb);
            Array.Reverse(bbBytes);
            foreach (byte bbByte in bbBytes)
            {
                string line = Convert.ToString(bbByte, 2).PadLeft(8, '0');
                line = line.Replace('1', 'X');
                line = line.Replace('0', '.');
                var chars = line.ToCharArray();
                Array.Reverse(chars);
                Console.WriteLine(string.Join(' ', chars));
            }
            Console.WriteLine();
        }

        private static long Divide(int depth)
        {
            long total = 0;
            int index = 0;
            var moveList = MovesLists[depth];
            ref TBoard position = ref Position;
            ulong king = Kings(ref position) & SideToMove(ref position);
            byte kingsq = (byte)LSB(king);
            ulong occupation = Occupation(ref position);
            ulong opposing = Opposing(ref position);
            ulong kingRayDiag = GenBishop(kingsq, occupation);
            ulong kingRayLat = GenRook(kingsq, occupation);

            ulong checks = (KnightDest[kingsq] & Knights(ref position)) | ((((king << 9) & 0xFEFEFEFEFEFEFEFEUL) | ((king << 7) & 0x7F7F7F7F7F7F7F7FUL)) & Pawns(ref position)) | (KingDest[kingsq] & Kings(ref position));

            TPieceType inCheck = (kingRayDiag & QueenOrBishops(ref position) & opposing) != 0 ||
                (kingRayLat & QueenOrRooks(ref position) & opposing) != 0 ||
                (checks & opposing) != 0 ? TPieceType.CHECKMOVEILLEGAL : TPieceType.EMPTY;

            ulong opposingDiag = QueenOrBishops(ref position) & opposing;
            ulong opposingLat = QueenOrRooks(ref position) & opposing;

            ulong kingRayD = 0;
            ulong kingRayA = 0;
            ulong kingRayF = 0;
            ulong kingRayR = 0;

            if ((DiagonalMasksA[kingsq] & opposingDiag) != 0)
            {
                kingRayA = DiagonalMasksA[kingsq] & kingRayDiag;
            }

            if ((DiagonalMasksD[kingsq] & opposingDiag) != 0)
            {
                kingRayD = DiagonalMasksD[kingsq] & kingRayDiag;
            }

            if ((LateralMasksF[kingsq] & opposingLat) != 0)
            {
                kingRayF = LateralMasksF[kingsq] & kingRayLat;
            }

            if ((LateralMasksR[kingsq] & opposingLat) != 0)
            {
                kingRayR = LateralMasksR[kingsq] & kingRayLat;
            }

            ulong pinnedSquares = (kingRayD | kingRayA | kingRayF | kingRayR);

            GenerateMoves(moveList, ref index, inCheck, pinnedSquares);

            for (int i = 0; i < index; i++)
            {
                if ((moveList[i].MoveType & TPieceType.CHECKMOVEILLEGAL) != 0 && Illegal(moveList[i], checks, kingsq))
                    continue;

                long nodes = 1;
                if (depth > 1)
                {
                    Make(moveList[i]);

                    nodes = Perft(depth - 1);
                    Position = Game[--iPosition];
                }

                total += nodes;

                Console.WriteLine($"  {MoveToStr(moveList[i], Position.STM)}:    {nodes:N0}");
            }

            return total;
        }

    }
}
User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

Re: Why C++ instead of C#?

Post by mvanthoor »

R. Tomasi wrote: Wed Sep 29, 2021 12:39 am Sing for us! :lol: :mrgreen:
:shock:

Ah well, I said so. I recorded a video.
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL
klx
Posts: 179
Joined: Tue Jun 15, 2021 8:11 pm
Full name: Emanuel Torres

Re: Why C++ instead of C#?

Post by klx »

krunch wrote: Wed Sep 29, 2021 6:13 am
Thanks. Is this the final version? How about the multithreading?

Now who's going to port this over to C++?

Lithander did the port to C# already. I ported it to Java. Spirch did a lot of C# optimizations. Meanwhile... Tomasi went on holiday... I think it's your turn to take one for the team Tomasi.
[Moderation warning] This signature violated the rule against commercial exhortations.
klx
Posts: 179
Joined: Tue Jun 15, 2021 8:11 pm
Full name: Emanuel Torres

Re: Why C++ instead of C#?

Post by klx »

Actually, let me rethink that.

If Tomasi ports it to C++, no-one will be able to read the code. Someone else please.
[Moderation warning] This signature violated the rule against commercial exhortations.
spirch
Posts: 95
Joined: Fri Nov 09, 2012 12:36 am

Re: Why C++ instead of C#?

Post by spirch »

@krunch thanks, going to look into this to learn new things

first thing i didn't know, i'm stupid i should have known, is

Code: Select all

 [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
in my mind it was always only one at a time that was possible :oops:

this is going to be interesting to check the whole thing after a good night :D
krunch
Posts: 10
Joined: Sat Sep 18, 2021 9:36 pm
Full name: Tony Schwebs

Re: Why C++ instead of C#?

Post by krunch »

klx wrote: Wed Sep 29, 2021 6:32 am
krunch wrote: Wed Sep 29, 2021 6:13 am
Thanks. Is this the final version? How about the multithreading?

Now who's going to port this over to C++?

Lithander did the port to C# already. I ported it to Java. Spirch did a lot of C# optimizations. Meanwhile... Tomasi went on holiday... I think it's your turn to take one for the team Tomasi.
This is the single threaded version that takes ~8.5 seconds on my machine. I'll post the multi-threaded version tomorrow after a bit of cleanup since it is over the post character limit
spirch
Posts: 95
Joined: Fri Nov 09, 2012 12:36 am

Re: Why C++ instead of C#?

Post by spirch »

i assume it need a 5000 serie amd?

i just ran it and it's slower than my version, i have a 3000 serie