2012-07-16 36 views
0

我目前正在嘗試在C#中開發一個國際象棋引擎。 感謝上一篇帖子給予我的詳細解答,我現在正在研究如何將棋盤系統應用於我的遊戲結構。 原則,我想再次,一些面向對象的設計適用於發動機的新概念,但現在我心裏有一些懸而未決的問題:Bitboard Wrapper - 性能和內存分配

  • 我想實現一個棋盤結構傾斜在一個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; 
     } 
    } 
} 

,在這裏,我想救類...

這是我的移動類:

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; 
     } 
    } 
} 

,這裏是我的抽象類海賊王:

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(); 
    } 
} 

我想強調的是,這裏的正確答案了一些非常重要的因素是速度優化和良好的面向對象設計。

感謝所有:)

回答

2

在你的問題的最後,指定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 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; 
}