C# - 使用哪种数据结构 - 操纵和存储180位的国际象棋位置?

时间:2012-10-12 07:40:22

标签: c# bit-manipulation bitwise-operators chess

我正在制作一个国际象棋应用程序,需要创建一本开放书,一个文件 可能包含数百万个动作和位置。我们有64平方米,其中一些有 有些是空的,有些是空的。让我们用以下几位代表我们的作品 (使用霍夫曼编码技术)。

              White          Black
-Empty        0

-Pawn         110           100

-Rook         11111         11110

-Knight       10110         10101

-Bishop       10100         11100

-Queen        111010        111011

-King         101110        101111

在初始位置,我们有32个不同的部分,32个平方 空。为了提高效率,我必须将位置存储在连续位中。碎片将是 按顺序放置在位数组中,从a1方块开始,然后是a2方形,... a8, 那么b1,b2 ...... b8 square等等。

因此,对于起始位置,这相当于32 x 1 + 16 x 3 + 12 x 5 + 4 x 6 = 164位。

对于不同的游戏情况,还需要一些额外的16位,比如 是否启用了castling,如果适用,还定义了enpassant square。 所以我需要大约180位(或23字节= 184位)来存储单个位置 棋盘。

现在的问题是我必须执行一些按位操作,所以想知道 该方案如何在我的代码中操作它以及如何存储在文件中。手段 我应该使用哪种数据结构。例如最大长度(数据类型)将仅包含4 bytes = 64位。我想避免使用字符串。任何机构都可以提出任何想法 继续这个。

我正在使用C#.Net,Framework 3.5。

4 个答案:

答案 0 :(得分:6)

对于国际象棋应用程序,最常用的结构是这个;

  • 一个ulong 64位变量,其中每个位代表板上的位置
  • 每种颜色的单独变量pawns,knigths等。

所以

ulong WHITE_PAWNS = xxx;
ulong BLACK_PAWNS = xxx;

这不是最具存储效率的版本,但可以让您快速完成很多工作。要把棋子和骑士聚集在一起,你可以做到

white_pawns_and_knights = WHITE_PAWNS | WHITE_KNIGHTS;

详细了解here(以及其他地方)。

