0

我目前正在尝试用 C# 开发一个国际象棋引擎。感谢在我之前的帖子中给我的详细答案,我现在正在研究如何将位板系统应用于我的游戏结构。原则上,我再次尝试将一些面向对象的设计应用于这个新的引擎概念,但现在我有一些未解决的问题:

  • 我想实现一个基于 UInt64 字段的位板结构来抽象该概念,可能提供 GetFirstBit() 或 Shift(..) 甚至 PopCount(..) 之类的方法,但我不知道这会如何影响性能和内存分配。由于引用副本,一个类会更好地提高性能,还是对于一个如此小的对象堆只会使事情复杂化?

  • 我什至会实现一个索引器来像普通数组一样输入单个位,这会浪费资源还是一个好主意(对于国际象棋引擎)?

  • 我试图尽量减少对我的项目的更改,但我意识到我的所有片段层次结构以及我的 Move 和 Square 类都将被搁置并且永远不会使用更多......我应该放弃那个设计,还是我可以重用这些类不知何故?

这是我想在我的引擎中实现的原型:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Chess_Engine___NOGUI
{
    public struct BitBoard
    {
        public UInt64 bitBoard;

        public BitBoard(UInt64 board)
        {
            bitBoard = board;
        }

        public static implicit operator BitBoard(UInt64 board)
        {
            return new BitBoard(board);
        }
        public static implicit operator UInt64(BitBoard board)
        {
            return board.bitBoard;
        }

        public static BitBoard operator <<(BitBoard board, int shift)
        {
            return board.bitBoard << shift;
        }
        public static BitBoard operator >>(BitBoard board, int shift)
        {
            return board.bitBoard >> shift;
        }
        public static BitBoard operator &(BitBoard a, BitBoard b)
        {
            return a.bitBoard & b.bitBoard;
        }
        public static BitBoard operator |(BitBoard a, BitBoard b)
        {
            return a.bitBoard | b.bitBoard;
        }
        public static BitBoard operator ^(BitBoard a, BitBoard b)
        {
            return a.bitBoard ^ b.bitBoard;
        }
        public static BitBoard operator ~(BitBoard a)
        {
            return ~a.bitBoard;
        }
    }
}

在这里,我想保存的课程......

这是我的移动课:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Chess_Engine___NOGUI
{
    class NullMove : Move
    {
        public NullMove()
            : base(null, null, null)
        {

        }
    }

    class Move
    {
        public string Algebraic
        {
            get
            {
                return ToAlgebraic();
            }
        } // JUST FOR DEBUG
        public Square FromSquare { get; set; }
        public Square ToSquare { get; set; }
        public Piece PieceMoved { get; set; }
        public Piece PieceCaptured { get; set; }
        public PieceType PiecePromoted { get; set; }
        public bool HasPromoted
        {
            get
            {
                return PiecePromoted != PieceType.None;
            }
        }
        public bool IsEnpassant { get; set; }
        public bool HasCaptured
        {
            get
            {
                if (PieceCaptured != null)
                    return true;
                else
                    return false;
            }
        }
        public bool IsCastling
        {
            get
            {
                return IsLongCastling || IsShortCastling;
            }
        }
        public bool IsLongCastling
        {
            get
            {
                if (PieceMoved is King)
                {
                    if (FromSquare.X - ToSquare.X == 2)
                        return true;
                    else
                        return false;
                }
                else
                {
                    return false;
                }

            }
        }
        public bool IsShortCastling
        {
            get
            {
                if (PieceMoved is King)
                {
                    if (FromSquare.X - ToSquare.X == -2)
                           return true;
                    else
                        return false;
                }
                else
                {
                    return false;
                }
            }
        }
        public bool IsCheck { get; set; }
        public bool IsCheckMate { get; set; }
        public bool IsDoublePawnPush
        {
            get
            {
                if (PieceMoved.Type == PieceType.Pawn)
                    if (!HasCaptured)
                        if (ToSquare.X == FromSquare.X)
                            if (SideMove == PieceColor.White)
                            {
                                if (ToSquare.Y - FromSquare.Y == 2)
                                    return true;
                            }
                            else
                            {
                                if (ToSquare.Y - FromSquare.Y == -2)
                                    return true;
                            }
                return false;
            }
        }
        public PieceColor SideMove
        {
            get
            {
                return PieceMoved.Color;
            }
        }


        public Piece RookMoved { get; set; }
        public Square KingPosition { get; set; }
        public Square RookPosition { get; set; }
        public float Score { get; set; }

        public Move(Square fromSquare, Square toSquare, Piece pieceMoved, PieceType piecePromoted = PieceType.None)
        {
            this.FromSquare = fromSquare;
            this.ToSquare = toSquare;
            this.PieceMoved = pieceMoved;
            this.PiecePromoted = piecePromoted;
        }

        public static bool operator ==(Move a, Move b)
        {
            return a.Equals(b);
        }
        public static bool operator !=(Move a, Move b)
        {
            return !a.Equals(b);
        }
        public override bool Equals(object other)
        {
            if (other is Move)
            {
                Move compare = (Move)other;
                return (this.FromSquare == compare.FromSquare && this.ToSquare == compare.ToSquare);
            }
            else
            {
                return false;
            }
        }
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public string ToAlgebraic()
        {
           StringBuilder algebraic = new StringBuilder();

            if (IsCastling) // se e` una mossa di arrocco
            {
                if (IsShortCastling)
                    algebraic.Append("O-O"); // arrocco corto
                else
                    algebraic.Append("O-O-O"); // arrocco lungo
            }
            else
            {
                algebraic.Append(FromSquare.ToAlgebraic());

                if (HasCaptured)
                    algebraic.Append("x"); // cattura

                algebraic.Append(ToSquare.ToAlgebraic());
            }

            if (HasPromoted)
                algebraic.Append(PiecePromoted.GetInitial());

            if (IsCheck)
                if (IsCheckMate)
                    algebraic.Append("#"); // scacco matto
                else
                    algebraic.Append("+"); // scacco

            return algebraic.ToString();
        }
    }
}

