首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基于C++国际象棋引擎(魔法)的移动生成

基于C++国际象棋引擎(魔法)的移动生成
EN

Code Review用户
提问于 2022-03-03 23:17:38
回答 1查看 195关注 0票数 1

我目前正在用C++编写一个基于位板的棋盘引擎,我最近完成了移动生成。我使用移动查找表,在应用程序启动时对滑动和非滑动块进行预计算。对于滑动块,我实现了神奇的位板,它们在MagicBitboards类中得到处理(在这里应该非常不相关,所以我没有将它们包括在问题中)。

move生成器现在成功地通过了多个perft()测试位置,但是它仍然非常慢,我觉得实现在代码方面到处都是(而且比它应该的时间长得多)。我试图尽可能地总结和概括函数,但总有理由让所有额外的函数单独存在(大多数情况下,如果我在同一个函数中打包了更相似但不同的功能,代码就会变得完全不可读)。

大多数CPU调用在check_for_legality()函数中被浪费掉,因为其他的一切基本上都只是在预先计算的数组和一些按位操作中查找移动。

如果您有任何建议,我的代码可以使更多的可读性或表现力,请分享您的见解!非常感谢!

在深度5处初始位置的perft()结果:

代码语言:javascript
复制
a2a4: 1
a2a3: 1
b1a3: 1
b1c3: 1
b2b4: 1
b2b3: 1
c2c3: 1
c2c4: 1
d2d3: 1
d2d4: 1
e2e4: 1
e2e3: 1
f2f4: 1
f2f3: 1
g2g4: 1
g1h3: 1
g1f3: 1
g2g3: 1
h2h3: 1
h2h4: 1

Depth: 1
Total nodes: 20
Total time: 1ms/0.001s

a2a4: 20
a2a3: 20
b1a3: 20
b1c3: 20
b2b4: 20
b2b3: 20
c2c3: 20
c2c4: 20
d2d3: 20
d2d4: 20
e2e4: 20
e2e3: 20
f2f4: 20
f2f3: 20
g2g4: 20
g1h3: 20
g1f3: 20
g2g3: 20
h2h3: 20
h2h4: 20

Depth: 2
Total nodes: 400
Total time: 1ms/0.001s

a2a4: 420
a2a3: 380
b1a3: 400
b1c3: 440
b2b4: 421
b2b3: 420
c2c3: 420
c2c4: 441
d2d3: 539
d2d4: 560
e2e4: 600
e2e3: 599
f2f4: 401
f2f3: 380
g2g4: 421
g1h3: 400
g1f3: 440
g2g3: 420
h2h3: 380
h2h4: 420

Depth: 3
Total nodes: 8902
Total time: 15ms/0.015s

a2a4: 9329
a2a3: 8457
b1a3: 8885
b1c3: 9755
b2b4: 9332
b2b3: 9345
c2c3: 9272
c2c4: 9744
d2d3: 11959
d2d4: 12435
e2e4: 13160
e2e3: 13134
f2f4: 8929
f2f3: 8457
g2g4: 9328
g1h3: 8881
g1f3: 9748
g2g3: 9345
h2h3: 8457
h2h4: 9329

Depth: 4
Total nodes: 197281
Total time: 291ms/0.291s

a2a4: 217832
a2a3: 181046
b1a3: 198572
b1c3: 234656
b2b4: 216145
b2b3: 215255
c2c3: 222861
c2c4: 240082
d2d3: 328511
d2d4: 361790
e2e4: 405385
e2e3: 402988
f2f4: 198473
f2f3: 178889
g2g4: 214048
g1h3: 198502
g1f3: 233491
g2g3: 217210
h2h3: 181044
h2h4: 218829

Depth: 5
Total nodes: 4865609
Total time: 6752ms/6.752s

H:

代码语言:javascript
复制
#ifndef MOVEGENERATOR_H
#define MOVEGENERATOR_H

#include "Bitboard.h"
#include "globals.h"
#include "GameState.h"
#include "MagicBitboards.h"

#include <array>
#include <vector>

namespace Chess_Engine 
{
    class MoveGenerator
    {
        using NonMagicAttackTable = std::array<Bitboard, 64>; 
        using BishopAttackTable = std::array<std::array<Bitboard, 512>, 64>; 
        using RookAttackTable = std::array<std::array<Bitboard, 4096>, 64>; 

    public:
    
        MoveGenerator();
        virtual ~MoveGenerator();

        std::vector<Move> generate_legal_moves(const GameState& game_state); // returns all legal moves for a given position
        std::vector<Move> generate_pseudo_legal_moves(const GameState& game_state); // returns all pseudo-legal moves for a given position
    
    private:
            
        // checks a single move for legality and returns result
        bool check_for_legality(const GameState& game_state, Move move, BoardState board_state, const Bitboard& all_enemy_attacks, const Bitboard& king_position, const Bitboard& king_attack_paths, const Bitboard& direct_king_attackers, const Bitboard& pinned, const Bitboard& pinners, std::unordered_map<int32_t, Bitboard>* allowed_pinned_move_path_for_index, bool king_in_check); 

        bool all_attackers_captured(Move move, const Bitboard& direct_king_attackers, bool white_to_move); 
        bool all_attack_paths_blocked(Move move, const Bitboard& king_attack_paths);

