我正在为一个国际象棋变体引擎调试我的换位表,其中可以放置各个部分(即最初不在板上)。我需要知道我经常遇到关键的碰撞。我正在保存每个表索引中的片段列表以及通常的哈希数据。我确定两个位置是否相等的简单解决方案是转换失败,因为我正在线性地比较两个位置。
请不要建议我应该以电路板为中心而不是以电脑为中心存储。由于可放置和捕获的碎片的独特性,我必须存储碎片清单。这些状态中的碎片就像它们占据了重叠且无位置的位置。 请查看件存储方式的说明。
// [Piece List]
//
// Contents: The location of the pieces.
// Values 0-63 are board indexes; -2 is dead; -1 is placeable
// Structure: Black pieces are at indexes 0-15
// White pieces are at indexes 16-31
// Within each set of colors the pieces are arranged as following:
// 8 Pawns, 2 Knights, 2 Bishops, 2 Rooks, 1 Queen, 1 King
// Example: piece[15] = 6 means the black king is on board index 6
// piece[29] = -2 means the white rook is dead
char piece[32];
当以不同的顺序移动棋子时,换位发生,但最终结果是相同的棋盘位置。例如,以下职位相同:
1) first rook on A1; second rook on D7
2) first rook on D7; second rook on A1
以下是非优化通用算法;内循环类似于另一个general problem,但增加了约束,0-63中的值只会发生一次(即每平方只有一个)。
for each color:
for each piece type:
are all pieces in the same position, disregarding transpositions?
以下比较因换位而无效。我需要的是一种检测转置相等的方法,只报告实际上不同的位置。
bool operator==(const Position &b)
{
for (int i = 0; i < 32; i++)
if (piece[i] != b.piece[i])
return false;
return true;
}
性能/内存是一个考虑因素,因为每回合获得超过100K的点击(其中键是相等的),典型的表有100万个项目。从此以后,我正在寻找比复制和排序列表更快的东西。
答案 0 :(得分:8)
对计算机国际象棋进行了大量研究,为位置创建独特哈希的方法是一个众所周知的问题,几乎每个国际象棋引擎都使用通用解决方案。
你需要做的是使用Zobrist Hashing创建一个独特的(不是真正独特的,但我们稍后会看到为什么这在实践中不是一个问题)关键每个不同的位置。 Algorithm applied to chess is explained here。
当你启动程序时,你创建了我们称之为zobrist键的东西。这些是每个片/方对的64位随机整数。在C中你会得到一个像这样的二维数组:
unsigned long long zobKeys[NUMBER_OF_PIECES][NUMBER_OF_SQUARES];
每个密钥都使用一个好的随机数生成器初始化(警告:随gcc或VC ++提供的随机数生成器不够好,使用Mersenne Twister的实现。)
当电路板为空时你随意将它的散列键设置为0,然后当你在电路板上添加一块时,在A1上说一个Rook,你也可以通过对A1上的一个小车的zobrist键进行异或来更新散列键。董事会的哈希键。像这样(在C中):
boardHash = boardHash ^ zobKeys[ROOK][A1];
如果你以后从这个方块中移除了车,你需要反转你刚刚做的事情,因为可以通过再次回应来反转XOR,你可以在删除它时再次使用相同的命令:
boardHash = boardHash ^ zobKeys[ROOK][A1];
如果你移动一块,说A1上的车就行至B1,你需要做两次XOR,一次取消A1上的车,另一辆在B2上添加车。
boardHash = boardHash ^ zobKeys[ROOK][A1] ^ boardHash ^ zobKeys[ROOK][B1];
这样每次修改电路板时都会修改散列。它非常有效。您还可以通过xoring与电路板上所有部件对应的zobKeys来计算每次scatch的哈希值。你还需要对可以采用的pawn的位置进行异或,以及双方的炼制能力的状态。您可以通过为每个可能的值创建zobris键来以相同的方式执行此操作。
这个algotitm并不保证每个位置都有一个独特的哈希,但是,如果你使用一个好的伪随机数发生器,发生碰撞的几率非常低,即使你让你的引擎在你的整个生命中发挥作用,几乎没有发生碰撞的可能性。
编辑:我只是红色,你正试图为一个有棋盘的棋子的变种实现这个。 Zobrist散列仍然是适合您的解决方案。您必须找到一种方法将这些信息合并到散列中。例如,您可以为板上部分提供一些键:
unsigned long long offTheBoardZobKeys[NUMBER_OF_PIECE][MAXIMUM_NUMBER_OF_ON_PIECE_TYPE];
如果你有2个棋子离开棋盘并将其中一个棋子放在a2上,你将需要进行2次操作:
// remove one pawn from the off-the-board
boardHash = boardHash ^ offTheBoardZobKeys[WHITE_PAWN][numberOfWhitePawsOffTheBoard];
// Put a pawn on a2
boardHash = boardHash ^ zobKeys[WHITE_PAWN][A2];
答案 1 :(得分:6)
为什么不在数据库中保留与棋盘布局相对应的64字节字符串?每种类型的作品,包括“无作品”代表一个字母(两种颜色的不同大写,即黑色的ABC,白色的abc)。董事会比较归结为简单的字符串比较。
一般来说,从棋盘角度来看,而不是片段视角,将摆脱你的换位问题!
答案 2 :(得分:4)
“不建议我应该以电路板为中心而不是以电脑为中心”。
你如此专注于不这样做,你错过了明显的解决方案。 比较特定于电路板。要比较两个职位列表L1
和L2
,请将L1
的所有元素放在(临时)主板上。然后,对于L2
的每个元素,检查它是否存在于临时板上。如果板上没有L2元素(因此在L1
中),则返回不等。
如果在移除L2
的所有元素后,电路板上仍有剩余部分,则L1
必须包含L2
中不存在的元素且列表相同。 <{1}}和L1
仅在临时董事会之后为空时才相等。
优化是首先检查L2
和L1
的长度。这不仅会很快发现许多差异,而且还消除了从baord中删除L2
的elemet和最后的“空板”检查的需要。只需要了解L2
是L1
的真实超集的情况。如果L2
和L1
具有相同的尺寸,并且L2
是L2
的子集,那么L1
和L1
必须相等。< / p>
答案 3 :(得分:3)
你主要反对以电路板方式存放国家是你有一袋无位置的作品。为什么不保持一块板+一块片?这符合您的要求,它的优势在于它是您所在州的规范代表。因此,您不需要排序,您可以在内部使用此表示形式,也可以在需要比较时转换为它:
Piece-type in A1
... 63 more squares
Number of white pawns off-board
Number of black pawns off-board
... other piece types
答案 4 :(得分:1)
从作品的角度来看,你可以这样做:
for each color:
for each piece type:
start new list for board A
for each piece of this piece type on board A
add piece position to the list
start new list for board B
for each piece of this piece type on board B
add piece position to the list
order both lists and compare them
优化可以有不同的方式。您的优势是:一旦发现差异,您就完成了!
例如,您可以通过总结所有部分的所有索引(对于两个板)进行快速和脏检查。总和应该相等。如果没有,那就有区别了。
如果总和相等,您可以快速比较独特作品(国王和王后)的位置。然后你可以写出(在某些复杂的if语句中)成对的片段的比较。然后你要做的就是使用上述方法比较棋子。
答案 5 :(得分:1)
还有第三个选项(我真的希望在一个问题上发布3个答案就可以了,stackoverflow-wise;)):
始终按索引顺序保留相同类型的片段,即列表中的第一个pawn应始终具有最低索引。如果发生打破此操作的移动,只需翻转列表中的棋子位置即可。使用不会看到差异,pawn是pawn。
现在比较位置时,您可以确定没有换位问题,您可以使用建议的for循环。
答案 6 :(得分:1)
鉴于您选择的游戏状态表示,您必须以某种方式对黑色棋子的指数,白色棋子的指数等进行排序。如果您在创建新游戏状态的过程中没有这样做,则必须在比较时进行。因为您只需要对最多8个元素进行排序,所以这可以非常快速地完成。
有几种方法可以代表您的游戏状态:
或
这两种选择不存在换位问题。
无论如何,当你第一次开始工作时,我的印象是你正在摆弄微优化。