From b74546857d4a16e57c975d8dfa0dc9b947126850 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 May 2024 22:11:09 +0800 Subject: [PATCH 1/5] test --- src/bitboard.h | 14 ++-- src/endgame.cpp | 6 +- src/evaluate.cpp | 28 +++---- src/misc.cpp | 25 ------ src/movegen.cpp | 12 ++- src/movegen.h | 42 +++++++++- src/parser.cpp | 193 +++++++++++++++++++++++++++++++++++++++++++++ src/position.cpp | 16 ++-- src/position.h | 200 +++++++++++++++++++++++++++++++++++++++++++++-- src/search.cpp | 10 +-- src/search.h | 1 + src/tt.cpp | 5 -- src/types.h | 76 +++++++++++++++++- src/uci.cpp | 25 ------ src/variant.h | 12 +++ 15 files changed, 558 insertions(+), 107 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index ce575b5d..87957d4c 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -463,28 +463,30 @@ inline Bitboard attacks_bb(Square s, Bitboard occupied) { /// pop_rider() finds and clears a rider in a (hybrid) rider type -inline RiderType pop_rider(RiderType* r) { - assert(*r); - const RiderType r2 = *r & ~(*r - 1); - *r &= *r - 1; +inline RiderType pop_rider(RiderType& r) { + assert(r); + const RiderType r2 = r & ~(r - 1); + r &= r - 1; return r2; } inline Bitboard attacks_bb(Color c, PieceType pt, Square s, Bitboard occupied) { + assert(pt != NO_PIECE_TYPE); Bitboard b = LeaperAttacks[c][pt][s]; RiderType r = AttackRiderTypes[pt]; while (r) - b |= rider_attacks_bb(pop_rider(&r), s, occupied); + b |= rider_attacks_bb(pop_rider(r), s, occupied); return b & PseudoAttacks[c][pt][s]; } template inline Bitboard moves_bb(Color c, PieceType pt, Square s, Bitboard occupied) { + assert(pt != NO_PIECE_TYPE); Bitboard b = LeaperMoves[Initial][c][pt][s]; RiderType r = MoveRiderTypes[Initial][pt]; while (r) - b |= rider_attacks_bb(pop_rider(&r), s, occupied); + b |= rider_attacks_bb(pop_rider(r), s, occupied); return b & PseudoMoves[Initial][c][pt][s]; } diff --git a/src/endgame.cpp b/src/endgame.cpp index a95ff373..4eb992a7 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -183,7 +183,8 @@ Value Endgame::operator()(const Position& pos) const { Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; // Non-standard promotion, evaluation unclear - if ( pos.promotion_zone(us) != rank_bb(relative_rank(us, RANK_8, pos.max_rank())) + /// yjf2002ghty: Since KPK means King-Pawn vs. King Endgame, I assume the piece type is PAWN. It can cause problems if the pawn is something else (e.g. Custom pawn piece) + if ( pos.promotion_zone(us, PAWN) != rank_bb(relative_rank(us, RANK_8, pos.max_rank())) || RANK_MAX != RANK_8 || !(pos.promotion_piece_types(us) & QUEEN)) { @@ -937,7 +938,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw, // it's probably at least a draw even with the pawn. - if ( pos.promotion_zone(us) != rank_bb(relative_rank(us, RANK_8, pos.max_rank())) + /// yjf2002ghty: Since KPKP means King-Pawn vs. King-Pawn Endgame, I assume the piece type is PAWN. It can cause problems if the pawn is something else (e.g. Custom pawn piece) + if ( pos.promotion_zone(us, PAWN) != rank_bb(relative_rank(us, RANK_8, pos.max_rank())) || RANK_MAX != RANK_8 || !(pos.promotion_piece_types(us) & QUEEN)) return SCALE_FACTOR_NONE; diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3dba8be6..dc37b575 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -46,11 +46,7 @@ // const unsigned int gEmbeddedNNUESize; // the size of the embedded file // Note that this does not work in Microsoft Visual Studio. #if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF) - #if defined(__EMSCRIPTEN__) - #include "emscripten/embedded_nnue.h" - #else INCBIN(EmbeddedNNUE, EvalFileDefaultName); - #endif #else const unsigned char gEmbeddedNNUEData[1] = {0x0}; [[maybe_unused]] @@ -106,12 +102,6 @@ namespace Eval { currentNnueVariant = variants.find(variant)->second; - #ifdef __EMSCRIPTEN__ - - vector dirs = { "", "" }; - - #else - #if defined(DEFAULT_NNUE_DIRECTORY) #define stringify2(x) #x #define stringify(x) stringify2(x) @@ -120,8 +110,6 @@ namespace Eval { vector dirs = { "" , "" , CommandLine::binaryDirectory }; #endif - #endif - for (string directory : dirs) if (eval_file_loaded != eval_file) { @@ -509,7 +497,7 @@ namespace { // Piece promotion bonus if (pos.promoted_piece_type(Pt) != NO_PIECE_TYPE) { - Bitboard zone = pos.promotion_zone(Us); + Bitboard zone = pos.promotion_zone(Us, Pt); if (zone & (b | s)) score += make_score(PieceValue[MG][pos.promoted_piece_type(Pt)] - PieceValue[MG][Pt], PieceValue[EG][pos.promoted_piece_type(Pt)] - PieceValue[EG][Pt]) / (zone & s && b ? 6 : 12); @@ -750,7 +738,7 @@ namespace { if (pos.promoted_piece_type(pt)) { otherChecks = attacks_bb(Us, pos.promoted_piece_type(pt), ksq, pos.pieces()) & attackedBy[Them][pt] - & pos.promotion_zone(Them) & pos.board_bb(); + & pos.promotion_zone(Them, pt) & pos.board_bb(); if (otherChecks & safe) kingDanger += SafeCheck[FAIRY_PIECES][more_than_one(otherChecks & safe)]; else @@ -855,7 +843,8 @@ namespace { constexpr Direction Up = pawn_push(Us); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); - Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe; + Bitboard b; + Bitboard weak, defended, nonPawnEnemies, stronglyProtected, safe; Score score = SCORE_ZERO; // Bonuses for variants with mandatory captures @@ -867,7 +856,8 @@ namespace { score -= make_score(2000, 2000) / (1 + popcount(captures & attackedBy[Them][ALL_PIECES] & ~attackedBy2[Us])); // Bonus if we threaten to force captures - Bitboard moves = 0, piecebb = pos.pieces(Us); + Bitboard moves = 0; + Bitboard piecebb = pos.pieces(Us); while (piecebb) { Square s = pop_lsb(piecebb); @@ -1012,7 +1002,8 @@ namespace { return pos.extinction_value() == VALUE_MATE ? 0 : pos.count(c) ? std::min(distance(pos.square(c), s), 5) : 5; }; - Bitboard b, bb, squaresToQueen, unsafeSquares, blockedPassers, helpers; + Bitboard b; + Bitboard bb, squaresToQueen, unsafeSquares, blockedPassers, helpers; Score score = SCORE_ZERO; b = pe->passed_pawns(Us); @@ -1147,7 +1138,8 @@ namespace { bool pawnsOnly = !(pos.pieces(Us) ^ pos.pieces(Us, PAWN)); // Early exit if, for example, both queens or 6 minor pieces have been exchanged - if (pos.non_pawn_material() < SpaceThreshold && !pawnsOnly && pos.double_step_region(Us)) + /// yjf2002ghty: By default double step is used for pawns in enhancing opening evaluation, so I assume the piece type is PAWN. It can cause problems if the pawn is something else (e.g. Custom pawn piece) + if (pos.non_pawn_material() < SpaceThreshold && !pawnsOnly && pos.double_step_region(Us, PAWN)) return SCORE_ZERO; constexpr Color Them = ~Us; diff --git a/src/misc.cpp b/src/misc.cpp index 4cd2b03c..3dad8000 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -65,29 +65,10 @@ namespace Stockfish { namespace { -#if defined(__EMSCRIPTEN__) - -#define MACRO_STRINGIFY_INTERNAL(X) #X -#define MACRO_STRINGIFY(X) MACRO_STRINGIFY_INTERNAL(X) - -const string Version = - "[" - "commit: " MACRO_STRINGIFY(EM_COMMIT) ", " - "upstream: " MACRO_STRINGIFY(EM_UPSTREAM) ", " - "emscripten: " MACRO_STRINGIFY(EM_EMSCRIPTEN) - "]"; - -#undef MACRO_STRINGIFY -#undef MACRO_STRINGIFY_INTERNAL - -#else - /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. const string Version = ""; -#endif - /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We /// can toggle the logging of std::cout and std:cin at runtime whilst preserving @@ -379,8 +360,6 @@ void* std_aligned_alloc(size_t alignment, size_t size) { return posix_memalign(&mem, alignment, size) ? nullptr : mem; #elif defined(_WIN32) return _mm_malloc(size, alignment); -#elif defined(__EMSCRIPTEN__) - return aligned_alloc(alignment, size); #else return aligned_alloc(alignment, size); #endif @@ -632,10 +611,6 @@ string binaryDirectory; // path of the executable directory string workingDirectory; // path of the working directory void init(int argc, char* argv[]) { - #ifdef __EMSCRIPTEN__ - return; - #endif - (void)argc; string pathSeparator; diff --git a/src/movegen.cpp b/src/movegen.cpp index f5a16eb4..7b8988fd 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -142,10 +142,13 @@ namespace { constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); - const Bitboard promotionZone = pos.promotion_zone(Us); + /// yjf2002ghty: Since it's generate_pawn_moves, I assume the piece type is PAWN. It can cause problems if the pawn is something else (e.g. Custom pawn piece) + const Bitboard promotionZone = pos.promotion_zone(Us, PAWN); const Bitboard standardPromotionZone = pos.sittuyin_promotion() ? Bitboard(0) : promotionZone; - const Bitboard doubleStepRegion = pos.double_step_region(Us); - const Bitboard tripleStepRegion = pos.triple_step_region(Us); + /// yjf2002ghty: Since it's generate_pawn_moves, I assume the piece type is PAWN. It can cause problems if the pawn is something else (e.g. Custom pawn piece) + const Bitboard doubleStepRegion = pos.double_step_region(Us, PAWN); + /// yjf2002ghty: Since it's generate_pawn_moves, I assume the piece type is PAWN. It can cause problems if the pawn is something else (e.g. Custom pawn piece) + const Bitboard tripleStepRegion = pos.triple_step_region(Us, PAWN); const Bitboard pawns = pos.pieces(Us, PAWN); const Bitboard movable = pos.board_bb(Us, PAWN) & ~pos.pieces(); @@ -302,13 +305,14 @@ namespace { Bitboard b = ( (attacks & pos.pieces()) | (quiets & ~pos.pieces())); Bitboard b1 = b & target; - Bitboard promotion_zone = pos.promotion_zone(Us); + Bitboard promotion_zone = pos.promotion_zone(Us, Pt); PieceType promPt = pos.promoted_piece_type(Pt); Bitboard b2 = promPt && (!pos.promotion_limit(promPt) || pos.promotion_limit(promPt) > pos.count(Us, promPt)) ? b1 : Bitboard(0); Bitboard b3 = pos.piece_demotion() && pos.is_promoted(from) ? b1 : Bitboard(0); Bitboard pawnPromotions = pos.variant()->promotionPawnTypes[Us] & Pt ? b & (Type == EVASIONS ? target : ~pos.pieces(Us)) & promotion_zone : Bitboard(0); Bitboard epSquares = pos.variant()->enPassantTypes[Us] & Pt ? attacks & ~quiets & pos.ep_squares() & ~pos.pieces() : Bitboard(0); + // target squares considering pawn promotions if (pawnPromotions && pos.mandatory_pawn_promotion()) b1 &= ~pawnPromotions; diff --git a/src/movegen.h b/src/movegen.h index bbb35b39..ffdd9bfd 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -28,11 +28,19 @@ namespace Stockfish { class Position; enum GenType { + /// yjf2002ghty: I think it's better to add some explanations here so that new developers can understand what these means, as some of the terms connot be found in chessprogramming wiki. I'm not sure if my explanations are correct. If there are anything wrong, please point it out. + + //Moves that a piece is removed from the board as part of the completion of the move CAPTURES, + //Moves which do not alter material, thus no captures nor promotions QUIETS, + //Moves which do not alter material, and give check to opponent QUIET_CHECKS, + //Check evasion moves, including interpositions, attacker capture and king withdrawal EVASIONS, + //Moves that are not check evasion moves NON_EVASIONS, + //Moves that are legal LEGAL }; @@ -55,12 +63,37 @@ inline bool operator<(const ExtMove& f, const ExtMove& s) { template ExtMove* generate(const Position& pos, ExtMove* moveList); +constexpr size_t moveListSize = sizeof(ExtMove) * MAX_MOVES; + /// The MoveList struct is a simple wrapper around generate(). It sometimes comes /// in handy to use this class instead of the low level generate() function. template struct MoveList { - explicit MoveList(const Position& pos) : last(generate(pos, moveList)) {} + +#ifdef USE_HEAP_INSTEAD_OF_STACK_FOR_MOVE_LIST + explicit MoveList(const Position& pos) + { + this->moveList = (ExtMove*)malloc(moveListSize); + if (this->moveList == 0) + { + printf("Error: Failed to allocate memory in heap."); + exit(1); + } + this->last = generate(pos, this->moveList); + } + + ~MoveList() + { + free(this->moveList); + } +#else + explicit MoveList(const Position& pos) : last(generate(pos, moveList)) + { + ; + } +#endif + const ExtMove* begin() const { return moveList; } const ExtMove* end() const { return last; } size_t size() const { return last - moveList; } @@ -69,7 +102,12 @@ struct MoveList { } private: - ExtMove moveList[MAX_MOVES], *last; + ExtMove* last; +#ifdef USE_HEAP_INSTEAD_OF_STACK_FOR_MOVE_LIST + ExtMove* moveList = 0; +#else + ExtMove moveList[MAX_MOVES]; +#endif }; } // namespace Stockfish diff --git a/src/parser.cpp b/src/parser.cpp index 3ebf1b16..da7dfe59 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -153,6 +153,162 @@ namespace { return !ss.fail(); } + template <> bool set(const std::string& value, PieceTypeBitboardGroup& target) { + size_t i; + int ParserState = -1; + int RankNum = 0; + int FileNum = 0; + char PieceChar = 0; + Bitboard board = 0x00; + // String parser using state machine + for (i = 0; i < value.length(); i++) + { + const char ch = value.at(i); + if (ch == ' ') + { + continue; + } + if (ParserState == -1) // Initial state, if "-" exists here then it means a null value. e.g. promotionRegion = - means no promotion region + { + if (ch == '-') + { + return true; + } + ParserState = 0; + } + if (ParserState == 0) // Find piece type character + { + if (ch >= 'A' && ch <= 'Z') + { + PieceChar = ch; + ParserState = 1; + } + else + { + std::cerr << "At char " << i << " of PieceTypeBitboardGroup declaration: Illegal piece type character: " << ch << std::endl; + return false; + } + } + else if (ParserState == 1) // Find "(" + { + if (ch == '(') + { + ParserState = 2; + } + else + { + std::cerr << "At char " << i << " of PieceTypeBitboardGroup declaration: Expect \"(\". Actual: " << ch << std::endl; + return false; + } + } + else if (ParserState == 2) //Find file + { + if (ch >= 'a' && ch <= 'z') + { + FileNum = ch - 'a'; + ParserState = 3; + } + else if (ch == '*') + { + FileNum = -1; + ParserState = 3; + } + else + { + std::cerr << "At char " << i << " of PieceTypeBitboardGroup declaration: Illegal file character: " << ch << std::endl; + return false; + } + } + else if (ParserState == 3) //Find rank and terminator "," or ")" + { + if (ch == '*') + { + RankNum = -1; + } + else if (ch >= '0' && ch <= '9' && RankNum >= 0) + { + RankNum = RankNum * 10 + (ch - '0'); + } + else if (ch == ',' || ch == ')') + { + if (RankNum == 0) // Here if RankNum==0 then it means either user delcared a 0 as rank, or no rank number delcared at all + { + std::cerr << "At char " << i << " of PieceTypeBitboardGroup declaration: Illegal rank number: " << RankNum << std::endl; + return false; + } + if (RankNum > 0) //When RankNum==-1, it means a whole File. + { + RankNum--; + } + if (RankNum < -1 || RankNum > RANK_MAX) + { + std::cerr << "At char " << i << " of PieceTypeBitboardGroup declaration: Max rank number exceeds. Max: " << RANK_MAX << "; Actual: " << RankNum << std::endl; + return false; + } + else if (FileNum < -1 || FileNum > FILE_MAX) + { + std::cerr << "At char " << i << " of PieceTypeBitboardGroup declaration: Max file number exceeds. Max: " << FILE_MAX << "; Actual: " << FileNum << std::endl; + return false; + } + if (RankNum == -1 && FileNum == -1) + { + board ^= ~board; + } + else if (FileNum == -1) + { + board |= rank_bb(Rank(RankNum)); + } + else if (RankNum == -1) + { + board |= file_bb(File(FileNum)); + } + else + { + board |= square_bb(make_square(File(FileNum), Rank(RankNum))); + } + if (ch == ')') + { + target.set(PieceChar, board); + ParserState = 4; + } + else + { + RankNum = 0; + FileNum = 0; + ParserState = 2; + } + } + else + { + std::cerr << "At char " << i << " of PieceTypeBitboardGroup declaration: Illegal rank character: " << ch << std::endl; + return false; + } + } + else if (ParserState == 4) // Find ";" + { + if (ch == ';') + { + ParserState = 0; + RankNum = 0; + FileNum = 0; + PieceChar = 0; + board = 0x00; + } + else + { + std::cerr << "At char " << i << " of PieceTypeBitboardGroup declaration: Expects \";\"." << std::endl; + return false; + } + } + } + if (ParserState != 0) + { + std::cerr << "At char " << i << " of PieceTypeBitboardGroup declaration: Unterminated expression." << std::endl; + return false; + } + return true; + } + template <> bool set(const std::string& value, CastlingRights& target) { char c; @@ -209,6 +365,7 @@ template bool VariantParser::parse_attribute(co : std::is_same() ? "ChasingRule" : std::is_same() ? "EnclosingRule" : std::is_same() ? "Bitboard" + : std::is_same() ? "PieceTypeBitboardGroup" : std::is_same() ? "CastlingRights" : std::is_same() ? "WallingRule" : typeid(T).name(); @@ -369,6 +526,15 @@ Variant* VariantParser::parse(Variant* v) { parse_attribute("startFen", v->startFen); parse_attribute("promotionRegionWhite", v->promotionRegion[WHITE]); parse_attribute("promotionRegionBlack", v->promotionRegion[BLACK]); + parse_attribute("pieceSpecificPromotionRegion", v->pieceSpecificPromotionRegion); + if (v->pieceSpecificPromotionRegion && !parse_attribute("whitePiecePromotionRegion", v->whitePiecePromotionRegion)) + { + std::cerr << "Syntax error in whitePiecePromotionRegion or missing whitePiecePromotionRegion definition." << std::endl; + } + if (v->pieceSpecificPromotionRegion && !parse_attribute("blackPiecePromotionRegion", v->blackPiecePromotionRegion)) + { + std::cerr << "Syntax error in blackPiecePromotionRegion or missing blackPiecePromotionRegion definition." << std::endl; + } // Take the first promotionPawnTypes as the main promotionPawnType parse_attribute("promotionPawnTypes", v->promotionPawnType[WHITE], v->pieceToChar); parse_attribute("promotionPawnTypes", v->promotionPawnType[BLACK], v->pieceToChar); @@ -422,6 +588,24 @@ Variant* VariantParser::parse(Variant* v) { parse_attribute("doubleStep", v->doubleStep); parse_attribute("doubleStepRegionWhite", v->doubleStepRegion[WHITE]); parse_attribute("doubleStepRegionBlack", v->doubleStepRegion[BLACK]); + parse_attribute("pieceSpecificDoubleStepRegion", v->pieceSpecificDoubleStepRegion); + if (v->pieceSpecificDoubleStepRegion && !parse_attribute("whitePieceDoubleStepRegion", v->whitePieceDoubleStepRegion)) + { + std::cerr << "Syntax error in whitePieceDoubleStepRegion or missing whitePieceDoubleStepRegion definition." << std::endl; + } + if (v->pieceSpecificDoubleStepRegion && !parse_attribute("blackPieceDoubleStepRegion", v->blackPieceDoubleStepRegion)) + { + std::cerr << "Syntax error in blackPieceDoubleStepRegion or missing blackPieceDoubleStepRegion definition." << std::endl; + } + parse_attribute("pieceSpecificTripleStepRegion", v->pieceSpecificTripleStepRegion); + if (v->pieceSpecificTripleStepRegion && !parse_attribute("whitePieceTripleStepRegion", v->whitePieceTripleStepRegion)) + { + std::cerr << "Syntax error in whitePieceTripleStepRegion or missing whitePieceTripleStepRegion definition." << std::endl; + } + if (v->pieceSpecificTripleStepRegion && !parse_attribute("blackPieceTripleStepRegion", v->blackPieceTripleStepRegion)) + { + std::cerr << "Syntax error in blackPieceTripleStepRegion or missing blackPieceTripleStepRegion definition." << std::endl; + } parse_attribute("tripleStepRegionWhite", v->tripleStepRegion[WHITE]); parse_attribute("tripleStepRegionBlack", v->tripleStepRegion[BLACK]); parse_attribute("enPassantRegion", v->enPassantRegion); @@ -460,6 +644,15 @@ Variant* VariantParser::parse(Variant* v) { parse_attribute("enclosingDropStart", v->enclosingDropStart); parse_attribute("whiteDropRegion", v->whiteDropRegion); parse_attribute("blackDropRegion", v->blackDropRegion); + parse_attribute("pieceSpecificDropRegion", v->pieceSpecificDropRegion); + if (v->pieceSpecificDropRegion && !parse_attribute("whitePieceDropRegion", v->whitePieceDropRegion)) + { + std::cerr << "Syntax error in whitePieceDropRegion or missing whitePieceDropRegion definition." << std::endl; + } + if (v->pieceSpecificDropRegion && !parse_attribute("blackPieceDropRegion", v->blackPieceDropRegion)) + { + std::cerr << "Syntax error in blackPieceDropRegion or missing blackPieceDropRegion definition." << std::endl; + } parse_attribute("sittuyinRookDrop", v->sittuyinRookDrop); parse_attribute("dropOppositeColoredBishop", v->dropOppositeColoredBishop); parse_attribute("dropPromoted", v->dropPromoted); diff --git a/src/position.cpp b/src/position.cpp index ade4798d..f3c35eb5 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1331,7 +1331,7 @@ bool Position::pseudo_legal(const Move m) const { // Handle the case where a mandatory piece promotion/demotion is not taken if ( mandatory_piece_promotion() && (is_promoted(from) ? piece_demotion() : promoted_piece_type(type_of(pc)) != NO_PIECE_TYPE) - && (promotion_zone(us) & (SquareBB[from] | to)) + && (promotion_zone(pc) & (SquareBB[from] | to)) && (!piece_promotion_on_capture() || capture(m))) return false; @@ -1353,16 +1353,16 @@ bool Position::pseudo_legal(const Move m) const { { // We have already handled promotion moves, so destination // cannot be on the 8th/1st rank. - if (mandatory_pawn_promotion() && (promotion_zone(us) & to) && !sittuyin_promotion()) + if (mandatory_pawn_promotion() && (promotion_zone(pc) & to) && !sittuyin_promotion()) return false; if ( !(pawn_attacks_bb(us, from) & pieces(~us) & to) // Not a capture && !((from + pawn_push(us) == to) && !(pieces() & to)) // Not a single push && !( (from + 2 * pawn_push(us) == to) // Not a double push - && (double_step_region(us) & from) + && (double_step_region(pc) & from) && !(pieces() & (to | (to - pawn_push(us))))) && !( (from + 3 * pawn_push(us) == to) // Not a triple push - && (triple_step_region(us) & from) + && (triple_step_region(pc) & from) && !(pieces() & (to | (to - pawn_push(us)) | (to - 2 * pawn_push(us)))))) return false; } @@ -1558,7 +1558,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { Piece captured = piece_on(type_of(m) == EN_PASSANT ? capture_square(to) : to); if (to == from) { - assert((type_of(m) == PROMOTION && sittuyin_promotion()) || (is_pass(m) && pass(us))); + assert((type_of(m) == PROMOTION && sittuyin_promotion()) || (is_pass(m) && (pass(us) || var->wallOrMove ))); captured = NO_PIECE; } st->capturedpromoted = is_promoted(to); @@ -1797,7 +1797,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { { Piece promotion = make_piece(us, type_of(m) == PROMOTION ? promotion_type(m) : promoted_piece_type(PAWN)); - assert((promotion_zone(us) & to) || sittuyin_promotion()); + assert((promotion_zone(pc) & to) || sittuyin_promotion()); assert(type_of(promotion) >= KNIGHT && type_of(promotion) < KING); st->promotionPawn = piece_on(to); @@ -2128,7 +2128,7 @@ void Position::undo_move(Move m) { assert(type_of(m) == DROP || empty(from) || type_of(m) == CASTLING || is_gating(m) || (type_of(m) == PROMOTION && sittuyin_promotion()) - || (is_pass(m) && pass(us))); + || (is_pass(m) && (pass(us) || var->wallOrMove))); assert(type_of(st->capturedPiece) != KING); // Reset wall squares @@ -2171,7 +2171,7 @@ void Position::undo_move(Move m) { if (type_of(m) == PROMOTION) { - assert((promotion_zone(us) & to) || sittuyin_promotion()); + assert((promotion_zone(st->promotionPawn) & to) || sittuyin_promotion()); assert(type_of(pc) == promotion_type(m)); assert(type_of(pc) >= KNIGHT && type_of(pc) < KING); assert(type_of(st->promotionPawn) == promotion_pawn_type(us) || !captures_to_hand()); diff --git a/src/position.h b/src/position.h index adaeeb6a..e0335e16 100644 --- a/src/position.h +++ b/src/position.h @@ -56,6 +56,7 @@ struct StateInfo { Square castlingKingSquare[COLOR_NB]; Bitboard wallSquares; Bitboard gatesBB[COLOR_NB]; + Bitboard not_moved_pieces[COLOR_NB]; // Not copied when making a move (will be recomputed anyhow) Key key; @@ -129,6 +130,8 @@ class Position { const std::string& piece_to_char() const; const std::string& piece_to_char_synonyms() const; Bitboard promotion_zone(Color c) const; + Bitboard promotion_zone(Color c, PieceType pt) const; + Bitboard promotion_zone(Piece p) const; Square promotion_square(Color c, Square s) const; PieceType promotion_pawn_type(Color c) const; PieceSet promotion_piece_types(Color c) const; @@ -144,7 +147,11 @@ class Position { PieceSet mutually_immune_types() const; bool endgame_eval() const; Bitboard double_step_region(Color c) const; + Bitboard double_step_region(Color c, PieceType pt) const; + Bitboard double_step_region(Piece p) const; Bitboard triple_step_region(Color c) const; + Bitboard triple_step_region(Color c, PieceType pt) const; + Bitboard triple_step_region(Piece p) const; bool castling_enabled() const; bool castling_dropped_piece() const; File castling_kingside_file() const; @@ -327,6 +334,8 @@ class Position { Score psq_score() const; Value non_pawn_material(Color c) const; Value non_pawn_material() const; + Bitboard not_moved_pieces(Color c) const; + Bitboard wall_squares() const; // Position consistency check, for debugging bool pos_is_ok() const; @@ -440,9 +449,39 @@ inline Bitboard Position::promotion_zone(Color c) const { return var->promotionRegion[c]; } +inline Bitboard Position::promotion_zone(Color c, PieceType pt) const { + assert(var != nullptr); + assert(pt != NO_PIECE_TYPE); + Bitboard b = var->promotionRegion[c]; + + // Piece specific promotion region + // Only filter moves based on var->promotionRegion[] which is a restriction that applies to all pieces + // Set whitePromotionRegion/blackPromotionRegion to AllSquares to remove the restriction + if (var->pieceSpecificPromotionRegion) + { + if (c == WHITE) + { + b &= var->whitePiecePromotionRegion.boardOfPiece(toupper(piece_to_char()[(c << PIECE_TYPE_BITS) | pt])); + } + else if (c == BLACK) + { + b &= var->blackPiecePromotionRegion.boardOfPiece(toupper(piece_to_char()[(c << PIECE_TYPE_BITS) | pt])); + } + } + return b; +} + +inline Bitboard Position::promotion_zone(Piece p) const { + assert(var != nullptr); + assert(p != NO_PIECE); + return promotion_zone(color_of(p), type_of(p)); +} + inline Square Position::promotion_square(Color c, Square s) const { assert(var != nullptr); - Bitboard b = promotion_zone(c) & forward_file_bb(c, s) & board_bb(); + /// yjf2002ghty: I'm not clear what Square s means, whether it refers to a square of a piece or it can refer to any square. I assume it means a square of a piece which can excute piece_on(s) + Piece p = piece_on(s); + Bitboard b = ((p == NO_PIECE) ? Bitboard(0) : promotion_zone(p)) & forward_file_bb(c, s) & board_bb(); return !b ? SQ_NONE : c == WHITE ? lsb(b) : msb(b); } @@ -516,11 +555,69 @@ inline Bitboard Position::double_step_region(Color c) const { return var->doubleStepRegion[c]; } +inline Bitboard Position::double_step_region(Color c, PieceType pt) const { + assert(var != nullptr); + assert(pt != NO_PIECE_TYPE); + Bitboard b = var->doubleStepRegion[c]; + + // Piece specific multi-step region (currently double & triple steps) + // Only filter moves based on var->doubleStepRegion[] which is a restriction that applies to all pieces + // Set whiteDoubleStepRegion/blackDoubleStepRegion to AllSquares to remove the restriction + if (var->pieceSpecificDoubleStepRegion) + { + if (c == WHITE) + { + b &= var->whitePieceDoubleStepRegion.boardOfPiece(toupper(piece_to_char()[(c << PIECE_TYPE_BITS) | pt])); + } + else if (c == BLACK) + { + b &= var->blackPieceDoubleStepRegion.boardOfPiece(toupper(piece_to_char()[(c << PIECE_TYPE_BITS) | pt])); + } + } + + return b; +} + +inline Bitboard Position::double_step_region(Piece p) const { + assert(var != nullptr); + assert(p != NO_PIECE); + return double_step_region(color_of(p), type_of(p)); +} + inline Bitboard Position::triple_step_region(Color c) const { assert(var != nullptr); return var->tripleStepRegion[c]; } +inline Bitboard Position::triple_step_region(Color c, PieceType pt) const { + assert(var != nullptr); + assert(pt != NO_PIECE_TYPE); + Bitboard b = var->tripleStepRegion[c]; + + // Piece specific multi-step region (currently double & triple steps) + // Only filter moves based on var->tripleStepRegion[] which is a restriction that applies to all pieces + // Set whiteTripleStepRegion/blackTripleStepRegion to AllSquares to remove the restriction + if (var->pieceSpecificTripleStepRegion) + { + if (c == WHITE) + { + b &= var->whitePieceTripleStepRegion.boardOfPiece(toupper(piece_to_char()[(c << PIECE_TYPE_BITS) | pt])); + } + else if (c == BLACK) + { + b &= var->blackPieceTripleStepRegion.boardOfPiece(toupper(piece_to_char()[(c << PIECE_TYPE_BITS) | pt])); + } + } + + return b; +} + +inline Bitboard Position::triple_step_region(Piece p) const { + assert(var != nullptr); + assert(p != NO_PIECE); + return triple_step_region(color_of(p), type_of(p)); +} + inline bool Position::castling_enabled() const { assert(var != nullptr); return var->castling; @@ -664,11 +761,26 @@ inline Bitboard Position::drop_region(Color c) const { inline Bitboard Position::drop_region(Color c, PieceType pt) const { Bitboard b = drop_region(c) & board_bb(c, pt); + // Piece specific drop region + // Only filter moves based on drop_region() which is a restriction that applies to all pieces + // Set whiteDropRegion/blackDropRegion to AllSquares to remove the restriction + if (var->pieceSpecificDropRegion) + { + if (c == WHITE) + { + b &= var->whitePieceDropRegion.boardOfPiece(toupper(piece_to_char()[(c << PIECE_TYPE_BITS) | pt])); + } + else if (c == BLACK) + { + b &= var->blackPieceDropRegion.boardOfPiece(toupper(piece_to_char()[(c << PIECE_TYPE_BITS) | pt])); + } + } + // Pawns on back ranks if (pt == PAWN) { if (!var->promotionZonePawnDrops) - b &= ~promotion_zone(c); + b &= ~promotion_zone(c, pt); if (!first_rank_pawn_drops()) b &= ~rank_bb(relative_rank(c, RANK_1, max_rank())); } @@ -1248,14 +1360,71 @@ inline Bitboard Position::attacks_from(Color c, PieceType pt, Square s) const { } inline Bitboard Position::moves_from(Color c, PieceType pt, Square s) const { + + Bitboard extraDestinations = 0x00; + + // Piece specific double/triple step region + // It adds new moves to the pieces, enabling the piece to move 2 or 3 squares ahead + // Since double step in introduced from chess variants where pawns cannot capture forward, capturing moves are not included here. + // Double/Triple step cannot attack other pieces, so attacks_from(Color c, PieceType pt, Square s) is not changed + // Due to some unknown issues, shift(Bitboard b) cannot be used here + if (var->pieceSpecificTripleStepRegion) + { + Bitboard tripleStepRegion = this->triple_step_region(c, pt); + Bitboard occupied = this->pieces(); //Bitboard where the bits whose corresponding squares having a piece on it are 1 + Bitboard piecePosition = square_bb(s); //Bitboard where only the bit which refers to the square that the piece starts the move (original square) is 1 + Bitboard extraMultipleStepMoveDestinations = 0x00; //Bitboard where extra legal multi-step destination square bits are 1 + if (tripleStepRegion & piecePosition & this->not_moved_pieces(c)) //If the original square is in tripleStepRegion and the piece is not moved + { + Bitboard oneSquareAhead = (c == WHITE) ? piecePosition << NORTH : piecePosition >> NORTH; + if (!(oneSquareAhead & occupied)) //If the square which is 1 square ahead of original square is NOT blocked + { + extraMultipleStepMoveDestinations |= oneSquareAhead; //Add the square which is 1 square ahead of original square to destination squares for triple step + Bitboard twoSquareAhead = (c == WHITE) ? piecePosition << NORTH << NORTH : piecePosition >> NORTH >> NORTH; + if (!(twoSquareAhead & occupied)) //If the square which is 2 squares ahead of original square is NOT blocked + { + extraMultipleStepMoveDestinations |= twoSquareAhead; //Add the square which is 2 squares ahead of original square to destination squares for triple step + Bitboard threeSquareAhead = (c == WHITE) ? piecePosition << NORTH << NORTH << NORTH : piecePosition >> NORTH >> NORTH >> NORTH; + if (!(threeSquareAhead & occupied)) //If the square which is 3 squares ahead of original square is NOT blocked + { + extraMultipleStepMoveDestinations |= threeSquareAhead; //Add the square which is 3 squares ahead of original square to destination squares for triple step + } + } + } + extraDestinations |= extraMultipleStepMoveDestinations; //Add destination squares to base board + } + } + if (var->pieceSpecificDoubleStepRegion) + { + Bitboard doubleStepRegion = this->double_step_region(c, pt); + Bitboard occupied = this->pieces(); //Bitboard where the bits whose corresponding squares having a piece on it are 1 + Bitboard piecePosition = square_bb(s); //Bitboard where only the bit which refers to the square that the piece starts the move (original square) is 1 + Bitboard extraMultipleStepMoveDestinations = 0x00; //Bitboard where extra legal multi-step destination square bits are 1 + if (doubleStepRegion & piecePosition & this->not_moved_pieces(c)) //If the original square is in doubleStepRegion and the piece is not moved + { + Bitboard oneSquareAhead = (c == WHITE) ? piecePosition << NORTH : piecePosition >> NORTH; + if (!(oneSquareAhead & occupied)) //If the square which is 1 square ahead of original square is NOT blocked + { + extraMultipleStepMoveDestinations |= oneSquareAhead; //Add the square which is 1 square ahead of original square to destination squares for triple step + Bitboard twoSquareAhead = (c == WHITE) ? piecePosition << NORTH << NORTH : piecePosition >> NORTH >> NORTH; + if (!(twoSquareAhead & occupied)) //If the square which is 2 squares ahead of original square is NOT blocked + { + extraMultipleStepMoveDestinations |= twoSquareAhead; //Add the square which is 2 squares ahead of original square to destination squares for triple step + } + } + extraDestinations |= extraMultipleStepMoveDestinations; //Add destination squares to base board + } + } + if (var->fastAttacks || var->fastAttacks2) - return moves_bb(c, pt, s, byTypeBB[ALL_PIECES]) & board_bb(); + return (moves_bb(c, pt, s, byTypeBB[ALL_PIECES]) | extraDestinations) & board_bb(); PieceType movePt = pt == KING ? king_type() : pt; - Bitboard b = moves_bb(c, movePt, s, byTypeBB[ALL_PIECES]); + Bitboard b = (moves_bb(c, movePt, s, byTypeBB[ALL_PIECES]) | extraDestinations); // Add initial moves - if (double_step_region(c) & s) + if (double_step_region(c, pt) & s) b |= moves_bb(c, movePt, s, byTypeBB[ALL_PIECES]); + // Xiangqi soldier if (pt == SOLDIER && !(promoted_soldiers(c) & s)) b &= file_bb(file_of(s)); @@ -1425,6 +1594,9 @@ inline void Position::put_piece(Piece pc, Square s, bool isPromoted, Piece unpro if (isPromoted) promotedPieces |= s; unpromotedBoard[s] = unpromotedPc; + + //Adding new pieces will make it as a not-moved-piece + this->st->not_moved_pieces[color_of(pc)] |= square_bb(s); } inline void Position::remove_piece(Square s) { @@ -1439,6 +1611,10 @@ inline void Position::remove_piece(Square s) { psq -= PSQT::psq[pc][s]; promotedPieces -= s; unpromotedBoard[s] = NO_PIECE; + + //not-moved-piece bitboard must ensure that there is a piece + this->st->not_moved_pieces[WHITE] &= (~square_bb(s)); + this->st->not_moved_pieces[BLACK] &= (~square_bb(s)); } inline void Position::move_piece(Square from, Square to) { @@ -1455,6 +1631,10 @@ inline void Position::move_piece(Square from, Square to) { promotedPieces ^= fromTo; unpromotedBoard[to] = unpromotedBoard[from]; unpromotedBoard[from] = NO_PIECE; + + //Once moved, no matter whether the piece is on original square or on destination square (including captures) or the color of the piece, it is no longer not-moved-piece + this->st->not_moved_pieces[WHITE] &= (~(square_bb(from) | square_bb(to))); + this->st->not_moved_pieces[BLACK] &= (~(square_bb(from) | square_bb(to))); } inline void Position::do_move(Move m, StateInfo& newSt) { @@ -1561,6 +1741,16 @@ inline bool Position::can_drop(Color c, PieceType pt) const { return variant()->freeDrops || count_in_hand(c, pt) > 0; } +//Returns the pieces that are not moved (including newly added pieces during the game, i.e. DROPS) of a side +inline Bitboard Position::not_moved_pieces(Color c) const { + return this->st->not_moved_pieces[c]; +} + +//Returns the places of wall squares +inline Bitboard Position::wall_squares() const { + return this->st->wallSquares; +} + } // namespace Stockfish #endif // #ifndef POSITION_H_INCLUDED diff --git a/src/search.cpp b/src/search.cpp index fea61f46..a90dcc24 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -936,7 +936,7 @@ namespace { && (ss-1)->statScore < 23767 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - 22 * improving + 168 * ss->ttPv + 159 + 200 * (!pos.double_step_region(pos.side_to_move()) && (pos.piece_types() & PAWN)) + && ss->staticEval >= beta - 20 * depth - 22 * improving + 168 * ss->ttPv + 159 + 200 * (!pos.double_step_region((ss - 1)->currentMovePiece) && (pos.piece_types() & PAWN)) && !excludedMove && pos.non_pawn_material(us) && pos.count(~us) != pos.count(~us) @@ -949,6 +949,7 @@ namespace { Depth R = (1090 - 300 * pos.must_capture() - 250 * !pos.checking_permitted() + 81 * depth) / 256 + std::min(int(eval - beta) / 205, pos.must_capture() || pos.blast_on_capture() ? 0 : 3); ss->currentMove = MOVE_NULL; + ss->currentMovePiece = NO_PIECE; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; pos.do_null_move(st); @@ -1017,6 +1018,7 @@ namespace { probCutCount++; ss->currentMove = move; + ss->currentMovePiece = pos.moved_piece(move); ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] [captureOrPromotion] [history_slot(pos.moved_piece(move))] @@ -1266,6 +1268,7 @@ namespace { // Speculative prefetch as early as possible // Update the current move (this must be done after singular extension search) ss->currentMove = move; + ss->currentMovePiece = pos.moved_piece(move); ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] [captureOrPromotion] [history_slot(movedPiece)] @@ -1685,6 +1688,7 @@ namespace { } ss->currentMove = move; + ss->currentMovePiece = pos.moved_piece(move); ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] [captureOrPromotion] [history_slot(pos.moved_piece(move))] @@ -1988,10 +1992,6 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { std::stringstream ss; TimePoint elapsed = Time.elapsed() + 1; - // Cf. https://github.com/niklasf/stockfish.wasm/issues/5 - #ifdef __EMSCRIPTEN__ - elapsed = std::max(elapsed, TimePoint(1)); - #endif const RootMoves& rootMoves = pos.this_thread()->rootMoves; size_t pvIdx = pos.this_thread()->pvIdx; size_t multiPV = std::min((size_t)Options["MultiPV"], rootMoves.size()); diff --git a/src/search.h b/src/search.h index 1af5a5e0..fbb6c8a2 100644 --- a/src/search.h +++ b/src/search.h @@ -44,6 +44,7 @@ struct Stack { PieceToHistory* continuationHistory; int ply; Move currentMove; + Piece currentMovePiece; Move excludedMove; Move killers[2]; Value staticEval; diff --git a/src/tt.cpp b/src/tt.cpp index 468e001d..5982d937 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -84,11 +84,6 @@ void TranspositionTable::resize(size_t mbSize) { // in a multi-threaded way. void TranspositionTable::clear() { - #ifdef __EMSCRIPTEN__ - // NOTE: Sometimes threaded TT initialization seems to fail (engine crash on startup), so here we simply initialize on main thread. - std::memset(table, 0, clusterCount * sizeof(Cluster)); - return; - #endif std::vector threads; diff --git a/src/types.h b/src/types.h index 5abfac80..2ce33868 100644 --- a/src/types.h +++ b/src/types.h @@ -227,13 +227,85 @@ typedef uint64_t Bitboard; constexpr int SQUARE_BITS = 6; #endif +//The piece type count. Currently we have A-Z as piece type so it's 26. +constexpr size_t PIECE_TYPE_COUNT = 26; + +//This is a bitboard group that matches to all 26 piece types. +//Each bitboard is related to a piece type, so we have PIECE_TYPE_COUNT of bitboards. +//For example, piece A is related to boardlist[0], piece B is related to boardlist[1], etc. +struct PieceTypeBitboardGroup +{ + PieceTypeBitboardGroup() + { + size_t i; + // By default, all bits are ZERO for all pieces. + for (i = 0; i < PIECE_TYPE_COUNT; i++) + { + this->boardlist[i] ^= this->boardlist[i]; + } + } + + PieceTypeBitboardGroup(const PieceTypeBitboardGroup& other) + { + size_t i; + for (i = 0; i < PIECE_TYPE_COUNT; i++) + { + this->boardlist[i] = other.boardlist[i]; + } + } + + // Returns the bitboard reference at the index of idx in boardlist. + // idx: The index + // : Bitboard reference at index in boardlist + Bitboard& at(const size_t idx) + { + assert(idx < PIECE_TYPE_COUNT); + return this->boardlist[idx]; + } + + // Returns the bitboard copy of a piece type. + // ptc: Only accepts A-Z + // : The copy of corresponding bitboard + // Example: + // _begin + // PieceTypeBitboardGroup a; + // Bitboard boardOfPieceA = a.boardOfPiece('A'); + // _end + Bitboard boardOfPiece(const char ptc) const + { + assert(ptc >= 65 && ptc <= 90); //ASCII of 'A'=65 + return this->boardlist[ptc - 65]; + } + + // Set the bitboard of a piece type. + // ptc: Only accepts A-Z + // board: The bitboard to set + void set(const char ptc, Bitboard board) + { + assert(ptc >= 65 && ptc <= 90); //ASCII of 'A'=65 + this->boardlist[ptc - 65] = board; + } + +private: + Bitboard boardlist[PIECE_TYPE_COUNT]; +}; + +//When defined, move list will be stored in heap. Delete this if you want to use stack to store move list. Using stack can cause overflow (Segmentation Fault) when the search is too deep. +#define USE_HEAP_INSTEAD_OF_STACK_FOR_MOVE_LIST + #ifdef ALLVARS constexpr int MAX_MOVES = 8192; -constexpr int MAX_PLY = 60; +#ifdef USE_HEAP_INSTEAD_OF_STACK_FOR_MOVE_LIST +constexpr int MAX_PLY = 246; +#else +constexpr int MAX_PLY = 60; +#endif +/// endif USE_HEAP_INSTEAD_OF_STACK_FOR_MOVE_LIST #else constexpr int MAX_MOVES = 1024; -constexpr int MAX_PLY = 246; +constexpr int MAX_PLY = 246; #endif +/// endif ALLVARS /// A move needs 16 bits to be stored /// diff --git a/src/uci.cpp b/src/uci.cpp index 6dd0aa7c..584977aa 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -34,11 +34,6 @@ #include "xboard.h" #include "syzygy/tbprobe.h" -#ifdef __EMSCRIPTEN__ -#include "emscripten/utils.h" -#include "emscripten/misc/timeit.hpp" -#endif - using namespace std; namespace Stockfish { @@ -289,13 +284,6 @@ namespace { } } - #ifdef __EMSCRIPTEN__ - void bench_eval(Position& pos) { - auto res = timeit::timeit([&]() { return Eval::evaluate(pos); }); - std::cout << res << std::endl; - } - #endif - } // namespace @@ -336,13 +324,8 @@ void UCI::loop(int argc, char* argv[]) { } do { - #ifdef __EMSCRIPTEN__ - argc = 1; - emscripten_utils_getline(cmd); - #else if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF cmd = "quit"; - #endif istringstream is(cmd); @@ -430,18 +413,10 @@ void UCI::loop(int argc, char* argv[]) { is.seekg(0); position(pos, is, states); } - #ifdef __EMSCRIPTEN__ - else if (token == "bench_eval") bench_eval(pos); - #endif else if (!token.empty() && token[0] != '#') sync_cout << "Unknown command: " << cmd << sync_endl; } while (token != "quit" && argc == 1); // Command line args are one-shot - - #ifdef __EMSCRIPTEN__ - // TODO: Ideally, we should send message to foreground and terminate gracefully from there - emscripten_force_exit(0); - #endif } diff --git a/src/variant.h b/src/variant.h index aa273835..bad33463 100644 --- a/src/variant.h +++ b/src/variant.h @@ -52,6 +52,9 @@ struct Variant { std::string startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; Bitboard mobilityRegion[COLOR_NB][PIECE_TYPE_NB] = {}; Bitboard promotionRegion[COLOR_NB] = {Rank8BB, Rank1BB}; + bool pieceSpecificPromotionRegion = false; + PieceTypeBitboardGroup whitePiecePromotionRegion; + PieceTypeBitboardGroup blackPiecePromotionRegion; PieceType promotionPawnType[COLOR_NB] = {PAWN, PAWN}; PieceSet promotionPawnTypes[COLOR_NB] = {piece_set(PAWN), piece_set(PAWN)}; PieceSet promotionPieceTypes[COLOR_NB] = {piece_set(QUEEN) | ROOK | BISHOP | KNIGHT, @@ -71,6 +74,12 @@ struct Variant { bool doubleStep = true; Bitboard doubleStepRegion[COLOR_NB] = {Rank2BB, Rank7BB}; Bitboard tripleStepRegion[COLOR_NB] = {}; + bool pieceSpecificDoubleStepRegion = false; + PieceTypeBitboardGroup whitePieceDoubleStepRegion; + PieceTypeBitboardGroup blackPieceDoubleStepRegion; + bool pieceSpecificTripleStepRegion = false; + PieceTypeBitboardGroup whitePieceTripleStepRegion; + PieceTypeBitboardGroup blackPieceTripleStepRegion; Bitboard enPassantRegion = AllSquares; PieceSet enPassantTypes[COLOR_NB] = {piece_set(PAWN), piece_set(PAWN)}; bool castling = true; @@ -99,6 +108,9 @@ struct Variant { Bitboard enclosingDropStart = 0; Bitboard whiteDropRegion = AllSquares; Bitboard blackDropRegion = AllSquares; + bool pieceSpecificDropRegion = false; + PieceTypeBitboardGroup whitePieceDropRegion; + PieceTypeBitboardGroup blackPieceDropRegion; bool sittuyinRookDrop = false; bool dropOppositeColoredBishop = false; bool dropPromoted = false; From e2802a2e955d445b4166247869aa74a2dff9973a Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 May 2024 22:19:41 +0800 Subject: [PATCH 2/5] Do not publish to NPM --- .github/workflows/ci.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8844e6b8..78dc1e8b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,12 +108,3 @@ jobs: with: name: no-embedded-nnue path: src/emscripten/public/stockfish* - - - name: Publish to NPM - if: startsWith(github.ref, 'refs/tags/') - working-directory: src/emscripten/public - run: | - npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN} - npm publish - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} From 3c4a820c4c64b148605469637006ffcf0852dc47 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 May 2024 22:41:20 +0800 Subject: [PATCH 3/5] fix --- src/evaluate.cpp | 12 ++++++++++++ src/misc.cpp | 27 ++++++++++++++++++++++++++- src/search.cpp | 4 ++++ src/tt.cpp | 7 ++++++- src/uci.cpp | 27 ++++++++++++++++++++++++++- 5 files changed, 74 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index dc37b575..069978a2 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -46,7 +46,11 @@ // const unsigned int gEmbeddedNNUESize; // the size of the embedded file // Note that this does not work in Microsoft Visual Studio. #if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF) + #if defined(__EMSCRIPTEN__) + #include "emscripten/embedded_nnue.h" + #else INCBIN(EmbeddedNNUE, EvalFileDefaultName); + #endif #else const unsigned char gEmbeddedNNUEData[1] = {0x0}; [[maybe_unused]] @@ -102,6 +106,12 @@ namespace Eval { currentNnueVariant = variants.find(variant)->second; + #ifdef __EMSCRIPTEN__ + + vector dirs = { "", "" }; + + #else + #if defined(DEFAULT_NNUE_DIRECTORY) #define stringify2(x) #x #define stringify(x) stringify2(x) @@ -110,6 +120,8 @@ namespace Eval { vector dirs = { "" , "" , CommandLine::binaryDirectory }; #endif + #endif + for (string directory : dirs) if (eval_file_loaded != eval_file) { diff --git a/src/misc.cpp b/src/misc.cpp index 3dad8000..9c6a8f47 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -65,10 +65,29 @@ namespace Stockfish { namespace { +#if defined(__EMSCRIPTEN__) + +#define MACRO_STRINGIFY_INTERNAL(X) #X +#define MACRO_STRINGIFY(X) MACRO_STRINGIFY_INTERNAL(X) + +const string Version = + "[" + "commit: " MACRO_STRINGIFY(EM_COMMIT) ", " + "upstream: " MACRO_STRINGIFY(EM_UPSTREAM) ", " + "emscripten: " MACRO_STRINGIFY(EM_EMSCRIPTEN) + "]"; + +#undef MACRO_STRINGIFY +#undef MACRO_STRINGIFY_INTERNAL + +#else + /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. const string Version = ""; +#endif + /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We /// can toggle the logging of std::cout and std:cin at runtime whilst preserving @@ -360,6 +379,8 @@ void* std_aligned_alloc(size_t alignment, size_t size) { return posix_memalign(&mem, alignment, size) ? nullptr : mem; #elif defined(_WIN32) return _mm_malloc(size, alignment); +#elif defined(__EMSCRIPTEN__) + return aligned_alloc(alignment, size); #else return aligned_alloc(alignment, size); #endif @@ -611,6 +632,10 @@ string binaryDirectory; // path of the executable directory string workingDirectory; // path of the working directory void init(int argc, char* argv[]) { + #ifdef __EMSCRIPTEN__ + return; + #endif + (void)argc; string pathSeparator; @@ -653,4 +678,4 @@ void init(int argc, char* argv[]) { } // namespace CommandLine -} // namespace Stockfish +} // namespace Stockfish \ No newline at end of file diff --git a/src/search.cpp b/src/search.cpp index a90dcc24..2be402a5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1992,6 +1992,10 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { std::stringstream ss; TimePoint elapsed = Time.elapsed() + 1; + // Cf. https://github.com/niklasf/stockfish.wasm/issues/5 + #ifdef __EMSCRIPTEN__ + elapsed = std::max(elapsed, TimePoint(1)); + #endif const RootMoves& rootMoves = pos.this_thread()->rootMoves; size_t pvIdx = pos.this_thread()->pvIdx; size_t multiPV = std::min((size_t)Options["MultiPV"], rootMoves.size()); diff --git a/src/tt.cpp b/src/tt.cpp index 5982d937..1ee55372 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -84,6 +84,11 @@ void TranspositionTable::resize(size_t mbSize) { // in a multi-threaded way. void TranspositionTable::clear() { + #ifdef __EMSCRIPTEN__ + // NOTE: Sometimes threaded TT initialization seems to fail (engine crash on startup), so here we simply initialize on main thread. + std::memset(table, 0, clusterCount * sizeof(Cluster)); + return; + #endif std::vector threads; @@ -159,4 +164,4 @@ int TranspositionTable::hashfull() const { return cnt / ClusterSize; } -} // namespace Stockfish +} // namespace Stockfish \ No newline at end of file diff --git a/src/uci.cpp b/src/uci.cpp index 584977aa..c303a13e 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -34,6 +34,11 @@ #include "xboard.h" #include "syzygy/tbprobe.h" +#ifdef __EMSCRIPTEN__ +#include "emscripten/utils.h" +#include "emscripten/misc/timeit.hpp" +#endif + using namespace std; namespace Stockfish { @@ -284,6 +289,13 @@ namespace { } } + #ifdef __EMSCRIPTEN__ + void bench_eval(Position& pos) { + auto res = timeit::timeit([&]() { return Eval::evaluate(pos); }); + std::cout << res << std::endl; + } + #endif + } // namespace @@ -324,8 +336,13 @@ void UCI::loop(int argc, char* argv[]) { } do { + #ifdef __EMSCRIPTEN__ + argc = 1; + emscripten_utils_getline(cmd); + #else if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF cmd = "quit"; + #endif istringstream is(cmd); @@ -413,10 +430,18 @@ void UCI::loop(int argc, char* argv[]) { is.seekg(0); position(pos, is, states); } + #ifdef __EMSCRIPTEN__ + else if (token == "bench_eval") bench_eval(pos); + #endif else if (!token.empty() && token[0] != '#') sync_cout << "Unknown command: " << cmd << sync_endl; } while (token != "quit" && argc == 1); // Command line args are one-shot + + #ifdef __EMSCRIPTEN__ + // TODO: Ideally, we should send message to foreground and terminate gracefully from there + emscripten_force_exit(0); + #endif } @@ -611,4 +636,4 @@ bool UCI::is_valid_option(UCI::OptionsMap& options, std::string& name) { Protocol CurrentProtocol = UCI_GENERAL; // Global object -} // namespace Stockfish +} // namespace Stockfish \ No newline at end of file From f1c86a6594699d75986fbeb71bcbf510c2903249 Mon Sep 17 00:00:00 2001 From: yjf2002ghty <47345902+yjf2002ghty@users.noreply.github.com> Date: Thu, 9 May 2024 11:17:26 +0800 Subject: [PATCH 4/5] Update ci.yml --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78dc1e8b..68dba717 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: matrix: wasm_simd: ['yes', 'no'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -104,7 +104,7 @@ jobs: docker-compose run -e uci_exe="node public/uci.js" -e sign_ref=${{ env.sign_ref }} -e UCI_NNUE_FILE="../${{ env.default_net }}" node bash tests/bench-signature.sh - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: no-embedded-nnue path: src/emscripten/public/stockfish* From 02544421bd4576f247269dbf99d0a795b32ab73f Mon Sep 17 00:00:00 2001 From: yjf2002ghty <47345902+yjf2002ghty@users.noreply.github.com> Date: Thu, 9 May 2024 11:25:00 +0800 Subject: [PATCH 5/5] Update evaluate.cpp: Remove unnecessary changes --- src/evaluate.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 069978a2..779d8c17 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -855,8 +855,7 @@ namespace { constexpr Direction Up = pawn_push(Us); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); - Bitboard b; - Bitboard weak, defended, nonPawnEnemies, stronglyProtected, safe; + Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe; Score score = SCORE_ZERO; // Bonuses for variants with mandatory captures @@ -868,8 +867,7 @@ namespace { score -= make_score(2000, 2000) / (1 + popcount(captures & attackedBy[Them][ALL_PIECES] & ~attackedBy2[Us])); // Bonus if we threaten to force captures - Bitboard moves = 0; - Bitboard piecebb = pos.pieces(Us); + Bitboard moves = 0, piecebb = pos.pieces(Us); while (piecebb) { Square s = pop_lsb(piecebb); @@ -1014,8 +1012,7 @@ namespace { return pos.extinction_value() == VALUE_MATE ? 0 : pos.count(c) ? std::min(distance(pos.square(c), s), 5) : 5; }; - Bitboard b; - Bitboard bb, squaresToQueen, unsafeSquares, blockedPassers, helpers; + Bitboard b, bb, squaresToQueen, unsafeSquares, blockedPassers, helpers; Score score = SCORE_ZERO; b = pe->passed_pawns(Us);