        // methods for filling move vector
        void add_king_moves(std::vector<Move>* move_list, const Bitboard& attacking_pieces, const Bitboard& allied_pieces, const Bitboard& enemy_pieces, bool castle_short, bool castle_long);
        void add_knight_moves(std::vector<Move>* move_list, const Bitboard& attacking_pieces, const Bitboard& allied_pieces, const Bitboard& enemy_pieces);
        void add_pawn_moves(std::vector<Move>* move_list, const Bitboard& attacking_pieces, const Bitboard& allied_pieces, const Bitboard& enemy_pieces, bool is_white_to_move, bool en_passant_possible, uint32_t en_passant_target_square);
        void add_bishop_moves(std::vector<Move>* move_list, const Bitboard& attacking_pieces, const Bitboard& allied_pieces, const Bitboard& enemy_pieces);
        void add_rook_moves(std::vector<Move>* move_list, const Bitboard& attacking_pieces, const Bitboard& allied_pieces, const Bitboard& enemy_pieces);

        // methods for filling threat bitboard
        void add_king_attacks(Bitboard* enemy_attacks, const Bitboard& attacking_pieces);
        // -> also adds knights to direct_king_attackers if necessary
        void add_knight_attacks(Bitboard* enemy_attacks, const Bitboard& attacking_pieces, Bitboard* direct_king_attackers, const Bitboard& king_position); 
        // -> also adds pawns to direct_king_attackers if necessary
        void add_pawn_attacks(Bitboard* enemy_attacks, const Bitboard& attacking_pieces, Bitboard* direct_king_attackers, const Bitboard& king_position, bool is_white_to_move); 
        void add_bishop_attacks(Bitboard* enemy_attacks, const Bitboard& attacking_pieces, const Bitboard& allied_pieces, const Bitboard& enemy_pieces, const Bitboard& king_position);
        void add_rook_attacks(Bitboard* enemy_attacks, const Bitboard& attacking_pieces, const Bitboard& allied_pieces, const Bitboard& enemy_pieces, const Bitboard& king_position);
        
        // returns a bitboard of all attacks threatened by the opponent (ignoring the king for sliding pieces)
        Bitboard get_enemy_attack_bitboard(const GameState& game_state, Bitboard* direct_king_attackers);

        // methods for getting king attack info for sliding pieces
        void get_bishop_king_attack_info(const GameState& game_state, const Bitboard& bishop_positions, const Bitboard& enemy_pieces, const Bitboard& allied_pieces, const Bitboard& king_position, Bitboard* attack_paths, Bitboard* direct_attackers, Bitboard* pinned, Bitboard* pinners);
        void get_rook_king_attack_info(const GameState& game_state, const Bitboard& bishop_positions, const Bitboard& enemy_pieces, const Bitboard& allied_pieces, const Bitboard& king_position, Bitboard* attack_paths, Bitboard* direct_attackers, Bitboard* pinned, Bitboard* pinners);
        
        // gets information on where the king gets attacked from and where the attackers and pinned pieces are
        void get_attack_on_king_info(const GameState& game_state, Bitboard* attack_paths, Bitboard* attackers, Bitboard* pinned, Bitboard* pinners); 

        void initialise_king_attack_table();
        void initialise_pawn_move_table_white();
        void initialise_pawn_attack_table_white();
        void initialise_pawn_move_table_black();
        void initialise_pawn_attack_table_black();
        void initialise_knight_attack_table();

        NonMagicAttackTable king_attack_table;
        NonMagicAttackTable pawn_move_table_white;
        NonMagicAttackTable pawn_attack_table_white;
        NonMagicAttackTable pawn_move_table_black;
        NonMagicAttackTable pawn_attack_table_black;
        NonMagicAttackTable knight_attack_table;

        MagicBitboards sliding_attack_generator;
    };
}
#endif

MoveGenerator.cpp:

代码语言:javascript
复制
#include "MoveGenerator.h"

#include "GameState.h"
#include "Bitboard.h"
#include "Move.h"
#include "globals.h"

#include <cstdint>
#include <iostream>
#include <unordered_map>
#include <vector>
#include <functional>

namespace Chess_Engine
{

    using NonMagicAttackTable = std::array<Bitboard, 64>; 

    MoveGenerator::MoveGenerator()
    {
        // this is where the static lookup tables get initialised 
        
        // non-sliding
        initialise_king_attack_table();
        initialise_pawn_move_table_white();
        initialise_pawn_attack_table_white();
        initialise_pawn_move_table_black();
        initialise_pawn_attack_table_black();
        initialise_knight_attack_table();
    }

    MoveGenerator::~MoveGenerator()
    {
    }

    std::vector<Move> MoveGenerator::generate_legal_moves(const GameState& game_state)
    {
        std::vector<Move> pseudo_legal_moves = generate_pseudo_legal_moves(game_state);
        std::vector<Move> legal_moves;

        Bitboard attack_paths, direct_king_attackers, pinned, pinners;
        std::unordered_map<int32_t, Bitboard> allowed_pinned_move_path_for_index;

        BoardState board_state = game_state.get_board_state();
        Bitboard all_enemy_attacks = get_enemy_attack_bitboard(game_state, &direct_king_attackers);

        Bitboard king_position = game_state.is_white_to_move() ? board_state.king_position_white : board_state.king_position_black;
        bool king_in_check = all_enemy_attacks & king_position;

        get_attack_on_king_info(game_state, &attack_paths, &direct_king_attackers, &pinned, &pinners);

        // test all pseudo legal moves for legality
        for (const Move& move : pseudo_legal_moves)
        {
            if (check_for_legality(game_state, move, board_state, all_enemy_attacks, king_position, attack_paths, direct_king_attackers, pinned, pinners, &allowed_pinned_move_path_for_index, king_in_check))
            {
                legal_moves.push_back(move);
            }
        }

        return legal_moves;
    }