这是我的 Square 课程:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Chess_Engine___NOGUI
{
    sealed class Square
    {
        public int X { get; set; }
        public int Y { get; set; }

        public Square(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }

        public static implicit operator Square(string str)
        {
            // converte la notazione algebrica (es. a1) in coordinate decimali
            str = str.ToLower(); // converte la stringa in minuscolo
            int x = (int)(str[0] - 'a');
            int y = (int)(str[1] - '1');

            return new Square(x, y);
        }

        public static bool operator ==(Square a, Square b)
        {
            if (System.Object.ReferenceEquals(a, b))
            {
                return true;
            }

            if (((object)a == null) || ((object)b == null))
            {
                return false;
            }

            if (a is Square)
            {
                Square compare = (Square)b;
                return (a.X == compare.X && a.Y == compare.Y);
            }
            else
            {
                return false;
            }
        }
        public static bool operator !=(Square a, Square b)
        {
            return !(a == b);
        }

        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public string ToAlgebraic()
        {
            string str = "";
            str += (char)(this.X + 97);
            str += (this.Y + 1).ToString();

            return str;
        }
    }
}

这是我的抽象 Piece 类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Chess_Engine___NOGUI
{
    public enum PieceType { None, Pawn, Knight, Bishop, Rook, Queen, King }
    public enum PieceColor { None, White, Black }

    public static class Extensions
    {
        public static PieceColor GetOpposite(this PieceColor color)
        {
            if (color == PieceColor.White)
                return PieceColor.Black;
            if (color == PieceColor.Black)
                return PieceColor.White;
            else
                return PieceColor.None;
        }

        public static char GetInitial(this PieceType type)
        {
            switch (type)
            {
                case PieceType.Bishop:
                    return 'B';
                case PieceType.King:
                    return 'K';
                case PieceType.Knight:
                    return 'N';
                case PieceType.Pawn:
                    return 'P';
                case PieceType.Queen:
                    return 'Q';
                case PieceType.Rook:
                    return 'R';
                default:
                    return ' ';
            }
        }

    }

    abstract class Piece
    {
        public char Notation { get; set; }
        protected List<Move> movesList;
        public Square startingSquare { get; set; }
        public Square square { get; protected set; }
        public Square lastSquare { get; set; }
        public PieceType Type { get; set; }
        public PieceColor Color { get; set; }
        public virtual bool AlreadyBeenMoved
        {
            get
            {
                return square != startingSquare;
            }
        }

        public Piece(Square square, PieceColor color)
        {
            this.startingSquare = square;
            this.square = square;
            this.lastSquare = square;
            this.Color = color;
            this.movesList = new List<Move>();
        }

        public void Move(Square destination)
        {
            square = destination; // aggiorna la posizione attuale
        }
        public bool ShouldUpdateMoves()
        {
            if (lastSquare == square) // se il pezzo non si e` mosso
            {
                if (movesList.Count > 0)
                    return false;
            }
            else
            {
                lastSquare = square;
                movesList.Clear();
            }
            return true;
        }

        public abstract List<Move> GetMoves();
    }
}

