From: H.G.Muller Date: Sat, 3 Jan 2026 18:10:11 +0000 (+0100) Subject: Encode board steps in an unambiguous way X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=1217cc687d1c6850537809d97aa9eff3277329d0;p=fairystockfish.git Encode board steps in an unambiguous way The steps/slider/hopper arrays that pass the leaps a piece can make from the betza parser to the routines in bitboard.cpp that generate attack sets from those are now encoded as if they are for a board of 32 files, in a new integer type DirectionCode. Routines to convert this from and to Direction are provided. This allows unambiguous encoding of moves by the board step for boards of up to 16 files, while using the board step itself already became ambiguous with leaps that moved 4 files. --- diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 4f00575..b590265 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -96,40 +96,42 @@ namespace { #endif // Rider directions - const std::map RookDirectionsV { {NORTH, 0}, {SOUTH, 0}}; - const std::map RookDirectionsH { {EAST, 0}, {WEST, 0} }; - const std::map BishopDirections { {NORTH_EAST, 0}, {SOUTH_EAST, 0}, {SOUTH_WEST, 0}, {NORTH_WEST, 0} }; - const std::map LameDabbabaDirections { {2 * NORTH, 0}, {2 * EAST, 0}, {2 * SOUTH, 0}, {2 * WEST, 0} }; - const std::map HorseDirections { {2 * SOUTH + WEST, 0}, {2 * SOUTH + EAST, 0}, {SOUTH + 2 * WEST, 0}, {SOUTH + 2 * EAST, 0}, - {NORTH + 2 * WEST, 0}, {NORTH + 2 * EAST, 0}, {2 * NORTH + WEST, 0}, {2 * NORTH + EAST, 0} }; - const std::map ElephantDirections { {2 * NORTH_EAST, 0}, {2 * SOUTH_EAST, 0}, {2 * SOUTH_WEST, 0}, {2 * NORTH_WEST, 0} }; - const std::map JanggiElephantDirections { {NORTH + 2 * NORTH_EAST, 0}, {EAST + 2 * NORTH_EAST, 0}, - {EAST + 2 * SOUTH_EAST, 0}, {SOUTH + 2 * SOUTH_EAST, 0}, - {SOUTH + 2 * SOUTH_WEST, 0}, {WEST + 2 * SOUTH_WEST, 0}, - {WEST + 2 * NORTH_WEST, 0}, {NORTH + 2 * NORTH_WEST, 0} }; - const std::map GrasshopperDirectionsV { {NORTH, 1}, {SOUTH, 1}}; - const std::map GrasshopperDirectionsH { {EAST, 1}, {WEST, 1} }; - const std::map GrasshopperDirectionsD { {NORTH_EAST, 1}, {SOUTH_EAST, 1}, {SOUTH_WEST, 1}, {NORTH_WEST, 1} }; + const std::map RookDirectionsV { {step(1, 0), 0}, {step(-1, 0), 0}}; + const std::map RookDirectionsH { {step(0, 1), 0}, {step(0, -1), 0} }; + const std::map BishopDirections { {step(1, 1), 0}, {step(-1, 1), 0}, {step(-1, -1), 0}, {step(1, -1), 0} }; + const std::map LameDabbabaDirections { {step(2, 0), 0}, {step(0, 2), 0}, {step(-2, 0), 0}, {step(0, -2), 0} }; + const std::map HorseDirections { {step(-2, 1), 0}, {step(-2, -1), 0}, {step(-1, 2), 0}, {step(-1, -2), 0}, + {step(1, -2), 0}, {step(1, 2), 0}, {step(2, -1), 0}, {step(2, 1), 0} }; + const std::map ElephantDirections { {step(2, 2), 0}, {step(-2, 2), 0}, {step(-2, -2), 0}, {step(2, -2), 0} }; + const std::map JanggiElephantDirections { {step(3, 2), 0}, {step(2, 3), 0}, + {step(-2, 3), 0}, {step(-3, 2), 0}, + {step(-3, -2), 0}, {step(-2, -3), 0}, + {step(2, -3), 0}, {step(3, -2), 0} }; + const std::map GrasshopperDirectionsV { {step(1, 0), 0xFFFE}, {step(-1, 0), 0xFFFE}}; + const std::map GrasshopperDirectionsH { {step(0, 1), 0xFFFE}, {step(0, -1), 0xFFFE} }; + const std::map GrasshopperDirectionsD { {step(1, 1), 0xFFFE}, {step(-1, 1), 0xFFFE}, + {step(-1, -1), 0xFFFE}, {step(1, -1), 0xFFFE} }; enum MovementType { RIDER, HOPPER, LAME_LEAPER, HOPPER_RANGE }; template #ifdef PRECOMPUTED_MAGICS - void init_magics(Bitboard table[], Magic magics[], std::map directions, const Bitboard magicsInit[]); + void init_magics(Bitboard table[], Magic magics[], std::map directions, const Bitboard magicsInit[]); #else - void init_magics(Bitboard table[], Magic magics[], std::map directions); + void init_magics(Bitboard table[], Magic magics[], std::map directions); #endif template - Bitboard sliding_attack(std::map directions, Square sq, Bitboard occupied, Color c = WHITE) { + Bitboard sliding_attack(std::map directions, Square sq, Bitboard occupied, Color c = WHITE) { assert(MT != LAME_LEAPER); Bitboard attack = 0; - for (auto const& [d, limit] : directions) + for (auto const& [v, limit] : directions) { int count = 0; bool hurdle = false; + Direction d = board_step(v); for (Square s = sq + (c == WHITE ? d : -d); is_ok(s) && distance(s, s - (c == WHITE ? d : -d)) <= 2; s += (c == WHITE ? d : -d)) @@ -156,10 +158,10 @@ namespace { return attack; } - Bitboard lame_leaper_path(Direction d, Square s) { + Bitboard lame_leaper_path(DirectionCode d, Square s) { Direction dr = d > 0 ? NORTH : SOUTH; - Direction df = (std::abs(d % NORTH) < NORTH / 2 ? d % NORTH : -(d % NORTH)) < 0 ? WEST : EAST; - Square to = s + d; + Direction df = h_step(d) < 0 ? WEST : EAST; + Square to = s + board_step(d); Bitboard b = 0; if (!is_ok(to) || distance(s, to) >= 4) return b; @@ -179,19 +181,21 @@ namespace { return b; } - Bitboard lame_leaper_path(std::map directions, Square s) { + Bitboard lame_leaper_path(std::map directions, Square s) { Bitboard b = 0; for (const auto& i : directions) b |= lame_leaper_path(i.first, s); return b; } - Bitboard lame_leaper_attack(std::map directions, Square s, Bitboard occupied) { + Bitboard lame_leaper_attack(std::map directions, Square s, Bitboard occupied) { Bitboard b = 0; for (const auto& i : directions) { - Square to = s + i.first; - if (is_ok(to) && distance(s, to) < 4 && !(lame_leaper_path(i.first, s) & occupied)) + Direction d = board_step(i.first); + Direction h = h_step(i.first); + Square to = s + d; + if (is_ok(to) && file_of(s) + h < FILE_NB && file_of(s) + h >= 0 && !(lame_leaper_path(i.first, s) & occupied)) b |= to; } return b; @@ -202,9 +206,11 @@ namespace { /// safe_destination() returns the bitboard of target square for the given step /// from the given square. If the step is off the board, returns empty bitboard. -inline Bitboard safe_destination(Square s, int step) { +inline Bitboard safe_destination(Square s, DirectionCode c) { + Direction step = board_step(c); + Direction h = h_step(c); Square to = Square(s + step); - return is_ok(to) && distance(s, to) <= 3 ? square_bb(to) : Bitboard(0); + return is_ok(to) && file_of(s) + h >= 0 && file_of(s) + h < FILE_NB ? square_bb(to) : Bitboard(0); } @@ -297,9 +303,9 @@ void Bitboards::init_pieces() { leaper = 0; for (auto const& [d, limit] : pi->steps[initial][modality]) { - pseudo |= safe_destination(s, c == WHITE ? d : -d); + pseudo |= safe_destination(s, DirectionCode(c == WHITE ? d : -d)); if (!limit) - leaper |= safe_destination(s, c == WHITE ? d : -d); + leaper |= safe_destination(s, DirectionCode(c == WHITE ? d : -d)); } pseudo |= sliding_attack(pi->slider[initial][modality], s, 0, c); pseudo |= sliding_attack(pi->hopper[initial][modality], s, 0, c); @@ -388,9 +394,9 @@ namespace { template #ifdef PRECOMPUTED_MAGICS - void init_magics(Bitboard table[], Magic magics[], std::map directions, const Bitboard magicsInit[]) { + void init_magics(Bitboard table[], Magic magics[], std::map directions, const Bitboard magicsInit[]) { #else - void init_magics(Bitboard table[], Magic magics[], std::map directions) { + void init_magics(Bitboard table[], Magic magics[], std::map directions) { #endif // Optimal PRNG seeds to pick the correct magics in the shortest time diff --git a/src/parser.cpp b/src/parser.cpp index cbd2404..b7829b7 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -627,7 +627,7 @@ Variant* VariantParser::parse(Variant* v) { || pi->hopper[0][MODALITY_CAPTURE].size() || std::any_of(pi->steps[0][MODALITY_CAPTURE].begin(), pi->steps[0][MODALITY_CAPTURE].end(), - [](const std::pair& d) { return d.second; })) + [](const std::pair& d) { return d.second; })) std::cerr << piece_name(v->kingType) << " is not supported as kingType." << std::endl; } } diff --git a/src/piece.cpp b/src/piece.cpp index 1a48f80..c78075c 100644 --- a/src/piece.cpp +++ b/src/piece.cpp @@ -148,22 +148,22 @@ namespace { auto has_dir = [&](std::string s) { return std::find(directions.begin(), directions.end(), s) != directions.end(); }; - if (directions.size() == 0 || has_dir("ff") || has_dir("vv") || has_dir("rf") || has_dir("rv") || has_dir("fh") || has_dir("rh") || has_dir("hr")) - v[Direction(atom.first * FILE_NB + atom.second)] = distance; - if (directions.size() == 0 || has_dir("bb") || has_dir("vv") || has_dir("lb") || has_dir("lv") || has_dir("bh") || has_dir("lh") || has_dir("hr")) - v[Direction(-atom.first * FILE_NB - atom.second)] = distance; - if (directions.size() == 0 || has_dir("rr") || has_dir("ss") || has_dir("br") || has_dir("bs") || has_dir("bh") || has_dir("rh") || has_dir("hr")) - v[Direction(-atom.second * FILE_NB + atom.first)] = distance; - if (directions.size() == 0 || has_dir("ll") || has_dir("ss") || has_dir("fl") || has_dir("fs") || has_dir("fh") || has_dir("lh") || has_dir("hr")) - v[Direction(atom.second * FILE_NB - atom.first)] = distance; - if (directions.size() == 0 || has_dir("rr") || has_dir("ss") || has_dir("fr") || has_dir("fs") || has_dir("fh") || has_dir("rh") || has_dir("hl")) - v[Direction(atom.second * FILE_NB + atom.first)] = distance; - if (directions.size() == 0 || has_dir("ll") || has_dir("ss") || has_dir("bl") || has_dir("bs") || has_dir("bh") || has_dir("lh") || has_dir("hl")) - v[Direction(-atom.second * FILE_NB - atom.first)] = distance; - if (directions.size() == 0 || has_dir("bb") || has_dir("vv") || has_dir("rb") || has_dir("rv") || has_dir("bh") || has_dir("rh") || has_dir("hl")) - v[Direction(-atom.first * FILE_NB + atom.second)] = distance; - if (directions.size() == 0 || has_dir("ff") || has_dir("vv") || has_dir("lf") || has_dir("lv") || has_dir("fh") || has_dir("lh") || has_dir("hl")) - v[Direction(atom.first * FILE_NB - atom.second)] = distance; + if (directions.size() == 0 || has_dir("ff") || has_dir("vv") || has_dir("rf") || has_dir("rv") || has_dir("fh") || has_dir("rh") || has_dir("hr")) + v[step(y, x)] = distance; + if (directions.size() == 0 || has_dir("bb") || has_dir("vv") || has_dir("lb") || has_dir("lv") || has_dir("bh") || has_dir("lh") || has_dir("hr")) + v[step(-y, -x)] = distance; + if (directions.size() == 0 || has_dir("rr") || has_dir("ss") || has_dir("br") || has_dir("bs") || has_dir("bh") || has_dir("rh") || has_dir("hr")) + v[step(-x, y)] = distance; + if (directions.size() == 0 || has_dir("ll") || has_dir("ss") || has_dir("fl") || has_dir("fs") || has_dir("fh") || has_dir("lh") || has_dir("hr")) + v[step(x, -y)] = distance; + if (directions.size() == 0 || has_dir("rr") || has_dir("ss") || has_dir("fr") || has_dir("fs") || has_dir("fh") || has_dir("rh") || has_dir("hl")) + v[step(x, y)] = distance; + if (directions.size() == 0 || has_dir("ll") || has_dir("ss") || has_dir("bl") || has_dir("bs") || has_dir("bh") || has_dir("lh") || has_dir("hl")) + v[step(-x, -y)] = distance; + if (directions.size() == 0 || has_dir("bb") || has_dir("vv") || has_dir("rb") || has_dir("rv") || has_dir("bh") || has_dir("rh") || has_dir("hl")) + v[step(-y, x)] = distance; + if (directions.size() == 0 || has_dir("ff") || has_dir("vv") || has_dir("lf") || has_dir("lv") || has_dir("fh") || has_dir("lh") || has_dir("hl")) + v[step(y, -x)] = distance; } } // Reset state diff --git a/src/piece.h b/src/piece.h index 8d14dfd..38fdcc9 100644 --- a/src/piece.h +++ b/src/piece.h @@ -34,9 +34,9 @@ enum MoveModality {MODALITY_QUIET, MODALITY_CAPTURE, MOVE_MODALITY_NB}; struct PieceInfo { std::string name = ""; std::string betza = ""; - std::map steps[2][MOVE_MODALITY_NB] = {}; - std::map slider[2][MOVE_MODALITY_NB] = {}; - std::map hopper[2][MOVE_MODALITY_NB] = {}; + std::map steps[2][MOVE_MODALITY_NB] = {}; + std::map slider[2][MOVE_MODALITY_NB] = {}; + std::map hopper[2][MOVE_MODALITY_NB] = {}; }; struct PieceMap : public std::map { @@ -52,6 +52,22 @@ inline std::string piece_name(PieceType pt) { : pieceMap.find(pt)->second->name; } +inline DirectionCode step(int y, int x) { + return DirectionCode(y * WIDTH + x); +} + +inline Direction h_step(DirectionCode v) { + return Direction(((v + HWIDTH) & X_STEP_MASK) - HWIDTH); +} + +inline Direction v_step(DirectionCode v) { + return Direction((v - h_step(v)) / WIDTH); +} + +inline Direction board_step(DirectionCode v) { + return Direction(v_step(v) * FILE_NB + h_step(v)); +} + } // namespace Stockfish #endif // #ifndef PIECE_H_INCLUDED diff --git a/src/psqt.cpp b/src/psqt.cpp index d1a84d6..0732c4d 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -154,7 +154,7 @@ constexpr Score PBonus[RANK_NB][FILE_NB] = // Scale down slider value based on distance -int slider_fraction(std::map slider) { +int slider_fraction(std::map slider) { int s = 0; for (auto const& [_, limit] : slider) { s += limit == 0 ? 100 : 200 * std::min(limit + 1, 8) / 16; @@ -175,8 +175,8 @@ Value piece_value(Phase phase, PieceType pt) + (phase == MG ? 100 : 80) * pi->hopper[0][MODALITY_CAPTURE].size() + (phase == MG ? 85 : 60) * pi->hopper[0][MODALITY_QUIET].size() // Rook sliding directions are more valuable, especially in endgame - + (phase == MG ? 15 : 15) * std::count_if(pi->slider[0][MODALITY_CAPTURE].begin(), pi->slider[0][MODALITY_CAPTURE].end(), [](const std::pair& d) { return std::abs(d.first) == NORTH || std::abs(d.first) == 1; }) - + (phase == MG ? 30 : 50) * std::count_if(pi->slider[0][MODALITY_QUIET].begin(), pi->slider[0][MODALITY_QUIET].end(), [](const std::pair& d) { return std::abs(d.first) == NORTH || std::abs(d.first) == 1; }); + + (phase == MG ? 15 : 15) * std::count_if(pi->slider[0][MODALITY_CAPTURE].begin(), pi->slider[0][MODALITY_CAPTURE].end(), [](const std::pair& d) { return std::abs(board_step(d.first)) == NORTH || std::abs(board_step(d.first)) == 1; }) + + (phase == MG ? 30 : 50) * std::count_if(pi->slider[0][MODALITY_QUIET].begin(), pi->slider[0][MODALITY_QUIET].end(), [](const std::pair& d) { return std::abs(board_step(d.first)) == NORTH || std::abs(board_step(d.first)) == 1; }); return Value(v0 * exp(double(v0) / 10000)); } @@ -227,8 +227,8 @@ void init(const Variant* v) { const PieceInfo* pi = pieceMap.find(pt)->second; bool isSlider = pi->slider[0][MODALITY_QUIET].size() || pi->slider[0][MODALITY_CAPTURE].size() || pi->hopper[0][MODALITY_QUIET].size() || pi->hopper[0][MODALITY_CAPTURE].size(); - bool isPawn = !isSlider && pi->steps[0][MODALITY_QUIET].size() && !std::any_of(pi->steps[0][MODALITY_QUIET].begin(), pi->steps[0][MODALITY_QUIET].end(), [](const std::pair& d) { return d.first < SOUTH / 2; }); - bool isSlowLeaper = !isSlider && !std::any_of(pi->steps[0][MODALITY_QUIET].begin(), pi->steps[0][MODALITY_QUIET].end(), [](const std::pair& d) { return dist(d.first) > 1; }); + bool isPawn = !isSlider && pi->steps[0][MODALITY_QUIET].size() && !std::any_of(pi->steps[0][MODALITY_QUIET].begin(), pi->steps[0][MODALITY_QUIET].end(), [](const std::pair& d) { return board_step(d.first) < SOUTH / 2; }); + bool isSlowLeaper = !isSlider && !std::any_of(pi->steps[0][MODALITY_QUIET].begin(), pi->steps[0][MODALITY_QUIET].end(), [](const std::pair& d) { return dist(board_step(d.first)) > 1; }); // Scale slider piece values with board size if (isSlider) @@ -261,7 +261,7 @@ void init(const Variant* v) { // Increase leapers' value in makpong else if (v->makpongRule) { - if (std::any_of(pi->steps[0][MODALITY_CAPTURE].begin(), pi->steps[0][MODALITY_CAPTURE].end(), [](const std::pair& d) { return dist(d.first) > 1 && !d.second; })) + if (std::any_of(pi->steps[0][MODALITY_CAPTURE].begin(), pi->steps[0][MODALITY_CAPTURE].end(), [](const std::pair& d) { return dist(board_step(d.first)) > 1 && !d.second; })) score = make_score(mg_value(score) * 4200 / (3500 + mg_value(score)), eg_value(score) * 4700 / (3500 + mg_value(score))); } diff --git a/src/types.h b/src/types.h index b5812ab..e271faf 100644 --- a/src/types.h +++ b/src/types.h @@ -543,6 +543,12 @@ enum Direction : int { NORTH_WEST = NORTH + WEST }; +enum DirectionCode : int { + HWIDTH = 16, + WIDTH = 2*HWIDTH, + X_STEP_MASK = WIDTH - 1 +}; + enum File : int { #ifdef LARGEBOARDS FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_I, FILE_J, FILE_K, FILE_L,