    bool MoveGenerator::check_for_legality(const GameState& game_state, Move move, BoardState board_state, const Bitboard& all_enemy_attacks, const Bitboard& king_position, const Bitboard& king_attack_paths, const Bitboard& direct_king_attackers, const Bitboard& pinned, const Bitboard& pinners, std::unordered_map<int32_t, Bitboard>* allowed_pinned_move_path_for_index, bool king_in_check)
    {
        int32_t from_index = move.get_from_index();
        int32_t to_index = move.get_to_index();

        bool is_king_move = Bitboard(1UL << from_index) & king_position;
        bool is_pinned = Bitboard(1UL << from_index) & pinned;
        bool white_to_move = game_state.is_white_to_move();
        int32_t flags = move.get_flags();

        // is the moving piece a king?
        if (is_king_move)   
        {   
            // is the destination square attacked by the opponent?
            if (Bitboard::is_attacked(to_index, all_enemy_attacks))
            {
                return false;
            }
        }
        // is the king in check?
        else if (king_in_check)
        {
            // if the king is in check and the moving piece is pinned, the move is invalid
            if (is_pinned)
            {
                return false;
            }

            // in the case of a double-check, all non-king moves are unvalid
            if (direct_king_attackers.bit_count() > 1)
            {
                return false;
            }

            // are neither all pieces attacking the king getting captured, nor all attack paths getting blocked?
            if (!(all_attackers_captured(move, direct_king_attackers, white_to_move)) && !(all_attack_paths_blocked(move, king_attack_paths)))
            {
                return false;
            }
        }
        // is the moving piece pinned?
        else if (is_pinned)
        {
            // if allowed_pinned_move_path for given origin square is not already calculated
            if (allowed_pinned_move_path_for_index->find(from_index) == allowed_pinned_move_path_for_index->end())
            {
                Bitboard allowed_pinned_move_path = 0;
                int32_t direction = 0;

                bool same_file = (king_position.get_index() % 8) == (from_index % 8);
                bool same_rank = (king_position.get_index() / 8) == (from_index / 8);

                // rook movement
                if (same_file || same_rank)
                {
                    if (same_file) // on the same file
                    {
                        direction = from_index > king_position.get_index() ? 8 : -8;
                    }
                    else // on the same rank
                    {
                        direction = from_index > king_position.get_index() ? 1 : -1;
                    }
                }
                // bishop movement
                else
                {
                    int32_t horizontal_direction = ((king_position.get_index() % 8) < (from_index % 8)) ? 1 : -1; // horizontal index shift 
                    int32_t vertical_direction = ((king_position.get_index() / 8) > (from_index / 8)) ? -8 : 8; // vertical index shift
                    direction = horizontal_direction + vertical_direction; // total shift
                }

                // contruct allowed_pinned_move_path
                for (int32_t index = king_position.get_index() + direction; !(Bitboard(1UL << (index - direction)) & pinners) ; index += direction)
                {
                    allowed_pinned_move_path |= Bitboard(1UL << index);
                }

                allowed_pinned_move_path_for_index->insert({from_index, allowed_pinned_move_path});
            }

            // if the pinned piece is moving outside of the allowed path, the move is invalid
            if (!(Bitboard(1UL << to_index) & allowed_pinned_move_path_for_index->at(from_index)))
            {
                return false;
            }
        }

        // castling
        if (move.get_flags() == Move::KINGSIDE_CASTLE)
        {
            Bitboard castling_blockers = all_enemy_attacks | game_state.get_board_state().get_all_positions();

            if (Bitboard::is_attacked(king_position.get_index() - 1, castling_blockers) || 
                Bitboard::is_attacked(king_position.get_index() - 2, castling_blockers) ||
                king_in_check)
            {
                return false;
            }
        }
        else if (flags == Move::QUEENSIDE_CASTLE)
        {
            Bitboard castling_blockers = all_enemy_attacks | game_state.get_board_state().get_all_positions();

            if (Bitboard::is_attacked(king_position.get_index() + 1, castling_blockers) || 
                Bitboard::is_attacked(king_position.get_index() + 2, castling_blockers) ||
                Bitboard::is_attacked(king_position.get_index() + 3, game_state.get_board_state().get_all_positions()) ||
                king_in_check)
            {
                return false;
            }
        }

        // make sure en-passant captures do not expose the king by capturing the last enemy piece blocking the attack (very rare)
        if (flags == Move::EN_PASSANT_CAPTURE && !((to_index % 8 == 0) || (to_index % 8 == 7))) 
        {
            Bitboard nearest_horizontal_pieces;
            int32_t capture_shift = white_to_move ? -8 : 8;

            // all pieces, except the moving pawn
            Bitboard all_pieces = board_state.get_all_positions() & ~Bitboard(1UL << from_index);

            // search to the right
            for (int32_t index = to_index + capture_shift - 1; (index % 8) != 7; --index)
            {
                if (Bitboard(1UL << index) & all_pieces)
                {
                    nearest_horizontal_pieces |= Bitboard(1UL << index);
                    break;
                }
            }

            // search to the left
            for (int32_t index = to_index + capture_shift + 1; (index % 8) != 0; ++index)
            {
                if (Bitboard(1UL << index) & all_pieces)
                {
                    nearest_horizontal_pieces |= Bitboard(1UL << index);
                    break;
                }
            }

            Bitboard enemy_rooks = white_to_move ? board_state.rook_positions_black : board_state.rook_positions_white;
            Bitboard enemy_queens = white_to_move ? board_state.queen_positions_black : board_state.queen_positions_white;
            Bitboard enemy_horizontal_attackers = enemy_rooks | enemy_queens;

            // if one nearest horizontal piece is the own king and the other is an enemy horizontal attacker, the capture exposes the king
            if ((nearest_horizontal_pieces & king_position) && (nearest_horizontal_pieces & enemy_horizontal_attackers))
            {
                return false;
            }
        }

        return true;
    }