我写了一个我自己的国际象棋程序(在Delphi和部分C#中),那里有很多好东西。这些是我对这个主题的书签;

我还有一个C#单元,可以根据需要对ulongs进行快速位操作。如果您需要,请发送电子邮件至ivotops#gmail#com,并随时提出要求。

不幸的是我的时间太少而且从未完成过C#版本; - )

答案 1 :(得分:3)

对于一本开头书,你可以使用这种方法;

为每个电路板生成一个好的64位哈希码(google zobrist hashing)

有一个字典,您可以使用哈希码作为密钥。

所以你不存储实际的板子。如果哈希码匹配,你可以假设它是一个相同的板(64位冲突的机会在十五进制之后开始)。作为最终检查测试,是否允许在棋盘上进行开球,你就可以了。

然后编写代码以保存整个字典。此代码执行此操作并适用于所有可枚举容器;

 // save
 using (var fs = new FileStream(fileName, FileMode.Create))
        {
            var bw = new BinaryWriter(fs);
            foreach (var kvp in this)
            {
                kvp.Key.AddToStream(bw);
                kvp.Value.AddToStream(bw);
            }
        }

 // load
 using (var fs = new FileStream(fileName, FileMode.Open))
            {
                var fslen = fs.Length;
                var br = new BinaryReader(fs);
                while (fs.Position < fslen)
                {
                    var k = new Pattern();
                    var v = new BestMove();
                    k.ReadFromStream(br);
                    v.ReadFromStream(br);
                    Add(k, v);
                }
            }

这是如何生成64位Zobrist哈希值。这些应该存储在一些永久性的地方,以便以后可以在本答案底部显示的代码方法中重用它们。在这里,它们被存储为静态类的静态成员:

internal static class HashKeys
{
    internal static readonly UInt64[,] PieceSquareKeys = new UInt64[64,16];
    internal static readonly UInt64[] EnPassantKeys = new UInt64[64];
    internal static readonly UInt64 SideToMoveKey;
    internal static readonly UInt64 WhiteCastlingKingSideKey;
    internal static readonly UInt64 WhiteCastlingQueenSideKey;
    internal static readonly UInt64 BlackCastlingKingSideKey;
    internal static readonly UInt64 BlackCastlingQueenSideKey;

    // Constructor - generates pseudo-random numbers for Zobrist hashing.
    // The use of a CSPRNG is a good guaranteee of genuinely random numbers.
    static HashKeys()
    {
        RNGCryptoServiceProvider randomGenerator = new RNGCryptoServiceProvider();
        byte[] eightRandomBytes = new byte[8];

        try
        {
            for (Int32 i1 = 0; i1 < 64; i1++)
            {
                for (Int32 i2 = 0; i1 < 16; i1++)
                {
                    randomGenerator.GetBytes(eightRandomBytes);
                    PieceSquareKeys[i1, i2] = BitConverter.ToUInt64(eightRandomBytes, 0);
                }
                randomGenerator.GetBytes(eightRandomBytes);
                EnPassantKeys[i1] = BitConverter.ToUInt64(eightRandomBytes, 0);
            }

            randomGenerator.GetBytes(eightRandomBytes);
            SideToMoveKey = BitConverter.ToUInt64(eightRandomBytes, 0);
            randomGenerator.GetBytes(eightRandomBytes);
            WhiteCastlingKingSideKey = BitConverter.ToUInt64(eightRandomBytes, 0);

            randomGenerator.GetBytes(eightRandomBytes);
            WhiteCastlingQueenSideKey = BitConverter.ToUInt64(eightRandomBytes, 0);

            randomGenerator.GetBytes(eightRandomBytes);
            BlackCastlingKingSideKey = BitConverter.ToUInt64(eightRandomBytes, 0);

            randomGenerator.GetBytes(eightRandomBytes);
            BlackCastlingQueenSideKey = BitConverter.ToUInt64(eightRandomBytes, 0);
        }
        finally
        {
            randomGenerator.Dispose();
        }
    }
}

这是如何生成代表董事会职位的64位Zobrish哈希(包括移动方,铸造权,en-passant等:

// Init Zobrist position hash, used in transposition table and draw detection.
// This will be incrementally updated during move make/unmake.
internal static UInt64 InitPositionHash(byte[] squares, ComplexProperties propertyStore, byte sideToMove)
{
    UInt64 positionHash = 0;

    // Calculate piece/square hashes.
    for (Int32 i = 0; i < 64; i++)
    {
        if (squares[i] != Constants.EMPTY)
        {
            positionHash ^= HashKeys.PieceSquareKeys[i, squares[i]];
        }
    }

    // Add side to move only if Black.
    if (sideToMove == Constants.BLACK)
    {
        positionHash ^= HashKeys.SideToMoveKey;
    }

    // Add en-passant square if applicable.
    if (propertyStore.EpSquare != 0)
    {
        positionHash ^= HashKeys.EnPassantKeys[propertyStore.EpSquare];
    }

    // White castling.
    switch (propertyStore.WhiteCastlingStatus)
    {
        case Constants.EnumCastlingStatus.CAN_CASTLE_BOTH:
             positionHash ^= HashKeys.WhiteCastlingKingSideKey;
             positionHash ^= HashKeys.WhiteCastlingQueenSideKey;
             break;
        case Constants.EnumCastlingStatus.CAN_CASTLE_OO:
             positionHash ^= HashKeys.WhiteCastlingKingSideKey;
             break;
        case Constants.EnumCastlingStatus.CAN_CASTLE_OOO:
             positionHash ^= HashKeys.WhiteCastlingQueenSideKey;
             break;
        case Constants.EnumCastlingStatus.CANT_CASTLE:
             break;
        default:
             Debug.Assert(false, "White has an invalid castling status!");
             break;
    }

    // Black castling.
    switch (propertyStore.BlackCastlingStatus)
    {
        case Constants.EnumCastlingStatus.CAN_CASTLE_BOTH:
             positionHash ^= HashKeys.BlackCastlingKingSideKey;
             positionHash ^= HashKeys.BlackCastlingQueenSideKey;
             break;
        case Constants.EnumCastlingStatus.CAN_CASTLE_OO:
             positionHash ^= HashKeys.BlackCastlingKingSideKey;
             break;
        case Constants.EnumCastlingStatus.CAN_CASTLE_OOO:
             positionHash ^= HashKeys.BlackCastlingQueenSideKey;
             break;
        case Constants.EnumCastlingStatus.CANT_CASTLE:
             break;
        default:
             Debug.Assert(false, "Black has an invalid castling status!");
             break;
    }

    return positionHash;
}

答案 2 :(得分:2)

我会去BitArray。它专门用于位操作,但包含操作。

但听起来你的需求足够专业,可能与你自己实现的收藏相关。

答案 3 :(得分:0)

在运行时,您可以使用BitArray类。它非常有效,因为您可以存储自定义大小的位数组

<强>更新

将BitArray保存到文件:

private void save()
{
    BitArray bits = new BitArray(164, true);
    byte[] bytes = new byte[21]; // 21 * 8 bits = 168 bits (last 4 bits are unused)
    bits.CopyTo(bytes, 0);
    for (int i = 0; i < 21; i++)
    {
        bytes[i] = ToByte(bits, i);
    }

    // now save your byte array to file
}

private byte ToByte(BitArray bits, int start) 
{
    int sum = 0;

    if (bits[start])
        sum += 8;

    if (bits[start + 1])
        sum += 4;

    if (bits[start + 2])
        sum += 2;

    if (bits[start + 3])
        sum += 1;

    return Convert.ToByte(sum);
}