2010-09-05 91 views
3

我试图编写一些AI的跳棋游戏。我的节目说白棋手有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<Move> 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<Move> GetSlides(Color c) 
    { 
     foreach (int i in Enumerate(c)) 
      foreach (var s in GetSlides(c, i)) 
       yield return s; 
    } 

    public IEnumerable<Move> GetJumps(Color c) 
    { 
     foreach (int i in Enumerate(c)) 
      foreach (var j in GetJumps(c, i)) 
       yield return j; 
    } 

    public IEnumerable<Move> 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 < Count && IsEnemy(c, moveSE) && IsEmpty(c, jumpSE)) 
      yield return new Move(c, i, jumpSE, jump: true, crown: IsCrowned(checker, jumpSE)); 

     if (checker.Class == Class.King) 
     { 
      int moveNW = i - rowWidth + offset; 
      int moveNE = moveNW + 1; 
      int jumpNW = i - rowWidth * 2 - 1; 
      int jumpNE = jumpNW + 2; 

      if (!againstLeft && jumpNW >= 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<Move> 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 < Count && IsEmpty(c, moveSE)) 
      yield return new Move(c, i, moveSE, crown: IsCrowned(checker, moveSE)); 

     if (checker.Class == Class.King) 
     { 
      int moveNW = i - rowWidth + offset; 
      int moveNE = moveNW + 1; 

      if (!againstLeft && moveNW >= 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 

(同样的输出两次)正如你所期望。

+1

您可以显示Board类以及'GetValidMoves'函数,以便我们可以重现行为吗? – 2010-09-05 21:46:54

+0

'board.GetValidMoves'有副作用吗?也就是说,如果你叫它,它会改变'board'的状态吗? – Oded 2010-09-05 21:47:22

+1

如果连续调用'GetValidMoves',将序列化/反序列化放在一边,会发生什么?正如其他人所说,似乎这个函数修改了对象的状态。 – 2010-09-05 21:51:39

回答

-1

很确定这个错误实际上是在Board.Clone方法中。我认为序列化/反序列化创建的全新对象,而我的克隆方法不是正确克隆所有东西,而是返回引用。详情请参阅How to clone an inherited object?

+0

我在这里下了你的意见,因为对于显示的代码来说,对象标识是一个问题是没有意义的,尤其是考虑到序列化的“正确”反序列化。您没有向我们展示的代码可能会导致您看到的问题是“枚举”。 – 2016-09-26 02:14:40

+0

@MarkHurd你不认为这是一个深刻的克隆问题?你可能是对的,但我不能肯定地说;这是6年前的事了。 – mpen 2016-09-26 17:20:30

+0

你在这个问题中向我们展示的代码实际上并不关心对象标识,它只是比较值。 – 2016-09-27 01:34:23

1

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

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

+0

我没有看到任何修改棋盘状态的东西......它只产生一堆“移动”对象。 – mpen 2010-09-05 22:00:44

+0

在这种情况下,我没有理由序列化/反序列化电路板。尝试两次调用GetValidMoves,一个接一个地调用GetValidMoves并查看给出的输出。如果它是相同的输出,那么问题在于你的序列化/反序列化代码。否则你会有一些副作用。 – 2010-09-05 22:06:10

相关问题