    bool MoveGenerator::all_attackers_captured(Move move, const Bitboard& direct_king_attackers, bool white_to_move)
    {
        if (Bitboard(1UL << move.get_to_index()) & direct_king_attackers)
        {
            return true;
        }

        if (move.get_flags() == Move::EN_PASSANT_CAPTURE)
        {
            int32_t capture_shift = white_to_move ? -8 : 8;
            int32_t capture_index = move.get_to_index() + capture_shift;

            if (Bitboard(1UL << capture_index) & direct_king_attackers)
            {
                return true;
            }
        }

        return false;
    }

    bool MoveGenerator::all_attack_paths_blocked(Move move, const Bitboard& king_attack_paths)
    {
        if (Bitboard(1UL << move.get_to_index()) & king_attack_paths)
        {
            return true;
        }

        return false;
    }

    std::vector<Move> MoveGenerator::generate_pseudo_legal_moves(const GameState& game_state)
    {
        std::vector<Move> pseudo_legal_moves;

        BoardState board_state = game_state.get_board_state();
        bool white_to_move = game_state.is_white_to_move();

        Bitboard allied_pieces = white_to_move ? board_state.get_all_positions_white() : board_state.get_all_positions_black();
        Bitboard enemy_pieces = white_to_move ? board_state.get_all_positions_black() : board_state.get_all_positions_white();

        std::array<Bitboard*, 6> allied_bitboards = white_to_move ? board_state.white_bitboards : board_state.black_bitboards;

        // process king attacks
        bool castle_short = white_to_move ? game_state.get_white_castle_short() : game_state.get_black_castle_short();
        bool castle_long = white_to_move ? game_state.get_white_castle_long() : game_state.get_black_castle_long();
        Bitboard king_position = *allied_bitboards[5];
        add_king_moves(&pseudo_legal_moves, king_position, allied_pieces, enemy_pieces, castle_short, castle_long);

        // process pawn attacks
        Bitboard pawn_positions = *allied_bitboards[0];
        add_pawn_moves(&pseudo_legal_moves, pawn_positions, allied_pieces, enemy_pieces, white_to_move, game_state.is_en_passant_possible(), game_state.get_en_passant_square());

        // process knight attacks
        Bitboard knight_positions = *allied_bitboards[2];
        add_knight_moves(&pseudo_legal_moves, knight_positions, allied_pieces, enemy_pieces);

        // process bishop attacks
        Bitboard bishop_positions = *allied_bitboards[3];
        add_bishop_moves(&pseudo_legal_moves, bishop_positions, allied_pieces, enemy_pieces);

        // process rook attacks
        Bitboard rook_positions = *allied_bitboards[1];
        add_rook_moves(&pseudo_legal_moves, rook_positions, allied_pieces, enemy_pieces);

        // process queen attacks (a queen moves like rook + bishop, so she doesn't need her own method)
        Bitboard queen_attacks = *allied_bitboards[4];
        add_bishop_moves(&pseudo_legal_moves, queen_attacks, allied_pieces, enemy_pieces);
        add_rook_moves(&pseudo_legal_moves, queen_attacks, allied_pieces, enemy_pieces);

        return pseudo_legal_moves;
    }

    void MoveGenerator::add_king_moves(std::vector<Move>* move_list, const Bitboard& attacking_pieces, const Bitboard& allied_pieces, const Bitboard& enemy_pieces, bool castle_short, bool castle_long)
    {
        Bitboard attacking = attacking_pieces;

        while (attacking)
        {
            int32_t from_index = Bitboard::isolate_LS1B(attacking).get_index();
            Bitboard attacks = king_attack_table[from_index];

            attacks &= ~allied_pieces;
            Bitboard capture_attacks = attacks & enemy_pieces;
            Bitboard quiet_attacks = attacks & ~enemy_pieces;

            while (capture_attacks)
            {
                int32_t to_index = Bitboard::isolate_LS1B(capture_attacks).get_index();
                move_list->push_back(Move(from_index, to_index, Move::CAPTURE));
                capture_attacks.reset_LS1B();
            }
            while (quiet_attacks)
            {
                int32_t to_index = Bitboard::isolate_LS1B(quiet_attacks).get_index();
                move_list->push_back(Move(from_index, to_index, Move::QUIET));
                quiet_attacks.reset_LS1B();
            }

            if (castle_short)
            {
                move_list->push_back(Move(from_index, from_index - 2, Move::KINGSIDE_CASTLE));
            }
            if (castle_long)
            {
                move_list->push_back(Move(from_index, from_index + 2, Move::QUEENSIDE_CASTLE));
            }

            attacking.reset_LS1B(); 
        }
    }

    void MoveGenerator::add_knight_moves(std::vector<Move>* move_list, const Bitboard& attacking_pieces, const Bitboard& allied_pieces, const Bitboard& enemy_pieces)
    {
        Bitboard attacking = attacking_pieces;

        while (attacking)
        {
            int32_t from_index = Bitboard::isolate_LS1B(attacking).get_index();
            Bitboard attacks = knight_attack_table[from_index];

            attacks &= ~allied_pieces;
            Bitboard capture_attacks = attacks & enemy_pieces;
            Bitboard quiet_attacks = attacks & ~enemy_pieces;

            while (capture_attacks)
            {
                int32_t to_index = Bitboard::isolate_LS1B(capture_attacks).get_index();
                move_list->push_back(Move(from_index, to_index, Move::CAPTURE));
                capture_attacks.reset_LS1B();
            }
            while (quiet_attacks)
            {
                int32_t to_index = Bitboard::isolate_LS1B(quiet_attacks).get_index();
                move_list->push_back(Move(from_index, to_index, Move::QUIET));
                quiet_attacks.reset_LS1B();
            }

            attacking.reset_LS1B(); 
        }
    }

