我正在研究国际象棋引擎,我目前正在实施转置表(一组已经评估过的移动中的董事会职位)。一般的想法是,当进行移动时,我检查换位表以找到匹配的位置。如果它存在,我跳过昂贵的评估过程,只需从转置表中提取先前计算的结果。如果它不存在,我会进行必要的计算,然后将其添加到转置表中以供将来参考。
对我而言,这听起来像是一个哈希集或字典的工作。我决定写字典。我希望键是代表我的板的类,并且值是一个int计数位置遇到的次数。计数的原因是能够检测到三倍的重复抽签。如果三次或更多次遇到同一位置,则玩家可以选择平局。使用字典而不是哈希集,我可以通过将它与我的转置表结合起来,大大简化三倍重复绘制的检测。
为了做到这一点,我已经使代表董事会状态的类重写了Equals()和GetHashCode()。它工作正常,我现在能够跟踪游戏以前遇到的所有历史状态。
我的问题是我需要能够访问字典中的密钥对象的属性。我可以看到是否存在匹配(通过使用.ContainsKey(...)方法和我的Equals()/ GetHashCode()逻辑),但现在我需要拉出前面提到的计算值(存储为我的板的公共属性)来自密钥的状态对象。
我能够提出:
BoardState board = TranspositionTable.Keys.FirstOrDefault(tt => tt.GetHashCode() == this.GetHashCode());
这很有效,但对我来说却很笨拙。转置表的重点是加速对数百万个连续板状态的递归搜索。查询每个匹配的字典只是听起来违反直觉。有更好的方法吗?
编辑:
正如已经指出的那样,由于大量有效的电路板状态,我的哈希值并不完美。 My Equals()覆盖检查第二个生成的哈希,以减少冲突的可能性。我的上述解决方案'是有缺陷的,因为它依赖于不完美的哈希。我应该使用:
BoardState board = TranspositionTable.Keys.FirstOrDefault(tt => tt.Equals(this));
我这是一个愚蠢的疏忽。尽管如此,我还不是一个查询词典的粉丝。
答案 0 :(得分:2)
一个选项是只保存对字典值中的键的引用,以及字典的实际值,如下所示:
Dictionary<BoardState, Tuple<BoardState, int>>
然后,无论何时添加对象,都要将相同的对象添加到与该键相同的值:
Dictionary<BoardState, Tuple<BoardState, int>> boards =
new Dictionary<BoardState,Tuple<BoardState,int>>();
BoardState board = new BoardState();
boards.Add(board, Tuple.Create(board, 1));
另一个选项(可能是更好的设计)是将您的板状态对象分解为两种不同的类型。有一个真正只代表董事会的状态(这应该是不可变的),另一个持有关于该状态的其他信息,包括它发生的时间和任何其他可能适用的可变属性根据您的具体应用。这可以防止您需要访问字典键。