我正在制作一个国际象棋应用程序,需要创建一本开放书,一个文件 可能包含数百万个动作和位置。我们有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。
答案 0 :(得分:6)
对于国际象棋应用程序,最常用的结构是这个;
所以
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);
}