    void MoveGenerator::add_pawn_moves(std::vector<Move>* move_list, const Bitboard& attacking_pieces, const Bitboard& allied_pieces, const Bitboard& enemy_pieces, bool is_white_to_move, bool en_passant_possible, uint32_t en_passant_target_square)
    {
        Bitboard attacking = attacking_pieces;
        NonMagicAttackTable pawn_move_table = is_white_to_move ? pawn_move_table_white : pawn_move_table_black;
        NonMagicAttackTable pawn_attack_table = is_white_to_move ? pawn_attack_table_white : pawn_attack_table_black;

        while (attacking)
        {
            int32_t from_index = Bitboard::isolate_LS1B(attacking).get_index();

            Bitboard moves = pawn_move_table[from_index]; // get quiet forward moves

            moves &= (~allied_pieces & ~enemy_pieces);

            int32_t move_index_shift = is_white_to_move ? 8 : -8;

            while (moves)
            {
                int32_t to_index = Bitboard::isolate_LS1B(moves).get_index();

                // if pawn is moving two squares
                if (to_index == (from_index + 2*move_index_shift))
                {
                    // if in between square is empty
                    if (!(Bitboard(1UL << (from_index + move_index_shift)) & Bitboard(allied_pieces | enemy_pieces)))
                    {
                        move_list->push_back(Move(from_index, to_index, Move::DOUBLE_PAWN_PUSH));
                    }
                }
                // if pawn moves to top or bottom rank
                else if ((to_index >= 56 && to_index <= 63) || (to_index >= 0 && to_index <= 7))
                {
                    move_list->push_back(Move(from_index, to_index, Move::KNIGHT_PROMOTION));
                    move_list->push_back(Move(from_index, to_index, Move::BISHOP_PROMOTION));
                    move_list->push_back(Move(from_index, to_index, Move::ROOK_PROMOTION));
                    move_list->push_back(Move(from_index, to_index, Move::QUEEN_PROMOTION));
                }
                else
                {
                    move_list->push_back(Move(from_index, to_index, Move::QUIET));
                }

                moves.reset_LS1B();
            }

            Bitboard attacks = pawn_attack_table[from_index]; // get diagonal capture moves

            // en-passant
            if (en_passant_possible)
            {
                if (Bitboard(1UL << en_passant_target_square) & attacks)
                {
                    move_list->push_back(Move(from_index, en_passant_target_square, Move::EN_PASSANT_CAPTURE));
                }
            }

            attacks &= enemy_pieces;

            while (attacks)
            {
                int32_t to_index = Bitboard::isolate_LS1B(attacks).get_index();

                if ((to_index >= 56 && to_index <= 63) || (to_index >= 0 && to_index <= 7))
                {
                    move_list->push_back(Move(from_index, to_index, Move::KNIGHT_PROMOTION_CAPTURE));
                    move_list->push_back(Move(from_index, to_index, Move::BISHOP_PROMOTION_CAPTURE));
                    move_list->push_back(Move(from_index, to_index, Move::ROOK_PROMOTION_CAPTURE));
                    move_list->push_back(Move(from_index, to_index, Move::QUEEN_PROMOTION_CAPTURE));
                }
                else
                {
                    move_list->push_back(Move(from_index, to_index, Move::CAPTURE));
                }

                attacks.reset_LS1B();
            }

            attacking.reset_LS1B(); 
        }
    }

    void MoveGenerator::add_bishop_moves(std::vector<Move>* move_list, const Bitboard& attacking_pieces, const Bitboard& allied_pieces, const Bitboard& enemy_pieces)
    {
        Bitboard attacking = attacking_pieces;

        while (attacking)
        {
            int32_t from_index = Bitboard::isolate_LS1B(attacking).get_index();
            Bitboard attacks = sliding_attack_generator.get_bishop_attacks(from_index, allied_pieces | enemy_pieces);

            attacks &= ~allied_pieces;
            Bitboard capture_attacks = attacks & enemy_pieces;
            Bitboard quiet_attacks = attacks & ~enemy_pieces;

            while (capture_attacks)
            {
                int32_t to_index = Bitboard::isolate_LS1B(capture_attacks).get_index();
                move_list->push_back(Move(from_index, to_index, Move::CAPTURE));
                capture_attacks.reset_LS1B();
            }
            while (quiet_attacks)
            {
                int32_t to_index = Bitboard::isolate_LS1B(quiet_attacks).get_index();
                move_list->push_back(Move(from_index, to_index, Move::QUIET));
                quiet_attacks.reset_LS1B();
            }

            attacking.reset_LS1B(); 
        }
    }

    void MoveGenerator::add_rook_moves(std::vector<Move>* move_list, const Bitboard& attacking_pieces, const Bitboard& allied_pieces, const Bitboard& enemy_pieces)
    {
        Bitboard attacking = attacking_pieces;

        while (attacking)
        {
            int32_t from_index = Bitboard::isolate_LS1B(attacking).get_index();
            Bitboard attacks = sliding_attack_generator.get_rook_attacks(from_index, allied_pieces | enemy_pieces);

            attacks &= ~allied_pieces;
            Bitboard capture_attacks = attacks & enemy_pieces;
            Bitboard quiet_attacks = attacks & ~enemy_pieces;

            while (capture_attacks)
            {
                int32_t to_index = Bitboard::isolate_LS1B(capture_attacks).get_index();
                move_list->push_back(Move(from_index, to_index, Move::CAPTURE));
                capture_attacks.reset_LS1B();
            }
            while (quiet_attacks)
            {
                int32_t to_index = Bitboard::isolate_LS1B(quiet_attacks).get_index();
                move_list->push_back(Move(from_index, to_index, Move::QUIET));
                quiet_attacks.reset_LS1B();
            }

            attacking.reset_LS1B(); 
        }
    }