我想强调的是,这里获得正确答案的一些非常重要的因素是速度优化和面向对象的设计。

谢谢大家 :)

4

1 回答 1

2

在问题的最后,您指定了两个从根本上相互冲突的因素。我的建议是你专注于其中一个。要么您重视良好的 OO 设计,要么您重视良好的性能。你真的不能两者兼得。

为了回答您第一个要点中的问题,我个人不使用任何 OO 来查找(例如)位板中的第一个有效位:

private const UInt64 DEBRUIJN64 = 0x07EDD5E59A4E28C2;
private static readonly Byte[] INDEX64 = {63,  0, 58,  1, 59, 47, 53,  2,
                                          60, 39, 48, 27, 54, 33, 42,  3,
                                          61, 51, 37, 40, 49, 18, 28, 20,
                                          55, 30, 34, 11, 43, 14, 22,  4,
                                          62, 57, 46, 52, 38, 26, 32, 41,
                                          50, 36, 17, 19, 29, 10, 13, 21,
                                          56, 45, 25, 31, 35, 16,  9, 12,
                                          44, 24, 15,  8, 23,  7,  6,  5};

// De Bruijn Multiplication, see http://chessprogramming.wikispaces.com/BitScan
// Don't use this if bitmap = 0!
internal static Byte BitScanForward(UInt64 bitmap)
{
    Debug.Assert(bitmap != 0);
    return INDEX64[((ulong)((long)bitmap & -(long)bitmap) * DEBRUIJN64) >> 58];
}

我不使用 Piece 或 Square 类:

// Piece identifiers, 4 bits each.
// Useful bitwise properties of this numbering scheme:
// white = 0..., black = 1..., sliding = .1.., nonsliding = .0..
// rank/file sliding pieces = .11., diagonally sliding pieces = .1.1
// pawns and kings (without colour bits), are < 3
// major pieces (without colour bits), are > 5
// minor and major pieces (without colour bits set), are > 2.
internal const byte EMPTY = 0;                  //  00000000
internal const byte WHITE_PAWN = 1;             //  00000001
internal const byte WHITE_KING = 2;             //  00000010
internal const byte WHITE_KNIGHT = 3;           //  00000011
internal const byte WHITE_BISHOP = 5;           //  00000101
internal const byte WHITE_ROOK = 6;             //  00000110
internal const byte WHITE_QUEEN = 7;            //  00000111
internal const byte BLACK_PAWN = 9;             //  00001001
internal const byte BLACK_KING = 10;            //  00001010
internal const byte BLACK_KNIGHT = 11;          //  00001011
internal const byte BLACK_BISHOP = 13;          //  00001101
internal const byte BLACK_ROOK = 14;            //  00001110
internal const byte BLACK_QUEEN = 15;           //  00001111

我确实使用了 Move 类,但它可能应该是一个结构。我的测试没有显示出显着差异:

internal sealed class Move
{
    internal Move()
    {
    }

    internal Move(byte squareFrom, byte squareTo, byte pieceMoved, byte pieceCaptured, byte piecePromoted)
    {
        this.SquareFrom = squareFrom;
        this.SquareTo = squareTo;
        this.PieceMoved = pieceMoved;
        this.PieceCaptured = pieceCaptured;
        this.PiecePromoted = piecePromoted;
    }

    // The FROM square.
    // Bits 1-3 are the board file, bits 4-6 are the board rank, bits 7-8 are unused.
    internal readonly byte SquareFrom;

    // The TO square.
    // Bits 1-3 are the board file, bits 4-6 are the board rank, bits 7-8 are unused.
    internal readonly byte SquareTo;

    // The MOVED piece.
    // Bits 1-3 are the piece type, bit 4 is the piece colour, bits 5-8 are unused.
    internal readonly byte PieceMoved;

    // The CAPTURED piece.
    // Bits 1-3 are the piece type, bit 4 is the piece colour, bits 5-8 are unused.
    internal readonly byte PieceCaptured;

    // The PROMOTED piece.
    // Bits 1-3 are the piece type, bit 4 is the piece colour, bits 5-8 are unused.
    // NB Overloaded to represent EN-PASSANT capture if promoted piece is a pawn.
    // NB Overloaded to represent CASTLING if promoted piece is a king.
    internal readonly byte PiecePromoted;
}
于 2012-07-20T23:57:21.690 回答