我目前正在尝试用C#开发一个国际象棋引擎。 感谢我在之前的帖子中给出的详细答案,我现在正在研究如何将比特板系统应用到我的游戏结构中。 原则上,我再次尝试将一些面向对象的设计应用于这个新的引擎概念,但现在我有一些未解决的问题:
我想实现一个倾斜于UInt64字段的位板结构来抽象这个概念,可能提供像GetFirstBit()或Shift(..)甚至PopCount(..)这样的方法,但我不知道知道这将如何影响性能和内存分配。由于引用副本,或者对于一个如此小的对象Heap会使事情变得复杂,会更好地提高性能吗?
我甚至会实现一个索引器来输入正常数组中的单个位,这会浪费资源还是一个好主意(对于国际象棋引擎)?
我正在尝试尽量减少对项目的更改,但我意识到我的所有片段层次结构和我的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;
}
}
}
这里是我想要保存的课程......
这是我的Move类:
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();
}
}
我想强调一下,正确答案的一些非常重要的因素是速度优化和面向对象的设计。
感谢所有人:)
答案 0 :(得分: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类,但它应该是一个struct。我的测试没有显示出显着的差异:
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;
}