    void MoveGenerator::add_king_attacks(Bitboard* enemy_attacks, const Bitboard& attacking_pieces)
    {
        Bitboard attacking = attacking_pieces;

        while (attacking)
        {
            int32_t from_index = Bitboard::isolate_LS1B(attacking).get_index();
            Bitboard attacks = king_attack_table[from_index];

            *enemy_attacks |= attacks;

            attacking.reset_LS1B(); 
        }
    }

    void MoveGenerator::add_knight_attacks(Bitboard* enemy_attacks, const Bitboard& attacking_pieces, Bitboard* direct_king_attackers, const Bitboard& king_position)
    {
        Bitboard attacking = attacking_pieces;

        while (attacking)
        {
            int32_t from_index = Bitboard::isolate_LS1B(attacking).get_index();
            Bitboard attacks = knight_attack_table[from_index];

            if (attacks & king_position)
            {
                *direct_king_attackers |= Bitboard(1UL << from_index);
            }

            *enemy_attacks |= attacks;

            attacking.reset_LS1B(); 
        }
    }

    void MoveGenerator::add_pawn_attacks(Bitboard* enemy_attacks, const Bitboard& attacking_pieces, Bitboard* direct_king_attackers, const Bitboard& king_position, bool is_white_to_move)
    {
        Bitboard attacking = attacking_pieces;

        while (attacking)
        {
            int32_t from_index = Bitboard::isolate_LS1B(attacking).get_index();
            Bitboard attacks;

            if (is_white_to_move)
            {
                attacks |= pawn_attack_table_black[from_index];
            }
            else
            {
                attacks |= pawn_attack_table_white[from_index];
            }

            if (attacks & king_position)
            {
                *direct_king_attackers |= Bitboard(1UL << from_index);
            }

            *enemy_attacks |= attacks;

            attacking.reset_LS1B(); 
        }
    }

    void MoveGenerator::add_bishop_attacks(Bitboard* enemy_attacks, const Bitboard& attacking_pieces, const Bitboard& allied_pieces, const Bitboard& enemy_pieces, const Bitboard& king_position)
    {
        Bitboard attacking = attacking_pieces;

        while (attacking)
        {
            int32_t from_index = Bitboard::isolate_LS1B(attacking).get_index();
            Bitboard attacks = sliding_attack_generator.get_bishop_attacks(from_index, (allied_pieces ^ king_position) | enemy_pieces);

            *enemy_attacks |= attacks;

            attacking.reset_LS1B(); 
        }
    }

    void MoveGenerator::add_rook_attacks(Bitboard* enemy_attacks, const Bitboard& attacking_pieces, const Bitboard& allied_pieces, const Bitboard& enemy_pieces, const Bitboard& king_position)
    {
        Bitboard attacking = attacking_pieces;

        while (attacking)
        {
            int32_t from_index = Bitboard::isolate_LS1B(attacking).get_index();
            Bitboard attacks = sliding_attack_generator.get_rook_attacks(from_index, (allied_pieces ^ king_position) | enemy_pieces);

            *enemy_attacks |= attacks;

            attacking.reset_LS1B(); 
        }
    }

    Bitboard MoveGenerator::get_enemy_attack_bitboard(const GameState& game_state, Bitboard* direct_king_attackers)
    {
        Bitboard all_attacks;

        BoardState board_state = game_state.get_board_state();
        bool white_to_move = game_state.is_white_to_move();

        Bitboard allied_pieces = white_to_move ? board_state.get_all_positions_white() : board_state.get_all_positions_black();
        Bitboard enemy_pieces = white_to_move ? board_state.get_all_positions_black() : board_state.get_all_positions_white();

        Bitboard king_position = white_to_move ? board_state.king_position_white : board_state.king_position_black;

        Bitboard enemy_king_position = white_to_move ? board_state.king_position_black : board_state.king_position_white;
        add_king_attacks(&all_attacks, enemy_king_position);

        Bitboard enemy_pawn_positions = white_to_move ? board_state.pawn_positions_black : board_state.pawn_positions_white;
        add_pawn_attacks(&all_attacks, enemy_pawn_positions, direct_king_attackers, king_position, white_to_move);

        Bitboard enemy_knight_positions = white_to_move ? board_state.knight_positions_black : board_state.knight_positions_white;
        add_knight_attacks(&all_attacks, enemy_knight_positions, direct_king_attackers, king_position);

        Bitboard enemy_bishop_positions = white_to_move ? board_state.bishop_positions_black : board_state.bishop_positions_white;
        add_bishop_attacks(&all_attacks, enemy_bishop_positions, allied_pieces, enemy_pieces, king_position);

        Bitboard enemy_rook_positions = white_to_move ? board_state.rook_positions_black : board_state.rook_positions_white;
        add_rook_attacks(&all_attacks, enemy_rook_positions, allied_pieces, enemy_pieces, king_position);

        Bitboard enemy_queen_positions = white_to_move ? board_state.queen_positions_black : board_state.queen_positions_white;
        add_bishop_attacks(&all_attacks, enemy_queen_positions, allied_pieces, enemy_pieces, king_position);
        add_rook_attacks(&all_attacks, enemy_queen_positions, allied_pieces, enemy_pieces, king_position);

        return all_attacks;
    }

