2

私はチェスのアプリケーションに取り組んでおり、数百万の動きと位置を含む可能性のあるファイルであるオープニング ブックを作成する必要があります。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 正方形などの順番でビット配列に配置されます。

したがって、開始位置の場合、これは 32 x 1 + 16 x 3 + 12 x 5 + 4 x 6 = 164 ビットに相当します。

また、キャスリングが有効かどうかなどのさまざまなゲーム状況や、該当する場合はエンパッサン スクエアを定義するために、さらに 16 ビットが必要です。したがって、チェス盤の 1 つの位置を格納するには、約 180 ビット (または 23 バイト = 184 ビット) が必要です。

問題は、いくつかのビット単位の操作を実行する必要があるため、コードでこれを操作する方法と、ファイルに保存する方法を知りたいということです。どのデータ構造を使用する必要があるかを意味します。たとえば、最大 long (データ型) には 4 バイト = 64 ビットのみが含まれます。文字列の使用を避けたい。これをどのように進めるかについて、何らかのアイデアを提案できますか?

C#.Net、Framework 3.5 を使用しています。

4

4 に答える 4

6

チェス アプリケーションの場合、最も一般的に使用される構造は次のとおりです。

  • 各ビットがボード上の位置を表す ulong 64 ビット変数
  • ポーン、ナイトなどの色ごとに変数を分けます。

それで

ulong WHITE_PAWNS = xxx;
ulong BLACK_PAWNS = xxx;

これは最もストレージ効率の高いバージョンではありませんが、多くのことを非常に迅速に行うことができます。ポーンとナイトを一緒にするには、次のことができます

white_pawns_and_knights = WHITE_PAWNS | WHITE_KNIGHTS;

詳細については、こちらをご覧ください(他の場所の中でも)。

私は自分自身のチェス プログラムを (Delphi と一部 C# で) 作成しましたが、世の中にはたくさんの優れたプログラムがあります。これらは、私がこの件に関して保持していたブックマークです。

必要に応じて、ulongs の高速ビット操作用の C# ユニットも用意しています。ご希望の場合は、ivotops#gmail#com までメールでお問い合わせください。

残念ながら、時間が足りず、C# バージョンを完成させることはできませんでした ;-)

于 2012-10-12T08:09:42.850 に答える
3

オープニングブックの場合、このアプローチを使用できます。

すべてのボードについて、適切な 64 ビット ハッシュコードを生成します (google zobrist ハッシュ)

ハッシュコードをキーとして使用する辞書を用意します。

したがって、実際のボードは保管しません。ハッシュコードが一致する場合、同一のボードであると見なすことができます (64 ビットの衝突の可能性は、10 進数の 15 番目以降から始まります)。ボード上で最初の動きが許可されているかどうかの最終チェック テストとして、準備ができているかどうかを確認します。

次に、辞書全体を保存するコードを記述します。このコードはそれを行い、すべての列挙可能なコンテナーに対して機能します。

 // 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 ハッシュを生成する方法です (移動する側、キャスリング権、アンパッサンなどを含む)。

// 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;
}
于 2012-10-12T08:54:47.557 に答える
2

私はに行きBitArrayます。ビット操作に特化していますが、操作をラップします。

しかし、あなたのニーズは十分に専門化されているようで、自分でコレクションを実装するのに関連する可能性があります.

于 2012-10-12T07:46:32.070 に答える
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);
}
于 2012-10-12T07:47:50.927 に答える