为棋盘生成唯一标识符

时间:2014-05-30 20:16:14

标签: artificial-intelligence checksum chess

我正在寻找像棋盘的校验和之类的东西。我正在寻找一个动态编程或memoized解决方案是否适用于AI国际象棋棋手。唯一标识符将用于轻松检查两个板是否相等或用作阵列中的索引。谢谢你的帮助。

5 个答案:

答案 0 :(得分:2)

电路板位置广泛使用的校验和Zobrist signature

这是任何国际象棋位置的几乎唯一的索引号,要求两个相似的位置产生完全不同的索引。这些索引号用于更快,更节省空间的换位表/开卷书。

您需要一组随机生成的位串:

  • 每个广场每件一件;
  • 一个表示要移动的一方;
  • 四个用于铸造权利;
  • 八个有效的en-passant square(如果有)的文件。

如果您想获取特定位置的Zobrist哈希码,您必须xor链接到给定功能的所有随机数(详情:hereCorrectly Implementing Zobrist Hashing)。< / p>

起始位置:

[Hash for White Rook on a1] xor [White Knight on b1] xor ... ( all pieces )
... xor [White castling long] xor ... ( all castling rights )

XOR允许在移动的make / unkeke期间快速增量更新散列键。

现代国际象棋程序通常使用64位作为标准尺寸(见The Effect of Hash Signature Collisions in a Chess Program)。

当您评估√2 32 == 2 16 时,您可能会遇到32位哈希冲突。使用64位散列,您可以预期在大约2 32 或40亿个位置(birthday paradox)之后发生碰撞。

答案 1 :(得分:1)

如果您正在寻找校验和,通常的解决方案是Zobrist Hashing

如果您正在寻找真正的唯一标识符,那么通常的人类可读解决方案是Forsyth notation

对于非人类可读的唯一标识符,您可以使用四位在每个方块上存储该块的类型/颜色。将另外3位用于en-passant square,4位仍然允许使用castling,并且将其转为1位,并且每个电路板设置最终只有33个字节。

答案 2 :(得分:0)

如果两个游戏通过不同的移动或移动订单达到相同的配置,它们应该仍然“相等”。例如您不必区分哪个 pawn位于特定位置,只要位置相同即可。您似乎并不真正想要哈希,而是要唯一且正确地区分这些板状态。

一种方法是使用64x12 square-by-piecetype成员矩阵。您可以将其存储为位向量,然后比较矢量以进行检查。例如向量中的前64个地址可能会显示棋盘上的哪些位置包含棋子。接下来的64个展示位置包含骑士。您可以让前6个部分显示白色部分的成员资格,最后6部分显示黑色部分的成员资格。

二进制成员矩阵伪代码:

bool[] memberships = zeros(64*12);
move(pawn,a3,a2);

def move(piece,location,oldlocation):
    memberships(pawn,location) = 1;
    memberships(pawn,oldlocation) = 0;

这很麻烦,因为你必须小心如何实现它。例如确保每个玩家只有一个最大值。优点是它只需要768位来存储状态。

另一种方法是长度为64的整数向量,表示电路板位置的向量化地址。在这种情况下,前8个地址可能代表电路板第一行的状态。

非二进制成员矩阵伪代码:

half[] memberships = zeros(64);
memberships[8] = 1;        // white pawn at location a2
memberships[0] = 2;        // white rook at location a1
...
memberships[63] = 11;      // black knight at location g8
memberships[64] = 12;      // black rook at location h8

关于非二进制向量的好处是你没有那么多的自由来意外地将多个部分分配给一个位置。缺点是现在存储每个州的规模都比较大。进行相等比较时,较大的表示将较慢。 (在我的例子中,假设每个向量位置存储一个16位半字,我们得到64 * 16 = 1014位来存储一个状态,而不是二进制向量的768位)

无论哪种方式,您可能想要枚举每个棋子和棋盘位置。

enumerate piece {
    empty = 0;
    white_pawn = 1;
    white_rook = 2;
    ...
    black_knight = 11;
    black_rook = 12;
}
enumerate location {
    a1 = 0;
    ...
}

测试相等只是将两个向量进行比较。

答案 3 :(得分:0)

有64个方格。国际象棋中有十二个不同的数字可以占据一个正方形加上没有数字占据它的可能性。制作13.您需要4位来表示那些13(2 ^ 4 = 16)。所以你最终得到32个字节来明确地存储国际象棋棋盘。

如果你想简化处理,你可以存储64个字节,每个字节一个字节,因为字节更容易读写。

编辑:我已经在国际象棋上阅读了更多内容并得出以下结论:如果自上次捕获或典当移动以来所有以前的电路板也相同,则两块电路板只是相同的。这是因为三重复规则。如果第三次棋盘在游戏中看起来完全相同,则可以申请平局。因此,尽管在两场比赛中看到相同的棋盘,但是在一场比赛中可能被认为是不幸做出某一动作,以避免平局,而在另一场比赛中没有这样的危险。

取决于你,你想怎么做。由于要存储的可变数量的先前板,您将需要可变长度的唯一标识符。好吧,也许你放轻松,对此视而不见只是存储最后五个动作以直接检测可能导致第三次重复位置的重复动作,这是最常出现的原因。

如果你想用棋盘存储动作:有64x63 = 4032个可想象的动作(需要12位),但其中许多当然是非法的。如果我正确计数,则有1728个合法移动(例如A1-> A2 =合法,A1-> D2非法),这将适合11位。然而,我仍然会选择12位,因为通过将A1 / 1存储为A1-> A2而将62/63存储为H7-> H8,使解释变得尽可能容易。

然后有50个移动规则。你不必在这里存放动作。只有自上次捕获或典当以来的移动次数从0移动到50(足够了;它不管是50,51还是更重要)。所以另外六位。

最后:黑人还是白人的举动? Enpassantable pawn?可铸造的车?一些额外的位(或13个占用的扩展以节省一些位)。

再次编辑:所以如果你想用电路板与其他电子火柴进行比较,那么&#34;两块电路板只是相同的,如果自上次捕获或典当移动以来所有以前的电路板都是相同的&#34;适用。但是,如果你只是想在同一个游戏中检测到位置的重复,那么你只需要使用15个占用x 64个方格加一个位来移动它就可以了。

答案 4 :(得分:0)

您可以使用像md5,sha这样的校验和,只需将棋盘单元格作为文本传递,例如:

TKBQKBHT
........
........
........
tkbqkbht

获取生成文本的校验和。

一个到另一个板之间的校验和将是不同的,没有任何相关值,此时可能创建一个唯一的字符串(或位数组)是最好的方法:

TKBQKBHT........................tkbqkbht

因为它也很独特,很容易与其他人比较。