    void MoveGenerator::get_attack_on_king_info(const GameState& game_state, Bitboard* attack_paths, Bitboard* direct_attackers, Bitboard* pinned, Bitboard* pinners)
    {
        BoardState board_state = game_state.get_board_state();
        bool white_to_move = game_state.is_white_to_move();

        Bitboard allied_pieces = white_to_move ? board_state.get_all_positions_white() : board_state.get_all_positions_black();
        Bitboard enemy_pieces = white_to_move ? board_state.get_all_positions_black() : board_state.get_all_positions_white();

        Bitboard king_position = white_to_move ? board_state.king_position_white : board_state.king_position_black;

        Bitboard enemy_bishop_positions = white_to_move ? board_state.bishop_positions_black : board_state.bishop_positions_white;
        get_bishop_king_attack_info(game_state, enemy_bishop_positions, enemy_pieces, allied_pieces, king_position, attack_paths, direct_attackers, pinned, pinners);

        Bitboard enemy_rook_positions = white_to_move ? board_state.rook_positions_black : board_state.rook_positions_white;
        get_rook_king_attack_info(game_state, enemy_rook_positions, enemy_pieces, allied_pieces, king_position, attack_paths, direct_attackers, pinned, pinners);

        Bitboard enemy_queen_positions = white_to_move ? board_state.queen_positions_black : board_state.queen_positions_white;
        get_bishop_king_attack_info(game_state, enemy_queen_positions, enemy_pieces, allied_pieces, king_position, attack_paths, direct_attackers, pinned, pinners);
        get_rook_king_attack_info(game_state, enemy_queen_positions, enemy_pieces, allied_pieces, king_position, attack_paths, direct_attackers, pinned, pinners);
    }

    void MoveGenerator::get_bishop_king_attack_info(const GameState& game_state, const Bitboard& bishop_positions, const Bitboard& enemy_pieces, const Bitboard& allied_pieces, const Bitboard& king_position, Bitboard* attack_paths, Bitboard* direct_attackers, Bitboard* pinned, Bitboard* pinners)
    {
        Bitboard enemy_bishop_positions = bishop_positions;

        while (enemy_bishop_positions)
        {
            int32_t from_index = Bitboard::isolate_LS1B(enemy_bishop_positions).get_index();
            Bitboard attacks = sliding_attack_generator.get_bishop_attacks(from_index, enemy_pieces);
            Bitboard king_attack_path;

            // if king is theoretically attacked
            if (attacks & king_position)
            {
                // calculate attack path
                int32_t horizontal_direction = ((king_position.get_index() % 8) < (from_index % 8)) ? -1 : +1; // horizontal index shift 
                int32_t vertical_direction = ((king_position.get_index() / 8) > (from_index / 8)) ? +8 : -8; // vertical index shift
                int32_t direction = horizontal_direction + vertical_direction; // total shift

                // loop through attack path and save it
                for (int32_t index = from_index + direction; index != king_position.get_index(); index += direction)
                {
                    king_attack_path |= (1UL << index);
                }

                Bitboard blockers = king_attack_path & allied_pieces; // all allied pieces blocking the path

                if (blockers.bit_count() == 0)
                {
                    *direct_attackers |= Bitboard(1UL << from_index); // add attacker to all direct attackers
                    *attack_paths |= king_attack_path; // add attack path to all attack paths
                }
                else if (blockers.bit_count() == 1)
                {
                    *pinned |= blockers; // add blocker to pinned pieces
                    *pinners |= Bitboard(1UL << from_index); // add attacker to pinning pieces
                }
            }

            enemy_bishop_positions.reset_LS1B(); 
        }
    }


    void MoveGenerator::get_rook_king_attack_info(const GameState& game_state, const Bitboard& rook_positions, const Bitboard& enemy_pieces, const Bitboard& allied_pieces, const Bitboard& king_position, Bitboard* attack_paths, Bitboard* direct_attackers, Bitboard* pinned, Bitboard* pinners)
    {
        Bitboard enemy_rook_positions = rook_positions;

        while (enemy_rook_positions)
        {
            int32_t from_index = Bitboard::isolate_LS1B(enemy_rook_positions).get_index();
            Bitboard attacks = sliding_attack_generator.get_rook_attacks(from_index, enemy_pieces);
            Bitboard king_attack_path;

            // if king is theoretically attacked
            if (attacks & king_position)
            {
                int32_t direction = 0;

                // calculate attack path
                if ((from_index % 8) == (king_position.get_index() % 8)) // on the same file
                {
                    if (from_index > king_position.get_index())
                    {
                        direction = -8;
                    }
                    else
                    {
                        direction = 8;
                    }
                }
                else // on the same rank
                {
                    if (from_index > king_position.get_index())
                    {
                        direction = -1;
                    }
                    else
                    {
                        direction = 1;
                    }
                }

                // loop through attack path and save it
                for (int32_t index = from_index + direction; index != king_position.get_index(); index += direction)
                {
                    king_attack_path |= Bitboard(1UL << index);
                }

                Bitboard blockers = king_attack_path & allied_pieces; // all allied pieces blocking the path

                if (blockers.bit_count() == 0)
                {
                    *direct_attackers |= Bitboard(1UL << from_index); // add attacker to all direct attackers
                    *attack_paths |= king_attack_path; // add attack path to all attack paths
                }
                else if (blockers.bit_count() == 1)
                {
                    *pinned |= blockers; // add blocker to pinned pieces
                    *pinners |= Bitboard(1UL << from_index); // add attacker to pinning pieces
                }
            }

            enemy_rook_positions.reset_LS1B(); 
        }
    }

