为什么这段代码为(看起来是)相同的输入提供了两个不同的输出?

我正在尝试为一个跳棋游戏编程一些人工智能。 我的节目是说白人玩家有0个动作,尽管我知道有。 GetValidMoves()函数已经过测试,可以在代码的其他区域中使用。

为了尝试隔离程序,我保存了有问题的板状态,然后将其加载回来,看看我是否会遇到同样的问题:

 using(Stream s = File.Open("board.dat", FileMode.Create)) { var bf = new BinaryFormatter(); bf.Serialize(s, board); } Debug.WriteLine(board.GetValidMoves(Color.White).Count()); using (Stream s = File.Open("board.dat", FileMode.Open)) { var bf = new BinaryFormatter(); board = (Board)bf.Deserialize(s); } Debug.WriteLine(board.GetValidMoves(Color.White).Count()); 

这打印:

 0 7 

当我期望输出相同时(7是正确的)。

什么可能导致这反序列化后开始工作? 董事会的两个实例看起来完全一样……我打印出所有属性,它们都是一样的。 我不知道从哪里开始?

电路板的第一个实例(反序列化之前)是克隆的结果。 我可以克隆它吗? 有“悬挂参考”吗?


GetValidMoves:

  public IEnumerable GetValidMoves(Color c) { var jumps = GetJumps(c); if (jumps.Any()) foreach (var j in jumps) yield return j; else foreach (var s in GetSlides(c)) yield return s; } public IEnumerable GetSlides(Color c) { foreach (int i in Enumerate(c)) foreach (var s in GetSlides(c, i)) yield return s; } public IEnumerable GetJumps(Color c) { foreach (int i in Enumerate(c)) foreach (var j in GetJumps(c, i)) yield return j; } public IEnumerable GetJumps(Color c, int i) { Checker checker = this[c, i] as Checker; bool indentedRow = i % Width < rowWidth; int column = i % rowWidth; int offset = indentedRow ? 0 : -1; bool againstLeft = column == 0; bool againstRight = column == rowWidth - 1; int moveSW = i + rowWidth + offset; int moveSE = moveSW + 1; int jumpSW = i + rowWidth * 2 - 1; int jumpSE = jumpSW + 2; if (!againstLeft && jumpSW < Count && IsEnemy(c, moveSW) && IsEmpty(c, jumpSW)) yield return new Move(c, i, jumpSW, jump: true, crown: IsCrowned(checker, jumpSW)); if (!againstRight && jumpSE = 0 && IsEnemy(c, moveNW) && IsEmpty(c, jumpNW)) yield return new Move(c, i, jumpNW, jump: true); if (!againstRight && jumpNE >= 0 && IsEnemy(c, moveNE) && IsEmpty(c, jumpNE)) yield return new Move(c, i, jumpNE, jump: true); } } public IEnumerable GetSlides(Color c, int i) { Checker checker = this[c, i] as Checker; bool indentedRow = i % Width < rowWidth; int column = i % rowWidth; int offset = indentedRow ? 0 : -1; bool againstLeft = !indentedRow && column == 0; bool againstRight = indentedRow && column == rowWidth - 1; int moveSW = i + rowWidth + offset; int moveSE = moveSW + 1; if (!againstLeft && moveSW < Count && IsEmpty(c, moveSW)) yield return new Move(c, i, moveSW, crown: IsCrowned(checker, moveSW)); if (!againstRight && moveSE = 0 && IsEmpty(c, moveNW)) yield return new Move(c, i, moveNW, crown: IsCrowned(checker, moveNW)); if (!againstRight && moveNE >= 0 && IsEmpty(c, moveNE)) yield return new Move(c, i, moveNE, crown: IsCrowned(checker, moveNE)); } } 

不应该有副作用。


要回答有关有效移动是否无意中更改电路板状态的问题:

  var board = new Board(8, 8); board.SetUp(); foreach(var m in board.GetValidMoves(Color.White)) Console.WriteLine(m); Console.WriteLine("---"); foreach(var m in board.GetValidMoves(Color.White)) Console.WriteLine(m); 

打印:

 8-12 8-13 9-13 9-14 10-14 10-15 11-15 --- 8-12 8-13 9-13 9-14 10-14 10-15 11-15 

(相同的输出两次)正如您所期望的那样。

只是一个疯狂的猜测,但你确定调用GetValidMoves绝对没有副作用?

由于在调用GetValidMoves之后对序列进行序列化和反序列化,看起来GetValidMoves以某种方式改变了电路板(考虑到函数的名称,这似乎有点奇怪)。 所以也许还有其他副作用你没有考虑到。

很确定这个bug实际上是在Board.Clone方法中。 我认为序列化/反序列化创建的全新对象,而我的克隆方法没有正确克隆所有内容,而是返回引用。

请参见如何克隆inheritance的对象? 详情。