我正在研究一个国际象棋引擎,我目前正在实施转置表(一组已经评估过的移动中的董事会职位).一般的想法是,当进行移动时,我检查换位表以找到匹配的位置.如果它存在,我跳过昂贵的评估过程,只需从转置表中提取先前计算的结果.如果它不存在,我会执行所需的计算,然后将其添加到转置表中以供将来参考.
对我来说,这听起来像是一个哈希集或字典的工作.我决定写字典.我希望键是代表我的板的类,并且值是一个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));
我这是一个愚蠢的疏忽.不过,我不是一个查询词典的粉丝.
最佳答案 一种选择是只保存对字典值的键的引用,以及字典的实际值,如下所示:
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));
另一个选项(可能是更好的设计)是将您的板状态对象分解为两种不同的类型.有一个真正只代表董事会的状态(这应该是不可变的),另一个保存有关该状态的其他信息,包括它已经发生的时间以及可能适用于您的特定应用程序的任何其他可变属性.这可以防止您需要访问字典键.