    void MoveGenerator::initialise_king_attack_table()
    {
        for (int32_t square = 0; square < 64; ++square)
        {
            Bitboard attacks;

            attacks |= Bitboard(1UL << (square + 9)) & Bitboard::mask_rank[0] & Bitboard::mask_file[7]; // up -> left
            attacks |= Bitboard(1UL << (square + 8)) & Bitboard::mask_rank[0]; // up
            attacks |= Bitboard(1UL << (square + 7)) & Bitboard::mask_rank[0] & Bitboard::mask_file[0]; // up -> right
            attacks |= Bitboard(1UL << (square + 1)) & Bitboard::mask_file[7]; // left
            attacks |= Bitboard(1UL << (square - 1)) & Bitboard::mask_file[0]; // right
            attacks |= Bitboard(1UL << (square - 7)) & Bitboard::mask_rank[7] & Bitboard::mask_file[7]; // down -> left
            attacks |= Bitboard(1UL << (square - 8)) & Bitboard::mask_rank[7]; // down
            attacks |= Bitboard(1UL << (square - 9)) & Bitboard::mask_rank[7] & Bitboard::mask_file[0]; // down -> right

            king_attack_table[square] = attacks;
        }
    }

    void MoveGenerator::initialise_pawn_move_table_white()
    {
        for (int32_t square = 0; square < 64; ++square)
        {
            Bitboard attacks;

            attacks |= Bitboard(1UL << (square + 8)) & Bitboard::mask_rank[0]; // up by one

            if (square <= 15 && square >= 8)
            {
                attacks |= (1UL << (square + 16)); // up by two
            }

            pawn_move_table_white[square] = attacks;
        }
    }

    void MoveGenerator::initialise_pawn_attack_table_white()
    {
        for (int32_t square = 0; square < 64; ++square)
        {
            Bitboard attacks;

            attacks |= Bitboard(1UL << (square + 9)) & Bitboard::mask_file[7] & Bitboard::mask_rank[0]; // diagonal left
            attacks |= Bitboard(1UL << (square + 7)) & Bitboard::mask_file[0] & Bitboard::mask_rank[0]; // diagonal right

            pawn_attack_table_white[square] = attacks;
        }
    }

    void MoveGenerator::initialise_pawn_move_table_black()
    {
        for (int32_t square = 0; square < 64; ++square)
        {
            Bitboard attacks;

            attacks |= Bitboard(1UL << (square - 8)) & Bitboard::mask_rank[7]; // up by one

            if (square <= 55 && square >= 48)
            {
                attacks |= (1UL << (square - 16)); // up by two
            }

            pawn_move_table_black[square] = attacks;
        }
    }

    void MoveGenerator::initialise_pawn_attack_table_black()
    {
        for (int32_t square = 0; square < 64; ++square)
        {
            Bitboard attacks;

            attacks |= Bitboard(1UL << (square - 9)) & Bitboard::mask_file[0] & Bitboard::mask_rank[7]; // diagonal right
            attacks |= Bitboard(1UL << (square - 7)) & Bitboard::mask_file[7] & Bitboard::mask_rank[7]; // diagonal left

            pawn_attack_table_black[square] = attacks;
        }
    }

    void MoveGenerator::initialise_knight_attack_table()
    {
        for (int32_t square = 0; square < 64; ++square)
        {
            Bitboard attacks;

            attacks |= Bitboard(1UL << (square + 17)) & Bitboard::mask_rank[0] & Bitboard::mask_rank[1] & Bitboard::mask_file[7]; // 2 up -> 1 left
            attacks |= Bitboard(1UL << (square + 15)) & Bitboard::mask_rank[0] & Bitboard::mask_rank[1] & Bitboard::mask_file[0]; // 2 up -> 1 right
            attacks |= Bitboard(1UL << (square + 10)) & Bitboard::mask_file[6] & Bitboard::mask_file[7] & Bitboard::mask_rank[0]; // 2 left -> 1 up
            attacks |= Bitboard(1UL << (square - 6)) & Bitboard::mask_file[6] & Bitboard::mask_file[7] & Bitboard::mask_rank[7]; // 2 left -> 1 down
            attacks |= Bitboard(1UL << (square - 15)) & Bitboard::mask_rank[6] & Bitboard::mask_rank[7] & Bitboard::mask_file[7];  // 2 down -> 1 left
            attacks |= Bitboard(1UL << (square - 17)) & Bitboard::mask_rank[6] & Bitboard::mask_rank[7] & Bitboard::mask_file[0]; // 2 down -> 1 right
            attacks |= Bitboard(1UL << (square + 6)) & Bitboard::mask_file[0] & Bitboard::mask_file[1] & Bitboard::mask_rank[0]; // 2 right -> 1 up
            attacks |= Bitboard(1UL << (square - 10)) & Bitboard::mask_file[0] & Bitboard::mask_file[1] & Bitboard::mask_rank[7]; // 2 right -> 1 down

            knight_attack_table[square] = attacks;
        }
    }
}
EN

回答 1

Code Review用户

发布于 2022-03-04 22:00:21

  • check_for_legality确实太复杂了。这似乎(不能肯定地说,没有剖析),盲目地做假合法的举动和测试国王的暴露后,应该是更快。当然,铸造是一个特殊的情况。在任何情况下,具有11个参数的方法都可以肯定地表明设计有问题。
  • 弃尸的合法性看上去不正确。b1 (或b8)正方形被攻击并不会使女王身边的铸物失效。此外,这里对king_in_check的测试也是多余的。我们已经知道国王没有受到控制。
  • 我不明白为什么add_*_moves应该关心capture_attacksquiet_attacks
  • all_attack_paths_blocked实现是反惯用的.如果(条件){返回真;}返回假;说返回条件是很长的路;
  • generate_pseudo_legal_moves觉得应该是private
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/274621

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档