2010-09-05 80 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